翻譯的初衷以及為什么選擇《Entity Framework 6 Recipes》來學習,請看本系列開篇
第八章 POCO
對象不應該知道如何保存它們,加載它們或者過濾它們。這是軟件開發中熟悉的口頭禪,特別是在領域驅動設計中。這是一個聰明的做法,如果對象和持久化綁得太緊,以至於不能對領域對象進行單元測試、重構和復用。在ObjectContext上下對象中,實體框架為模型實體生成的類,高度依賴實體框架管道(Plumbing)。對於一此開發人員來說,這些類對持久化機制知道得太多了。而且,它們與特定的模型和映射關聯太密切。幸好,我們還有另一個選項。
實體框架還支持使用你自己創建的類來作為模型中的實體。術語叫做“普通公共運行時對象”(Plain Old CLR Object),通常被簡單地叫做POCO,這並不意味着你的類普通而老掉牙。它僅僅是說,它不包含特定框架的引用,不需要來至第三方的代碼,不實現任何第三方的專用接口,並且,它不需要別的任何程序集或者命名空間。你可以實現自己的領域對象,你會看到通過自定義的ObjectContext上下對象使它們適合模型。也就是說,憑借實體框架強大的能力,你可以選擇任何架構模式。你同樣也能使用DbContext為你產生POCO類。
本章涵蓋了多個關於POCO的小節。第一節展示POCO最基本的用法,剩下的小節集中在,實體的加載和實體框架使用對象狀態保持同步。
本章故意手工編寫了大量的POCO類,是為了演示如何運用POCO。如果使用來至微軟ADO.NET開發團隊的T4模板,這些工作都將不復存在。
8-1 使用POCO
問題
你想在你的項目中使用POCO。
解決方案
假設你有如圖8-1所示的數據模型。
圖8-1. 一個關於客戶和它的訂單的數據庫模型
為了使用POCO類創建基於圖8-1所示的數據庫模型的實體框架模型,請按下面的步驟進行操作:
1、右鍵你的項目,選擇Add(增加) ➤New Item(新建項);
2、選擇Visual C#條目下的Data(數據)模板下的ADO.NET Entity Data Model(ADO.NET實體數據模型);
3、選擇Generate from database 從一個已存在的數據庫創建模型;
4、選擇表order,OrderDetail,Customer和Product,單擊下一步。在生成的模型中,實體Product有一個導航屬性OrderDetails,它是關聯產品的訂單明細。在這里它不是必要的,因此將其刪除(譯注:實際上,沒有刪除)。完成后的模型如圖8-2所示。
圖8-2. 客戶訂單的模型
5、我們使用生成的類作為的我們實體,默認情況下,實體框架6生成POCO實體類。 因此,所有的數據庫訪問代碼都在一個單獨的類中,實體被生成為普通的類。還可以在實體框架生成實體類之前關閉模型的代碼生成功能,然后手工創建相同的實體類。 在這個版本中,代碼生成策略已經被設置成None.代碼清單8-1展示了我們模型中的類。
代碼清單8-1. 我們模型的POCO類
1 public partial class Customer 2 { 3 public Customer() 4 { 5 this.Orders = new HashSet<Order>(); 6 } 7 8 public int CustomerId { get; set; } 9 public string ContactName { get; set; } 10 11 public virtual ICollection<Order> Orders { get; set; } 12 } 13 14 public partial class Order 15 { 16 public Order() 17 { 18 this.OrderDetails = new HashSet<OrderDetail>(); 19 } 20 21 public int OrderId { get; set; } 22 public int CustomerId { get; set; } 23 public System.DateTime OrderDate { get; set; } 24 25 public virtual Customer Customer { get; set; } 26 public virtual ICollection<OrderDetail> OrderDetails { get; set; } 27 } 28 29 public partial class OrderDetail 30 { 31 public int OrderId { get; set; } 32 public int ProductId { get; set; } 33 public decimal UnitPrice { get; set; } 34 public int Quantity { get; set; } 35 36 public virtual Order Order { get; set; } 37 public virtual Product Product { get; set; } 38 } 39 public partial class Product 40 { 41 public Product() 42 { 43 this.OrderDetails = new HashSet<OrderDetail>(); 44 } 45 46 public int ProductId { get; set; } 47 public string ProductName { get; set; } 48 public decimal UnitPrice { get; set; } 49 50 public virtual ICollection<OrderDetail> OrderDetails { get; set; } 51 }
注意,沒有Product到OrdeDetail的關聯,因為我們在設計器中移除了導航屬性(譯注:實際上,沒有移除)
6、為了使用POCO類,實體框架生成了DbContext的派生類。這個類將我們模型中的每個實體公布成ObjectSet<T>類型。 代碼清章8-2演示了,這個類的定義。
代碼清單8-2. 生成模型時創建的DbContext派生類
1 public partial class EFRecipesEntities : DbContext 2 { 3 public EFRecipesEntities() 4 : base("name=EFRecipesEntities") 5 { 6 } 7 8 protected override void OnModelCreating(DbModelBuilder modelBuilder) 9 { 10 throw new UnintentionalCodeFirstException(); 11 } 12 13 public DbSet<Customer> Customers { get; set; } 14 public DbSet<Order> Orders { get; set; } 15 public DbSet<OrderDetail> OrderDetails { get; set; } 16 public DbSet<Product> Products { get; set; } 17 }
這樣就完成了使用生成POCO類的模型,代碼清單8-3演示了從模型中插入和獲取數據
代碼清單8-3. 使用POCO類
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 RunExample(); 6 } 7 8 static void RunExample() 9 { 10 using (var context = new EFRecipesEntities()) 11 { 12 var tea = new Product { ProductName = "Green Tea", UnitPrice = 1.09M }; 13 var coffee = new Product 14 { 15 ProductName = "Colombian Coffee", 16 UnitPrice = 2.15M 17 }; 18 var customer = new Customer { ContactName = "Karen Marlowe" }; 19 var order1 = new Order { OrderDate = DateTime.Parse("10/06/13") }; 20 order1.OrderDetails.Add(new OrderDetail 21 { 22 Product = tea, 23 Quantity = 4, 24 UnitPrice = 1.00M 25 }); 26 order1.OrderDetails.Add(new OrderDetail 27 { 28 Product = coffee, 29 Quantity = 3, 30 UnitPrice = 2.15M 31 }); 32 customer.Orders.Add(order1); 33 context.Customers.Add(customer); 34 context.SaveChanges(); 35 } 36 37 using (var context = new EFRecipesEntities()) 38 { 39 var query = context.Customers.Include("Orders.OrderDetails.Product"); 40 foreach (var customer in query) 41 { 42 Console.WriteLine("Orders for {0}", customer.ContactName); 43 foreach (var order in customer.Orders) 44 { 45 Console.WriteLine("--Order Date: {0}--", 46 order.OrderDate.ToShortDateString()); 47 foreach (var detail in order.OrderDetails) 48 { 49 Console.WriteLine( 50 "\t{0}, {1} units at {2} each, unit discount: {3}", 51 detail.Product.ProductName, 52 detail.Quantity.ToString(), 53 detail.UnitPrice.ToString("C"), 54 (detail.Product.UnitPrice - detail.UnitPrice).ToString("C")); 55 } 56 } 57 } 58 } 59 Console.WriteLine("Enter input to exit:"); 60 string line = Console.ReadLine(); 61 if (line == "exit") 62 { 63 return; 64 }; 65 } 66 }
代碼清單8-3的輸出如下:
Orders for Karen Marlowe --Order Date: 4/19/2010-- Green Tea, 4 units at $1.00 each, unit discount: $0.09 Colombian Coffee, 3 units at $2.15 each, unit discount: $0.00
原理
生成POCO類,是當前版本實體框架的默認特性。代碼生成策略的屬性值已經被設置為None。上下文對象也是被單獨生成。所以POCO類中已經沒有了數據訪問代碼。
如果與模型中實體對應的所有類已經被創建,它們簡單,潔凈。這樣的話就,沒有代碼生成,沒有上下文被生成。為了實現適合我們模型和實體的上下文對象,派生至DbContext的一個新類在數據模型生成時就被創建了。這個類還提供了對應每個實體的,類型為DbSet<T>的屬性。 默認情況下,我們的上下文對象EFRecipesEntities,已經包含了能連接數據庫的構造函數代碼。
實體框架交流QQ群: 458326058,歡迎有興趣的朋友加入一起交流
謝謝大家的持續關注,我的博客地址:http://www.cnblogs.com/VolcanoCloud/