《Entity Framework 6 Recipes》中文翻譯系列 (7) -----第二章 實體數據建模基礎之拆分實體到多表以及拆分表到多實體


2-6 拆分實體到多表

問題

  你有兩張或是更多的表,他們共享一樣的主鍵,你想將他們映射到一個單獨的實體。

解決方案

  讓我們用圖2-15所示的兩張表來演示這種情況。

圖 2-15,兩張表,Prodeuct 和ProductWebInfo,擁有共同的主鍵

  按下面的步驟為這兩張表建模一個單獨實體:

  1、在你的項目中,創建一個繼承至DbContext的上下文對象EF6RecipesContext;

  2、使用代碼清單2-8創建一個POCO實體Product; 

代碼清單2-8:創建一個POCO實體Product

1 public class Product {
2         [Key]
3         [DatabaseGenerated(DatabaseGeneratedOption.None)]
4         public int SKU { get; set; }
5         public string Description { get; set; }
6         public decimal Price { get; set; }
7         public string ImageURL { get; set; }
8     }

  3、在EF6RecipesContext中添加類型為DbSet<Product>的屬性Products;

  4、使用代碼清單2-9在EF6RecipesContext中重寫OnModelCreating()方法;

代碼清單2-9 重寫OnModelCreating()方法

 1 public class EF6RecipesContext : DbContext {
 2         public DbSet<Product> Products { get; set; }
 3         public EF6RecipesContext()
 4             : base("name=EF6CodeFirstRecipesContext") {
 5         }
 6         protected override void OnModelCreating(DbModelBuilder modelBuilder) {
 7             base.OnModelCreating(modelBuilder);
 8             modelBuilder.Entity<Product>()
 9             .Map(m => {
10                 m.Properties(p => new { p.SKU, p.Description, p.Price });
11                 m.ToTable("Product", "Chapter2");
12             })
13             .Map(m => {
14                 m.Properties(p => new { p.SKU, p.ImageURL });
15                 m.ToTable("ProductWebInfo", "Chapter2");
16             });
17         }
18     }

原理

  這種情況常見於遺留系統中,一個表中的每一行都包含額外的,本該屬於另一張表的信息。隨着數據庫變化,這樣情況經常發生。沒有人願意去打破現有的代碼,而是通過在一個關鍵的表中添加一些列來解決問題。處理這種情況的答案是,建一張新表來“移植”這對額外的列。

  通合並兩張或多張並到一個單獨的實體,通常也被叫作分拆一個實體到兩張或多張數據庫表,我可以把每個組成部分當成一個邏輯實體。這過程叫做垂直分拆。

  垂直分拆的缺點在,我們獲取實體類型實例時,分拆的表需要一個額外的join(連接)來構建實體類型。這個額外的join如清單2-10所示:

清單2-10 垂直分拆需要額外的Join連接

1 SELECT
2 [Extent1].[SKU] AS [SKU],
3 [Extent2].[Description] AS [Description],
4 [Extent2].[Price] AS [Price],
5 [Extent1].[ImageURL] AS [ImageURL]
6 FROM [dbo].[ProductWebInfo] AS [Extent1]
7 INNER JOIN [dbo].[Product] AS [Extent2] ON [Extent1].[SKU] = [Extent2].[SKU]

  插入和獲取Product實體沒有特別的要求。代碼清單2-11演示了操作被垂直分拆的Product實體類型

 

 1  using (var context = new EF6RecipesContext()) {
 2                 var product = new Product {
 3                     SKU = 147,
 4                     Description = "Expandable Hydration Pack",
 5                     Price = 19.97M,
 6                     ImageURL = "/pack147.jpg"
 7                 };
 8                 context.Products.Add(product);
 9                 product = new Product {
10                     SKU = 178,
11                     Description = "Rugged Ranger Duffel Bag",
12                     Price = 39.97M,
13                     ImageURL = "/pack178.jpg"
14                 };
15                 context.Products.Add(product);
16                 product = new Product {
17                     SKU = 186,
18                     Description = "Range Field Pack",
19                     Price = 98.97M,
20                     ImageURL = "/noimage.jp"
21                 };
22                 context.Products.Add(product);
23                 product = new Product {
24                     SKU = 202,
25                     Description = "Small Deployment Back Pack",
26                     Price = 29.97M,
27                     ImageURL = "/pack202.jpg"
28                 };
29                 context.Products.Add(product);
30                 context.SaveChanges();
31             }
32             using (var context = new EF6RecipesContext()) {
33                 foreach (var p in context.Products) {
34                     Console.WriteLine("{0} {1} {2} {3}", p.SKU, p.Description,
35                     p.Price.ToString("C"), p.ImageURL);
36                 }
37             }

代碼清單2-11的輸出如下:

147 Expandable Hydration Pack $19.97 /pack147.jpg
178 Rugged Ranger Duffel Bag $39.97 /pack178.jpg
186 Range Field Pack $98.97 /noimage.jpg
202 Small Deployment Back Pack $29.97 /pack202.jpg

 

2-7 分拆一張表到多個實體

問題

  你有這樣的一張數據庫表,里面包含經常使用的字符,一些不常用的大字段。為了性能,需要避免每個查詢都去加載這些字段。你需要將這張表分拆成兩個或是更多的實體。

