在Entity Framework Model First下, 一個非常常見的需求是改變數據庫腳本的生成方式。這個應用場景是指,當用戶在Designer上單擊鼠標右鍵,然后選擇Generate Database from Model選項,此時Entity Framework Model First會根據模型產生數據庫SQL腳本,並將SQL腳本文件添加到解決方案資源管理器中。
事實上,這個自動化產生的數據庫SQL腳本還是會有一些局限性。比如:Model上支持DateTime這一CLR類型,在自動化SQL生成的過程中,Entity Framework會自動使用SQL中的數據類型datetime來產生相應的字段定義,如果我們希望對於某些DateTime類型的屬性產生date類型,而不是datetime類型的字段,那么我們就需要對數據庫腳本的生成方式做一些修改。
例子
首先看一個例子,我們建立一個非常簡單的模型:Employee,在這個Employee實體中會有一個DayOfBirth的屬性,用以保存雇員的生日日期。該模型定義如下:
在模型設計器上單擊鼠標右鍵,選擇“Generate Database from Model”菜單后,產生的SQL語句如下,可以看到,對於DayOfBirth屬性,產生的字段是datetime類型:
現在,讓我們來嘗試改變Entity Framework Model First下數據庫SQL腳本的生成方式,以使得所產生的DayOfBirth字段為date類型。
實現
通過使用Entity Framework的Structural Annotation的特性,我們可以很方便地定制SQL腳本的生成方式。
首先,在Solution Explorer中,找到模型文件(擴展名為edmx的文件),單擊鼠標右鍵,選擇Open With選項。在Open With對話框中,選擇Automatic Editor Selector:
此時會關閉模型設計器,並以XML編輯器的方式打開edmx文件。在打開的編輯器中,我們可以看到edmx文件的詳細內容。如果模型比較大的話,這個文件的內容也會比較多(有的甚至幾千幾萬行)。總體來看,主要有三個部分:
- SSDL content:對存儲模型的定義
- CSDL content:對概念模型的定義 - 也就是保存設計器上所設計的模型
- C-S mapping content:定義了概念模型與存儲模型之間的映射
一看就知道,Entity Framework就是一個ORM框架(廢話)。
接下來,我們要對edmx的概念模型部分做一些修改。修改的目的就是為了給SQL腳本的自動化生成提供一些客戶化的信息,以便自動化生成工具能夠根據這些客戶化信息產生不同的結果。
我們需要在ConceptualModels節點下的Schema上定義自己的命名空間。例如:
然后,我們自己自定義一個XML標簽,並把這個標簽應用到概念模型中的DayOfBirth屬性上,如下:
注意此處的“edmx:CopyToSSDL”屬性,意思是這部分屬性需要在產生模型的時候復制到SSDL存儲模型中。因為在生成SQL腳本時,轉換引擎會讀取SSDL中的內容並根據這些內容產生SQL。現在,我們雙擊edmx文件,並重新在設計器中打開模型。同樣在設計器中點擊鼠標右鍵,選擇“Generate Database from Model”選項,待SQL腳本重新生成之后,再用XML編輯器打開edmx文件,此時我們會看到,在SSDL部分,先前添加的“custom:SqlType”節點也被復制到了這里,只不過稍許有些變化:
現在,我們需要定制SQL腳本的產生過程。打開模型設計器,在模型設計器的屬性編輯窗口中,我們可以看到一個名為“DDL Generation Template”的屬性:
它就是主導SQL腳本生成的T4模板文件,現在需要對這個T4文件進行定制。該文件位於%PROGRAMFILES(x86)%\Microsoft Visual Studio 12.0\Common7\IDE\Extensions\Microsoft\Entity Framework Tools\DBGen目錄下。為了不更改原有的SSDLToSQL10.tt文件,我們將其復制到Solution Explorer中,注意將該文件的BuildAction設置為None,並去掉Custom Tool的設置:
仍然打開模型設計器,在屬性窗口中,設置“DDL Generation Template”屬性為“.\SSDLToSQL10.tt”,注意路徑符“.\”,它表示需要使用Solution Explorer下的SSDLToSQL10.tt文件,而不是標准的那個文件。
最關鍵的一步,就是修改SSDLToSQL10.tt文件。打開這個文件,找到“Creating all tables”部分,並用以下粉紅色高亮部分替換其中的內容:
注意:我們還需要在這個tt文件的頂部引入System.XML和System.XML.Linq的命名空間:
<#@ assembly name="System.Xml" #> <#@ assembly name="System.Xml.Linq" #> <#@ import namespace="System.Xml" #> <#@ import namespace="System.Xml.Linq" #>
至此,實現部分已經完成。
測試
現在來測試一下效果。雙擊打開edmx模型,在模型設計器上單擊鼠標右鍵,選擇“Generate Database from Model”,然后查看生成的SQL語句。我們發現,DayOfBirth已經變成了date類型了:
如果在產生數據庫腳本的時候提示以下錯誤,請稍許更改一下模型(比如拖動一下模型中的實體等)保存之后再試。