2-6 拆分实体到多表

问题

  你有两张或是更多的表,他们共享一样的主键,你想将他们映射到一个单独的实体。

解决方案

  让我们用图2-15所示的两张表来演示这种情况。

图 2-15,两张表,Prodeuct 和ProductWebInfo,拥有共同的主键

  按下面的步骤为这两张表建模一个单独实体:

  1、在你的项目中,创建一个继承至DbContext的上下文对象EF6RecipesContext;

  2、使用代码清单2-8创建一个POCO实体Product; 

代码清单2-8:创建一个POCO实体Product

 public class Product {
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int SKU { get; set; }
public string Description { get; set; }
public decimal Price { get; set; }
public string ImageURL { get; set; }
}

  3、在EF6RecipesContext中添加类型为DbSet<Product>的属性Products;

  4、使用代码清单2-9在EF6RecipesContext中重写OnModelCreating()方法;

代码清单2-9 重写OnModelCreating()方法

 public class EF6RecipesContext : DbContext {
public DbSet<Product> Products { get; set; }
public EF6RecipesContext()
: base("name=EF6CodeFirstRecipesContext") {
}
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Product>()
.Map(m => {
m.Properties(p => new { p.SKU, p.Description, p.Price });
m.ToTable("Product", "Chapter2");
})
.Map(m => {
m.Properties(p => new { p.SKU, p.ImageURL });
m.ToTable("ProductWebInfo", "Chapter2");
});
}
}

原理

  这种情况常见于遗留系统中,一个表中的每一行都包含额外的,本该属于另一张表的信息。随着数据库变化,这样情况经常发生。没有人愿意去打破现有的代码,而是通过在一个关键的表中添加一些列来解决问题。处理这种情况的答案是,建一张新表来“移植”这对额外的列。

  通合并两张或多张并到一个单独的实体,通常也被叫作分拆一个实体到两张或多张数据库表,我可以把每个组成部分当成一个逻辑实体。这过程叫做垂直分拆。

  垂直分拆的缺点在,我们获取实体类型实例时,分拆的表需要一个额外的join(连接)来构建实体类型。这个额外的join如清单2-10所示:

清单2-10 垂直分拆需要额外的Join连接

 SELECT
[Extent1].[SKU] AS [SKU],
[Extent2].[Description] AS [Description],
[Extent2].[Price] AS [Price],
[Extent1].[ImageURL] AS [ImageURL]
FROM [dbo].[ProductWebInfo] AS [Extent1]
INNER JOIN [dbo].[Product] AS [Extent2] ON [Extent1].[SKU] = [Extent2].[SKU]

  插入和获取Product实体没有特别的要求。代码清单2-11演示了操作被垂直分拆的Product实体类型

  using (var context = new EF6RecipesContext()) {
var product = new Product {
SKU = ,
Description = "Expandable Hydration Pack",
Price = 19.97M,
ImageURL = "/pack147.jpg"
};
context.Products.Add(product);
product = new Product {
SKU = ,
Description = "Rugged Ranger Duffel Bag",
Price = 39.97M,
ImageURL = "/pack178.jpg"
};
context.Products.Add(product);
product = new Product {
SKU = ,
Description = "Range Field Pack",
Price = 98.97M,
ImageURL = "/noimage.jp"
};
context.Products.Add(product);
product = new Product {
SKU = ,
Description = "Small Deployment Back Pack",
Price = 29.97M,
ImageURL = "/pack202.jpg"
};
context.Products.Add(product);
context.SaveChanges();
}
using (var context = new EF6RecipesContext()) {
foreach (var p in context.Products) {
Console.WriteLine("{0} {1} {2} {3}", p.SKU, p.Description,
p.Price.ToString("C"), p.ImageURL);
}
}