解決方案

  我們假設你有一張如圖2-16的表,它存儲照片的信息,以及照片的縮略圖和全分辨率圖。

圖2-16  Photograph表,有一個二進制的大對象字段,保存圖像數據

   按下面的步驟創建一個包含成本合理且經常使用列的實體,同時創建一個包含成本高且極少使用的高分辨位列的實體:

    1、在你的項目中創建一個繼承自DbContext的上下文對象EF6RecipesContext;

    2、使用代碼清單2-12創建一個POCO實體Photograph;

      代理清單2-12  創建一個POCO實體Photograph    

1   public class Photograph {
2         [Key]
3         [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
4         public int PhotoId { get; set; }
5         public string Title { get; set; }
6         public byte[] ThumbnailBits { get; set; }
7         [ForeignKey("PhotoId")]
8         public virtual PhotographFullImage PhotographFullImage { get; set; }
9     }

    3、使用代碼清單2-13創建一個POCO實體PhotographFullImage;

      代理清單2-13  創建一個POCO實體PhotographFullImage

1     public class PhotographFullImage {
2         [Key]
3         public int PhotoId { get; set; }
4         public byte[] HighResolutionBits { get; set; }
5         [ForeignKey("PhotoId")]
6         public virtual Photograph Photograph { get; set; }
7     }

    4、在上下文對象EF6RecipesContext中添加DbSet<Photograph>屬性;

    5、在上下文對象EF6RecipesContext中添加另一個DbSet<PhotographFullImage>屬性;

    6、使用代碼清單2-14重寫上下文對象中的OnModelCreating()方法;

      代碼清單2-14 重寫上下文對象中的OnModelCreating()方法

1  protected override void OnModelCreating(DbModelBuilder modelBuilder) {
2             base.OnModelCreating(modelBuilder);
3             modelBuilder.Entity<Photograph>()
4             .HasRequired(p => p.PhotographFullImage)
5             .WithRequiredPrincipal(p => p.Photograph);
6             modelBuilder.Entity<Photograph>().ToTable("Photograph", "Chapter2");
7             modelBuilder.Entity<PhotographFullImage>().ToTable("Photograph", "Chapter2");
8         }

原理

  實體框架不直接支持延遲加載某個單一的實體屬性。為了得到延遲加載成本昂貴屬性的好處,利用實體框架延遲加載關聯實體的特性,我們創建一個新的,包含成本昂貴的保存完整圖像列的實體PhotographFullImage,一個Photograph實體和PhotographFullImange實體之單的關聯。並且我們在概念層添加一個跟數據庫引用約束相似的約束,告訴實體框架一個PhotographFullImage不能離開Photograph而獨立存在。

  由於引用約束的存在,在模型中,我們有兩件需要注意的事:一個是,當我們新建一個PhotographFullImage實體的實例或者調用SaveChages()方法之前,Photogrpah的實例必須存在上下文中。第二個是,如果我刪除一個photograph,與之關聯的photographFullImage也會被刪除,這有點像是數據庫中引用約束的級聯刪除。

  代碼清單2-15 演示從模型中插入和獲取數據。

代碼清單2-15 插入和延遲加載成本昂貴的字段

 

 1             byte[] thumbBits = new byte[100];
 2             byte[] fullBits = new byte[2000];
 3             using (var context = new EF6RecipesContext()) {
 4                 var photo = new Photograph {
 5                     Title = "My Dog",
 6                     ThumbnailBits = thumbBits
 7                 };
 8                 var fullImage = new PhotographFullImage { HighResolutionBits = fullBits };
 9                 photo.PhotographFullImage = fullImage;
10                 context.Photographs.Add(photo);
11                 context.SaveChanges();
12             }
13             using (var context = new EF6RecipesContext()) {
14                 foreach (var photo in context.Photographs) {
15                     Console.WriteLine("Photo: {0}, ThumbnailSize {1} bytes",
16                         photo.Title, photo.ThumbnailBits.Length);
17 
18                     //顯式加載存儲完整圖像的字段
19                     context.Entry(photo).Reference(p => p.PhotographFullImage).Load();
20                     Console.WriteLine("Full Image Size: {0} bytes",
21                         photo.PhotographFullImage.HighResolutionBits.Length);
22                 }
23             }

  代碼清單2-15的輸出如下:

Photo: My Dog, Thumbnail Size: 100 bytes
Full Image Size: 2000 bytes

 

  代碼清單2-15創建並初始化實體Photograph和PhotographFullmage的實例對象,並將他們添加到上下文對象中,然后調用方法SaveChanges()保存。

  在查詢中,我們獲取數據庫中每一個photograph,打印它們的信息,並顯示加載與之關系的實體PhotographFullImage。注意,我們沒有關閉上下文中默認的延遲加載選項,這正是我們需要的。我們可以選擇不去加載PhotographFullImage的實例,如果獲取成百上千張的照片,這將為我們節約大量的時間和帶寬。

  本篇到此結束,如果你在堅持看本系列話,請點推薦以示支持。謝謝~

 

實體框架交流QQ群:  458326058,歡迎有興趣的朋友加入一起交流

謝謝大家的持續關注,我的博客地址:http://www.cnblogs.com/VolcanoCloud/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM