《Entity Framework 6 Recipes》中文翻译系列 -----第二章 实体数据建模基础之拆分实体到多表以及拆分表到多实体
Posted yunxia_云霞
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了《Entity Framework 6 Recipes》中文翻译系列 -----第二章 实体数据建模基础之拆分实体到多表以及拆分表到多实体相关的知识,希望对你有一定的参考价值。
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的实例,如果获取成百上千张的照片,这将为我们节约大量的时间和带宽。
本篇到此结束,如果你在坚持看本系列话,请点推荐以示支持。谢谢~
以上是关于《Entity Framework 6 Recipes》中文翻译系列 -----第二章 实体数据建模基础之拆分实体到多表以及拆分表到多实体的主要内容,如果未能解决你的问题,请参考以下文章
添加 [DataContract] 到 Entity Framework 6.0 POCO Template