代码清单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    

   public class Photograph {
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int PhotoId { get; set; }
public string Title { get; set; }
public byte[] ThumbnailBits { get; set; }
[ForeignKey("PhotoId")]
public virtual PhotographFullImage PhotographFullImage { get; set; }
}

    3、使用代码清单2-13创建一个POCO实体PhotographFullImage;

      代理清单2-13  创建一个POCO实体PhotographFullImage

     public class PhotographFullImage {
[Key]
public int PhotoId { get; set; }
public byte[] HighResolutionBits { get; set; }
[ForeignKey("PhotoId")]
public virtual Photograph Photograph { get; set; }
}

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

    5、在上下文对象EF6RecipesContext中添加另一个DbSet<PhotographFullImage>属性;

    6、使用代码清单2-14重写上下文对象中的OnModelCreating()方法;

      代码清单2-14 重写上下文对象中的OnModelCreating()方法

  protected override void OnModelCreating(DbModelBuilder modelBuilder) {
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Photograph>()
.HasRequired(p => p.PhotographFullImage)
.WithRequiredPrincipal(p => p.Photograph);
modelBuilder.Entity<Photograph>().ToTable("Photograph", "Chapter2");
modelBuilder.Entity<PhotographFullImage>().ToTable("Photograph", "Chapter2");
}

原理

  实体框架不直接支持延迟加载某个单一的实体属性。为了得到延迟加载成本昂贵属性的好处,利用实体框架延迟加载关联实体的特性,我们创建一个新的,包含成本昂贵的保存完整图像列的实体PhotographFullImage,一个Photograph实体和PhotographFullImange实体之单的关联。并且我们在概念层添加一个跟数据库引用约束相似的约束,告诉实体框架一个PhotographFullImage不能离开Photograph而独立存在。

  由于引用约束的存在,在模型中,我们有两件需要注意的事:一个是,当我们新建一个PhotographFullImage实体的实例或者调用SaveChages()方法之前,Photogrpah的实例必须存在上下文中。第二个是,如果我删除一个photograph,与之关联的photographFullImage也会被删除,这有点像是数据库中引用约束的级联删除。

  代码清单2-15 演示从模型中插入和获取数据。

代码清单2-15 插入和延迟加载成本昂贵的字段

             byte[] thumbBits = new byte[];
byte[] fullBits = new byte[];
using (var context = new EF6RecipesContext()) {
var photo = new Photograph {
Title = "My Dog",
ThumbnailBits = thumbBits
};
var fullImage = new PhotographFullImage { HighResolutionBits = fullBits };
photo.PhotographFullImage = fullImage;
context.Photographs.Add(photo);
context.SaveChanges();
}
using (var context = new EF6RecipesContext()) {
foreach (var photo in context.Photographs) {
Console.WriteLine("Photo: {0}, ThumbnailSize {1} bytes",
photo.Title, photo.ThumbnailBits.Length); //显式加载存储完整图像的字段
context.Entry(photo).Reference(p => p.PhotographFullImage).Load();
Console.WriteLine("Full Image Size: {0} bytes",
photo.PhotographFullImage.HighResolutionBits.Length);
}
}

  代码清单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/

