当前位置: 首页 > news >正文

《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的实例,如果获取成百上千张的照片,这将为我们节约大量的时间和带宽。

  本篇到此结束,如果你在坚持看本系列话,请点推荐以示支持。谢谢~

转载于:https://www.cnblogs.com/yunxiaguo/p/5819546.html

相关文章:

  • java解惑你知多少(二)
  • lnmp的使用
  • java解惑你知多少(三)
  • linux挂载windows共享文件夹
  • java解惑你知多少(四)
  • Android selector的使用
  • java解惑你知多少(五)
  • 快速傅里叶变换FFT学习小记
  • java解惑你知多少(六)
  • ThinkPHP3.2.3扩展之生成PDF文件(MPDF)
  • codeforces 711E E. ZS and The Birthday Paradox(数学+概率)
  • java解惑你知多少(七)
  • css3 TransformZ() 3D缩放
  • java解惑你知多少(八)
  • 多线程总结之旅(8):线程同步之信号量
  • [分享]iOS开发-关于在xcode中引用文件夹右边出现问号的解决办法
  • Android开发 - 掌握ConstraintLayout(四)创建基本约束
  • Android组件 - 收藏集 - 掘金
  • Javascript设计模式学习之Observer(观察者)模式
  • Mysql数据库的条件查询语句
  • php面试题 汇集2
  • Python进阶细节
  • Python学习之路13-记分
  • WinRAR存在严重的安全漏洞影响5亿用户
  • 前嗅ForeSpider教程:创建模板
  • 新书推荐|Windows黑客编程技术详解
  • 一起来学SpringBoot | 第十篇:使用Spring Cache集成Redis
  • 这几个编码小技巧将令你 PHP 代码更加简洁
  • zabbix3.2监控linux磁盘IO
  • ​【原创】基于SSM的酒店预约管理系统(酒店管理系统毕业设计)
  • ​七周四次课(5月9日)iptables filter表案例、iptables nat表应用
  • # Apache SeaTunnel 究竟是什么?
  • #我与Java虚拟机的故事#连载04:一本让自己没面子的书
  • (13)Hive调优——动态分区导致的小文件问题
  • (1综述)从零开始的嵌入式图像图像处理(PI+QT+OpenCV)实战演练
  • (26)4.7 字符函数和字符串函数
  • (c语言)strcpy函数用法
  • (DFS + 剪枝)【洛谷P1731】 [NOI1999] 生日蛋糕
  • (LeetCode C++)盛最多水的容器
  • (SpringBoot)第七章:SpringBoot日志文件
  • (分布式缓存)Redis哨兵
  • (分类)KNN算法- 参数调优
  • (一)python发送HTTP 请求的两种方式(get和post )
  • (转)es进行聚合操作时提示Fielddata is disabled on text fields by default
  • (转)大道至简,职场上做人做事做管理
  • .gitignore文件设置了忽略但不生效
  • .h头文件 .lib动态链接库文件 .dll 动态链接库
  • .NET 8 中引入新的 IHostedLifecycleService 接口 实现定时任务
  • .NET CF命令行调试器MDbg入门(二) 设备模拟器
  • .Net CF下精确的计时器
  • .NET Framework 的 bug?try-catch-when 中如果 when 语句抛出异常,程序将彻底崩溃
  • .NET 程序如何获取图片的宽高(框架自带多种方法的不同性能)
  • .net获取当前url各种属性(文件名、参数、域名 等)的方法
  • .NET开发不可不知、不可不用的辅助类(三)(报表导出---终结版)
  • .NET项目中存在多个web.config文件时的加载顺序