概述
在開發面向數據的軟件時我們常常為了解決業務問題實體、關系和邏輯構建模型而費盡心機,ORM的產生為我們提供了一種優雅的解決方案。ADO.NET Entity Framework是.NET開發中一種由ADO.NET驅動的ORM框架,使用Entity Framework開發人員可以不必考慮數據的基礎數據表和列,在處理數據時能夠以更高的抽象級別進行工作,並能夠以相對傳統開發編寫更少的代碼來創建和維護應用程序。
我們知道面向對象的編程與數據存儲系統的交換提出了一個難題:類結構通常同關系數據表組織結構相近但又不同。例如數據中可能使用一個外鍵表示一個實體與另一個實體的關系,但是對於類而言我們通知會在類中定義一個屬性來描述這種關系。對於這個問題當前ORM框架一般選擇通過將面向對象的類和屬性映射到關系表和列來彌補這種不足。但是Entity Framework並沒有采取這種方式而是將邏輯模型中的表、列和外鍵約束映射到概念模型中的實體和關系,實體數據模型工具再基於概念模型生成可擴展的數據類。這些類派生自基類,而基類提供服務以將實體具體化為對象並進行跟蹤和保存。這樣一來開發人員不僅可以很方便的對數據類進行擴展而且可以像處理關聯對象一樣處理實體和關系。
三種編程方式
Entity Framework4.1之前EF支持“Database First”和“Model First”編程方式,從EF4.1開始EF開始支持支持“Code First”編程方式,今天簡單看一下EF三種編程方式。
開始介紹這三種EF操作方式之前,首先在Visual Studio 2013中建立一個數據庫連接,這里我們以“AdventureWorks”數據庫為例:
Database First
“Database First”模式我們稱之為“數據庫優先”,前提是你的應用已經有相應的數據庫,你可以使用EF設計工具根據數據庫生成數據數據類,你可以使用Visual Studio模型設計器修改這些模型之間對應關系。
首先創建一個控制台應用程序,然后右鍵添加新建項,選擇“ADO.NET Entity Data Model”,名稱輸入AdventureWorksContext:
接着選擇從數據庫生成:
下一步,選擇數據庫連接“SQL2008.AdventureWorks.dbo”:
點擊下一步,然后選擇“Person”和“PersonPhone”表:
創建完模型之后,你會發現Visual Studio自動為你生成了“Perrson”、“PersonPhone”兩個實體類和一個“AdventureWorksContext”數據庫上下文操作類:
下面簡單的看一下如何使用EF進行數據查詢,通過下面的代碼我們可以看到EF對於數據的操作入多么優雅:
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace DatabaseFirst { class Program { static void Main(string[] args) { using (var db = new AdventureWorksEntities()) { IQueryable<Person> persons = from people in db.People where people.LastName == "Sánchez" select people; foreach (Person p in persons) { Console.WriteLine("first name is :{0}", p.FirstName); if (p.FirstName == "Angela") { p.AdditionalContactInfo = "other info"; } } var persons2 = from people in db.People where people.LastName == "Sánchez" && people.FirstName == "Angela" select people; if (persons2.Count() > 0) { Console.WriteLine("additional contact information is :{0}", persons2.First().AdditionalContactInfo); } //上面雖然可以查出來AddtionalContactInfo,但是實際省並未保存到數據庫,具體保存方法在此不再詳細描述 } } } }
執行結果如下圖:
![]() |
注意:如果你的數據庫表結構發生改變后,只需在模型設計視圖空白處右鍵,選擇“從數據庫更新模型”接着按照向導操作即可。 |
Model First
Model First我們稱之為“模型優先”,這里的模型指的是“ADO.NET Entity Framework Data Model”,此時你的應用並沒有設計相關數據庫,在Visual Studio中我們通過設計對於的數據模型來生成數據庫和數據類。
首先創建一個控制台應用程序,右鍵添加新建項,選擇“ADO.NET Entity Data Model”,名稱輸入AdventureWorksContext:
接着選擇空模型:
在模型設計視圖中,添加新實體:
輸入實體對應的屬性:
添加,兩個Scalar屬性:“Customer”和“OrderDate”;同樣的方式添加第二個實體“OrderDetail”,並添加“Product”和“UnitPrice”屬性:
接下來我們添加二者之間的關系,“Order”和“OrderDetail”是一對多的關系,“Order”可以通過“OrderDetails”屬性訪問“OrderDetail”實體,“OrderDetail”可以通過“Order”屬性訪問“Order”實體,並且添加了一個外鍵約束到“OrderDetail”中:
添加過關系后:
到目前為止Model First中的Model已經創建結束,下面就需要生成到數據庫了,在模型設計視圖空白處選擇“從模型生成到數據庫…”:
選擇數據庫連接,點擊下一步,你將會看到生成的sql語句:
點擊完成,不出意外的話將打開生成的腳本,當然你也可能會出現如下錯誤,請下載最新的SQL Server Data Tool(我本地VS2012,數據庫SQLServer2008R2出現了下面的提示,下載更新即可,建議直接下載鏡像文件):
此時生成了數據庫上下文和實體類,並且打開了建表的腳本:
打開的數據庫腳本:
然后右鍵選擇執行即可。
然后編碼查詢一下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace ModelFirst { class Program { static void Main(string[] args) { using(var dbContext=new AdventureWorksContextContainer()) { var o = new Order(); o.OrderDate = DateTime.Now; ctx.Orders.Add(o); ctx.SaveChanges(); var orders = from od in dbContext.Orders select od; foreach (Order order2 in orders) { Console.WriteLine("OrderID:{0},OrderDate:{1}",order2.Id,order2.OrderDate); } Console.Read(); } } } }
查詢結果:
![]() |
注意:如果我們的模型發生改變,只需要在模型設計視圖修改模型,讓后保存此時實體類就會相應改變,然后選擇“從模型生成到數據庫”重新執行生成的腳本即可。 |
Code First
Code First模式我們稱之為“代碼優先”模式,是從EF4.1開始新建加入的功能。使用Code First模式進行EF開發時開發人員只需要編寫對應的數據類(其實就是領域模型的實現過程),然后自動生成數據庫。這樣設計的好處在於我們可以針對概念模型進行所有數據操作而不必關系數據的存儲關系,使我們可以更加自然的采用面向對象的方式進行面向數據的應用程序開發。
從某種角度來看,其實“Code First”和“Model First”區別並不是太明顯,只是它不借助於實體數據模型設計器,而是直接通過編碼(數據類)方式設計實體模型(這也是為什么最開始“Code First”被叫做“Code Only”的原因)。但是對於EF它的處理過程有所差別,例如我們使用Code First就不再需要EDM文件,所有的映射通過“數據注釋”和“fluent API”進行映射和配置。另外需要注意的是“Code First”並不代表一定就必須通過數據類來定義模型,事實上也可以通過現有數據庫生成數據類。
那么我們首先看一下傳統的Code First如何使用。
首先創建一個控制台應用程序,接下來添加兩個類“Order”和“OrderDetail”,我們可以看到這兩個類只是簡單的C#對象(POCO,Plain Old C# Object)這兩個類基本和EF沒有任何關系,需要注意的是這兩個類有兩個導航屬性“Order.OrderDetails”和“OrderDetail.Order”:
using System; using System.Collections.Generic; namespace CodeFirst { public class Order { public int Id { get; set; } public string Customer { get; set; } public System.DateTime OrderDate { get; set; } public virtual List<OrderDetail> OrderDetails { get; set; } } }
using System; using System.Collections.Generic; namespace CodeFirst { public partial class OrderDetail { public int Id { get; set; } public string Product { get; set; } public string UnitPrice { get; set; } public int OrderId { get; set; } public virtual Order Order { get; set; } } }
有了這兩個類之后讓我們定義一個數據庫上下文,有了它我們就可以對數據進行增刪改查操作了,這個類必須繼承於"System.Data.Entity.DbContext”類以賦予它數據操作能力。因此接下來我們需要給這個應用安裝EntityFramework包,因為到目前為止我們並沒有引入EF框架相關的任何內容,我們需要引入EF相關程序集。但是我們有更好的選擇那就是NuGet。通過NuGet進行在線安裝:項目中右鍵選擇"Manage NuGet Packages…”;選擇Online;再選擇“EntityFramework”;然后點擊安裝即可。不了解NuGet的朋友到這里看一下使用 NuGet 管理項目庫。數據庫上下文操作類:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Data.Entity; namespace CodeFirst { public class OrderContext:DbContext { public DbSet<Order> Orders { get; set; } public DbSet<OrderDetail> OrderDetails { get; set; } } }
然后我們進行測試,在這個類中我們首先創建了一個Order實例,接着編碼查詢:
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace CodeFirst { class Program { static void Main(string[] args) { using (var ctx = new OrderContext()) { var o = new Order(); o.OrderDate = DateTime.Now; ctx.Orders.Add(o); ctx.SaveChanges(); var query = from order in ctx.Orders select order; foreach (var q in query) { Console.WriteLine("OrderId:{0},OrderDate:{1}", q.Id, q.OrderDate); } Console.Read(); } } } }
查詢結果如圖:
如果是第一次使用EF Code First的朋友一定會有疑問,我們沒有進行任何數據庫配置,增加了一條數據通過查詢確實保存上了,那么我們的數據到底在哪呢?事實上如果用戶不進行數據庫配置EF默認會使用“.\SQLEXPRESS”數據庫實例,如果你沒有安裝“.\SQLEXPRESS”則默認使用LocalDb,關於LocalDb的具體細節請看:SQL Server 2012 Express LocalDB,例如我本機就存放在:“C:\Users\Kenshin”,可以看到創建了一個名為“CodeFirst.OrderContext”的數據庫:
但是多數情況下我們是希望自己控制這個數據庫的,例如我想讓他保存在我機器上的”.\SQL2008”實例上,此時我們就需要在配置文件App.Config中配置一個數據庫連接串,然后在我們的數據庫上下文中指定這個連接名稱。
配置文件:
<?xml version="1.0" encoding="utf-8"?> <configuration> <configSections> <!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 --> <section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=4.4.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" /> </configSections> <connectionStrings> <add name="CodeFirstDb" connectionString="Data Source=.\SQL2008;Database=CodeFirstDb;UID=sa;PWD=123;" providerName="System.Data.SqlClient"></add> </connectionStrings> <entityFramework> <defaultConnectionFactory type="System.Data.Entity.Infrastructure.LocalDbConnectionFactory, EntityFramework"> <parameters> <parameter value="v11.0" /> </parameters> </defaultConnectionFactory> </entityFramework> </configuration>
OrderContext類,構造函數多了一個連接名參數:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Data.Entity; namespace CodeFirst { public class OrderContext:DbContext { public OrderContext(string connectionName) : base(connectionName) { } public DbSet<Order> Orders { get; set; } public DbSet<OrderDetail> OrderDetails { get; set; } } }
使用的時候,傳入配置的數據庫連接字符串名稱:
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace CodeFirst { class Program { static void Main(string[] args) { using (var ctx = new OrderContext("CodeFirstDb")) { var o = new Order(); o.OrderDate = DateTime.Now; ctx.Orders.Add(o); ctx.SaveChanges(); var query = from order in ctx.Orders select order; foreach (var q in query) { Console.WriteLine("OrderId:{0},OrderDate:{1}", q.Id, q.OrderDate); } Console.Read(); } } } }
執行之后就會發現在”.\SQL2008”實例上多了一個“CodeFirstDb”數據庫(注意圖中除了我們創建的兩個實體表還有一個系統表dbo._MigrationHistory它記錄了模型的定義,在以后的文章中我會着重解釋此表):
到了這里我們三種EF編程方式已經全部介紹完了,關於Code First模式如何更新模型、如何先建數據庫再生成數據類以及更多EF問題在接下來的文章中再探討。