《Entity Framework 6 Recipes》中文翻译系列 (7) -----第二章 实体数据建模基础之拆分实体到多表以及拆分表到多实体的更多相关文章

  1. 《Entity Framework 6 Recipes》翻译系列 (3) -----第二章 实体数据建模基础之创建一个简单的模型

    第二章 实体数据建模基础 很有可能,你才开始探索实体框架,你可能会问“我们怎么开始?”,如果你真是这样的话,那么本章就是一个很好的开始.如果不是,你已经建模,并在实体分裂和继承方面感觉良好,那么你可以 ...

  2. 《Entity Framework 6 Recipes》翻译系列 (5) -----第二章 实体数据建模基础之有载荷和无载荷的多对多关系建模

    2-3 无载荷(with NO Payload)的多对多关系建模 问题 在数据库中,存在通过一张链接表来关联两张表的情况.链接表仅包含连接两张表形成多对多关系的外键,你需要把这两张多对多关系的表导入到 ...

  3. 《Entity Framework 6 Recipes》翻译系列 (4) -----第二章 实体数据建模基础之从已存在的数据库创建模型

    不知道对EF感兴趣的并不多,还是我翻译有问题(如果是,恳请你指正),通过前几篇的反馈,阅读这个系列的人不多.不要这事到最后成了吃不讨好的事就麻烦了,废话就到这里,直奔主题. 2-2 从已存在的数据库创 ...

  4. 《Entity Framework 6 Recipes》翻译系列 (1) -----第一章 开始使用实体框架之历史和框架简述

    微软的Entity Framework 受到越来越多人的关注和使用,Entity Framework7.0版本也即将发行.虽然已经开源,可遗憾的是,国内没有关于它的书籍,更不用说好书了,可能是因为EF ...

  5. 《Entity Framework 6 Recipes》翻译系列(2) -----第一章 开始使用实体框架之使用介绍

    Visual Studio 我们在Windows平台上开发应用程序使用的工具主要是Visual Studio.这个集成开发环境已经演化了很多年,从一个简单的C++编辑器和编译器到一个高度集成.支持软件 ...

  6. 《Entity Framework 6 Recipes》中文翻译系列 (8) -----第二章 实体数据建模基础之继承关系映射TPT

    翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 2-8 Table per Type Inheritance 建模 问题 你有这样一 ...

  7. 《Entity Framework 6 Recipes》中文翻译系列 (9) -----第二章 实体数据建模基础之继承关系映射TPH

    翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 2-10 Table per Hierarchy Inheritance 建模 问题 ...

  8. 《Entity Framework 6 Recipes》中文翻译系列 (10) -----第二章 实体数据建模基础之两实体间Is-a和Has-a关系建模、嵌入值映射

    翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 2-11 两实体间Is-a和Has-a关系建模 问题 你有两张有Is-a和Has-a ...

  9. 《Entity Framework 6 Recipes》中文翻译系列 (6) -----第二章 实体数据建模基础之使用Code First建模自引用关系

    2-5 使用Code First建模自引用关系 问题 你的数据库中一张自引用的表,你想使用Code First 将其建模成一个包含自关联的实体. 解决方案 我们假设你有如图2-14所示的数据库关系图的 ...

随机推荐

  1. Beginning Scala study note(4) Functional Programming in Scala

    1. Functional programming treats computation as the evaluation of mathematical and avoids state and ...

  2. jQuery中事件绑定到bind、live、delegate、on方法的探究

    1. 给页面上的某个元素绑定事件,最初采用下面的方式实现: $(‘selector’).click(function(){ //code }); 缺点: 不能同时绑定多个事件,不能绑定动态的元素. 后 ...

  3. selenium使用笔记(一)

    有时候在交流群里经常会看到这样的问题,selenium能进行性能测试吗?selenium1和selenium2有什么区别等等问题,在这里谈一下自己学习和工作以后对selenium的认识.我所记录的东西 ...

  4. 封装jdbc 单例模式的应用

    实现增删该查的jdbc封装 import java.io.IOException; import java.io.InputStream; import java.sql.Connection; im ...

  5. mac apache 2.4的配置

    开启vhost 文件 命令行输入 vim /etc/apache2/httpd.conf 找到把前边的# include /private/etc/apache2/extra/httpd-vhost. ...

  6. HDU5838 Mountain(状压DP + 容斥原理)

    题目 Source http://acm.hdu.edu.cn/showproblem.php?pid=5838 Description Zhu found a map which is a N∗M ...

  7. 每天一个linux命令--批处理

    简单来说awk就是把文件逐行的读入,以空格为默认分隔符将每行切片,切开的部分再进行各种分析处理. 创建批处理脚本wanghy.sh: #!/bin/sh cd /opt/virgo-tomcat-se ...

  8. SpringMvc相关配置的作用

    1. <!-- 配置 handlerAdapter--> <bean class="org.springframework.web.servlet.mvc.SimpleCo ...

  9. 关于2016.12.12——T1的反思:凸包的意义与应用

    2016.12.12 T1 给n个圆,保证圆圆相离,求将圆围起来的最小周长.n<=100 就像上图.考场上,我就想用切线的角度来做凸包.以圆心x,y排序,像点凸包一样,不过用两圆之间的下切线角度 ...

  10. iOS推送原理

    1.首先app会和apns建立长连接,会发送 udid和 bundle id给apns 2.apns会返回给app一个device token 3.用会把device token 发给自己的服务器 4 ...