摘要:使用EF「程式碼優先」函數庫操作現有資料庫
[原文發表位置]:Using EF 「Code First」 with an Existing Database
[原文發表時間]:2010/8/4 11:09 AM
上個月我部落格裡提到了Entity Framework 4新的「程式碼優先」開發模式。EF「程式碼優先」類別庫啟用超棒的寫碼至上的開發工作流來存取資料。它讓你可以:
· 不需要打開一個設計工具或定義XML映射檔案就可以進行開發。
· 定義模型物件時只需寫一個簡單的類,而不需要指定基礎類別。
· 使用「慣例高於配置」的方式實現資料庫持久化,不需要顯式配置任何資訊。
在我的第一篇文章裡我介紹了EF「程式碼優先」類別庫,並示範了使用EF4預設的映射慣例來建立一個新的資料庫。這些預設慣例在新的應用程式裡工作的很好,而且還避免你顯式配置任何東西從/往資料庫中映射型別。後來我又寫了第二篇自訂資料庫結構映射的文章,討論了覆蓋預設的持久化映射規則,和自訂資料庫結構的方法。
今天的文章我將回答最近很多人問我的問題:「如何對現有的資料庫使用EF程式碼優先類別庫?」
使用EF「程式碼優先」類別庫操作現有資料庫
實際上,EF程式碼優先類別庫和現有的資料庫工作的非常好,而且還是一個很好的寫碼至上的開發模式。特別是,你可以用「簡單CLR物件」(即POCO)定義好模型對象,用預設的映射慣例,或用自訂的映射規則,從/往資料庫裡映射它們。
下面逐步示範了使用EF「程式碼優先」類別庫操作現有資料庫的方法。
第一步:建立一個新的ASP.NET Web應用程式專案
讓我們從建立一個新的ASP.NET Web應用程式專案開始。我前兩篇EF「程式碼優先」文章用的是ASP.NET MVC—這篇文章我將用ASP.NET Web表單(來實現)。請注意,不論你使用哪種型別的ASP.NET應用程式,背後的EF理念都是相同的。
我們使用VS 2010(或者免費的Visual Web Developer 2010體驗版)的「檔案->新建專案」選單,並選擇「ASP.NET Web應用程式」專案模板來建立新的程式。
VS 2010裡新的「ASP.NET Web應用程式」專案是一個很好的初學者模板,它的預設母板佈局頁面 (Master Page) 設計使用的是CSS(我在以前的文章裡提到了新的初學者專案模版)。當建立完畢以後,你可以看到它裡面包含下面這些檔案:
我們並不需要這些檔案(實際上也可以使用「ASP.NET空Web應用程式」模版)—但如果用它們的話,可以讓我們的程式看起來更漂亮一些。
第二步:添加對EF程式碼優先庫的引用
下一步將為我們的專案添加對EF程式碼優先類別庫的引用。在方案總管 (Solution Explorer) 上右擊「引用」節點,並選擇「添加引用」。
你需要引用「Microsoft.Data.Entity.Ctp.dll」檔案,你下載並安裝EF程式碼優先類別庫後,它安裝在「\Program Files\Microsoft ADO.NET Entity Framework Feature CTP4\Binaries\」資料夾裡。添加完引用後,專案引用視窗類似下圖:
第三步:Northwind資料庫
如果你的SQL Server資料庫已經安裝了Northwind(或者其它的資料庫),你可以跳過這一步。
如果你尚未安裝,可以在這裡下載它。你既可以使用裡面的.SQL檔案來將它安裝到SQL資料庫裡,也可以拷貝Northwind.mdf這個SQL Express檔案到你應用程式的\App_Data資料夾裡:
第四步:建立我們的模型層
現在我們來寫模型類別,並使用EF「程式碼優先」類別庫將它們映射到我們的Northwind資料庫。下面就是所有要寫的程式碼—就這些:
下面是這些程式碼的詳細工作原理:
POCO模型類別
EF「程式碼優先」類別庫允許我們使用「簡單CLR物件」(即POCO)來表現資料庫裡的實體。也就是說我們的模型類別不需要繼承自一個基礎類別,也不需要實現任何的介面或特性(attribute)。這樣可以使我們的模型型別保持整潔並且「持久化無關」。
前面我們定義了兩個POCO類別—「Product」 (產品)和「Category」 (種類)—用來表示Northwind資料庫裡的「Product」 (產品)和「Category」 (種類)表格。兩個類的屬性分別對應表的各個資料列。「Product」 (產品)或「Category」 (種類)的實例分別對應表裡的一行。
可空列 (Nullable Row)
請注意「Product」 (產品)類裡有些屬性被定義成可空屬性(即Decimal?—說明它是一個可空型別)。資料表的可空列(Nullable column)的型別如果是實值型別的話,在模型類別裡應該用可空屬性表示:
如果在模型類別中,你不會存取可空列,你可以選擇省略它。比如說,Northwind的「Product」 (產品)表裡的「QuantityPerUnit」是一個可空的nvarchar型別,而「UnitsOnOrder」列是一個可空的smallint型。我在定義上面的
「Product」 (產品)類時省略掉了這些屬性。因為它們在資料庫裡是可空的,所以我在執行讀取、插入、更新和刪除操作時不會有任何問題。
關聯屬性和延遲載入
EF「程式碼優先」類別庫使應用資料庫裡的主鍵/外鍵關係時變得更容易,它們通過模型類別的屬性表示,我們可以通過巡覽模型類別別來使用這些關係。
前面我們在「Product」 (產品)類裡定義了一個「Category」 (種類)屬性,在「Category」 (種類)類裡定義了一個「Product」 (產品)屬性。通過存取這些屬性,可以讓我們使用兩個表之間的主鍵/外鍵關係,並獲取相關模型實例的引用。注意,這些屬性依然是「POCO」對象,不需要使用任何EF定義的集合型別來定義它們。
標記為「virtual」的關聯屬性預設採用的是延遲載入技術。也就是說,如果你抓取的是「Product」 (產品)實體,除非顯式存取,則不會從資料庫中載入它的「Category」 (種類)方面的資訊(除非當你在使用LINQ查詢抓取Product物件時,明確指定獲取Category的資料) 。
EF 場景(Context)類別
一旦建立好了「Product」 (產品)和「Category」 (種類)POCO類別,我們用EF「程式碼優先」類別庫來建立一個 "場景(context)" 類別來從/往資料庫映射我們的POCO模型類別:
上面的「Northwind」類別是一個場景(Context)類,用來從/往資料庫映射我們的POCO模型類別。它從EF「程式碼優先」類別庫裡的DbContext這個基礎類別繼承下來,通過兩個屬性來陳列資料庫中相應的表。在這個範例中,我們用預設的「慣例高於配置」映射規則來從/往資料庫映射型別。
如果我們要讓模型類別的對象模型跟資料庫的結構有些差異,我們也可以覆寫「OnModelCreating」函數來指定自訂的映射規則。我前面的EF「程式碼優先」類別庫文章講解了相關方法。
第五步:配置資料庫連接字串
我們已經寫好了定義模型層的所有程式碼,在使用它之前最後一步是設置連接到資料庫的連接字串。
在我的第一篇EF「程式碼優先」類別庫文章裡,我介紹了EF「程式碼優先」類別庫提供的一個很酷的選項,就是它可以為你自動建立/重新建立資料庫的結構。這個選項對新建應用系統開發的情況特別有用—它允許你在專案早期只關注與模型層,而不需要在模型改動時,花時間更新你的資料庫結構。
更重要的是,這個資料庫自動建立功能只是一個選項—它不是必須的。如果你的資料庫連接字串指向的是一個已有的資料庫,那EF「程式碼優先」類別庫不會自動建立一個新的。而且這個自動建立選項只在你顯式指明的情況下EF才會這樣做—因此你不必擔心它會刪除並重建你的資料庫,除非你顯式地告訴它應該這麼做。
在這篇文章裡,我們不會自動建立資料庫。而是通過在web.config檔案裡添加一個「Northwind」連接字串指向我們已有的Northwind資料庫:
<connectionStrings>
<add name="Northwind"
connectionString="data source=.\SQLEXPRESS;Integrated Security=SSPI;AttachDBFilename=|DataDirectory|\northwind.mdf;User Instance=true"
providerName="System.Data.SqlClient" />
</connectionStrings>
EF「程式碼優先」類別庫在搜尋連接字串時,按照慣例,根據場景(Context)類名來尋找。因為我們的場景(Context)類名字叫「Northwind」,所以預設尋找名為「Northwind」連接字串。上面我們配置Northwind連接字串使用本地的SQL Express資料庫,你也可以指向一個遠程的SQL伺服器。
第六步:使用我們的模型類別
現在讓我們使用Northwind模型類別寫一個非常簡單的頁面,從資料庫中顯示一些資料。
我們從向ASP.NET專案,右擊web專案,然後選擇「添加->新項」,然後選擇「使用母板的Web表單(Web Form using Master Page)」檔案模板。我們把這個頁面命名為「Product.aspx」並且採用ASP.NET Web專案預設的初學者模板自帶的「Site.master」母板頁。
我們將在Product.aspx頁面添加一個<asp:GridView>控制項。並配置它只顯示Products(產品)的名稱和價格:
而在後台檔案裡,通過編寫下列的LINQ查詢從我們的模型類別來獲取資料庫中所有有效的產品(Product),並將它們繫結到上面的GridView控制項上:
現在當我們運行專案並瀏覽Products.aspx頁面時,我們可以得到類似下圖的產品列表:
現在我們已經有了一個簡單的應用程式,它基於現有資料庫叫用EF「程式碼優先」類別庫。
下載範例程式碼
你可以從這裡下載完整的範例程式碼,它假設你已經安裝了EF「程式碼優先」類別庫 CTP 4版本和SQL Express。
其它程式碼範例
下面的程式碼Demo了使用Northwind模型的其它常見用法。
跨關係查詢
下面的LINQ查詢Demo了基於產品種類(Category)的名稱來獲取產品(Product)物件序列。注意下圖我們的LINQ查詢跨越產品(Product)物件和與其相關的種類(Category)對象的屬性的編寫方式。實際的過濾過程是在資料庫引擎裡面做的—中間層只需要傳回產品(Product)物件集合(也就更有效率):
使用Find方法獲取單個產品(Product)物件
除了允許你編寫上面的LINQ查詢以外,EF「程式碼優先」類別庫還支持對DbSet<T>集合叫用「Find()」方法,這樣你就可以用類似下面的程式碼根據ID獲取單個實例了:
插入一個新的種類(Category)
下面的程式碼Demo了往資料庫中添加一個新的種類(Category)的方法:
留意我們建立種類(Category)對象,給它的屬性指派,然後將它加到語境(Context)的Categories集合的方式。接著我們叫用語境(Context)對象的SaveChanges()把改動保存到資料庫中。
添加一個新的種類(Category)和產品(Product)(並關聯它們)
下面的程式碼Demo了建立一個新的種類(Category)和一個新的產品(Product),並關聯產品(Product)屬於新建的種類(Category),最後將它們都保存進資料庫:
留意上面我們可以通過將新建種類(Category)的實例指派給新建產品(Product)的「Category」 (種類)屬性, 將新建產品(Product)引用到新建種類(Category)實例。我們不需要顯式地設置CategoryID外鍵屬性—這個會在保存進資料庫時自動完成。
EF使用一個叫做「工作單元」的模式—它會跟蹤語境(Context)裡的多重改動,當「SaveChanges()」被叫用時,它會將這些改動以原子事務的方式一併保存(要麼全部成功保存,要麼全部失敗)。這是保證你的資料庫不會被污染(即有些保存了,有些卻沒有)的簡便方法。
在上面的程式碼中,種類(Category)和產品(Product)要麼全保存了,要麼全部沒有(會扔出來有一個異常)。
更新一個產品(Product)並保存
下面的程式碼Demo了獲取和更新一個產品(Product),並保存回資料庫的方式。前面我範例了使用Find()函數根據ProductID獲取單個產品(Product)的方法。下面我們寫一個根據ProductName獲取特定產品(Product)的LINQ查詢:
我們可以做多次更改(任何對象,新建的對象也是)。當我們叫用SaveChanges()時,會以一個事務的形式將它們保存到資料庫中。
預設慣例 vs 自訂映射規則
之前我們建立產品(Product)和種類(Category)類時,我們使用的是EF「程式碼優先」類別庫預設的慣例從/往資料庫中映射型別。這讓我們避免了自己指定映射規則,保持乾淨的程式碼。
當然肯定也有你不喜歡資料庫結構的時候,而希望有自己不同形狀的模型類別。參見我的自訂資料庫結構映射文章來瞭解使用EF指定自訂映射規則的方法。這些方法也同樣適用於已有的資料庫。
總結
我的確很喜歡EF「程式碼優先」類別庫的功能,並認為它提供了極佳的寫碼至上的資料處理方式。它很好很強大。我喜歡它是因為它讓程式碼保持乾淨,可維護並且很簡潔。希望這三篇文章能夠讓你一瞥它的功能 — 針對新的或已有的資料庫(的功能)。
你可以從這裡下載EF「程式碼優先」類別庫 CTP 4版本。未盡事宜請參看ADO.NET團隊的文章:
· 資料註釋(DataAnnotation)和程式碼優先類別庫
希望這能對您有所幫助。
附:[除了寫部落格以外,我現在也使用推特(Twitter)來及時更新狀態和分享連結,您可以到這個位址「推」我一下:twitter.com/scottgu]