zoukankan      html  css  js  c++  java
  • 《Entity Framework 6 Recipes》中文翻译系列 (36) ------ 第六章 继承与建模高级应用之TPC继承映射

    翻译的初衷以及为什么选择《Entity Framework 6 Recipes》来学习,请看本系列开篇

    6-12  TPC继承映射建模

    问题

      你有两张或多张架构和数据类似的表,你想使用TPC继承映射为这些表建模。

    解决方案

      假设我们有如图6-18所示的表。

    图6-18 表Toyota和BMW有相似的结构,它们可以成为派生至实体Car的派生类型

      在图6-18中,表Toyota和BMW有相似的架构(Schema),并描述类似的数据。BMW表只多了额外的一列,它用一bit值来指示对应的实例是否具有避免碰撞(collision-avoidance)特性。我们想在建模中,用一个基类来持有表Toyta和BMW的公共属性。另外,我们还想表示经销商与汽车库存量之间的一对多关系。 图6-22(译注:应该是图6-19)展示的是最终的模型。

      按下面的步骤创建模型:

        1、在你的项目中添加一个ADO.NET Entity Data Model(ADO.NET实体数据模型),并导入表Toyota,BMW,CarDealer和Dealer;

        2、右键设计器,并选择Add(新增)➤Entity(实体)。命名新实体为Car,不勾选Create Key property(创建键属性)复选框;

        3、右键实体Car,选择Properties(属性)。设置Abstract(抽象)属性为True;

        4、将实体Toyota和BMW的公共属性移动到实体Car中,可以使用Cut/Paste(剪切/粘贴)来完成移动。确保Toyota实体没有属性,BMW实体只有CollisionAvoidance实体。这两个实体将从实体Car继承公共属性;

        5、右键Car实体,选择Add(增加) ➤Inheritance(继承)。选择Car作为基类,BMW作为派生类。

        6、重复上一步的操作,选择Car作为基类,Toyota作为派生类型。

        7、右键CarDealer实体,选择Delete(删除)。当提示是否从存储模型删除CarDealer时,选择No(否);

        8、右键设计器,选择Add(增加) ➤Association(关联)。命名关联为CarDealer。在左边选择Dealer,并将多重性设置为1,在右边选择Car,并将多重性设置为多。将Car这边的导航属性命名为Dealer,将Dealer这边的导航属性命名为Cars。确保不勾选Add foreign key properties(添加外键属性);

        9、选择关联,然后查看Mapping Details window(映射详细信息窗口)。在Add a Talbe or View(添加表或视图)下面菜单中,选择CarDealer。确保DealerId属性映射到DealerId列,CarID属性映射到CarId列;

      在解决方案浏览器中右键.edmx文件,选择Open With(打开方式) ➤XML Editor(XML文本编辑器),使用代码清单6-3中的更改为实体BMW和Toyota编辑映射节.

    代码清单6-35. 映射实体BMW和Toyota到表

     1     <EntitySetMapping Name="Cars">
     2                 <EntityTypeMapping TypeName="IsTypeOf(Apress.EF6Recipes.BeyondModelingBasics.Recipe12.BMW)">
     3                     <MappingFragment StoreEntitySet="BMW">
     4                         <ScalarProperty Name="CollisionAvoidance"
     5                                         ColumnName="CollisionAvoidance" />
     6                         <ScalarProperty Name="CarId" ColumnName="CarId"/>
     7                         <ScalarProperty Name="Model" ColumnName="Model"/>
     8                         <ScalarProperty Name="Year" ColumnName="Year"/>
     9                         <ScalarProperty Name="Color" ColumnName="Color"/>
    10                     </MappingFragment>
    11                 </EntityTypeMapping>
    12                 <EntityTypeMapping TypeName="IsTypeOf(Apress.EF6Recipes.BeyondModelingBasics.Recipe12.Toyota)">
    13                     <MappingFragment StoreEntitySet="Toyota">
    14                         <ScalarProperty Name="CarId" ColumnName="CarId"/>
    15                         <ScalarProperty Name="Model" ColumnName="Model"/>
    16                         <ScalarProperty Name="Year" ColumnName="Year"/>
    17                         <ScalarProperty Name="Color" ColumnName="Color"/>
    18                     </MappingFragment>
    19                 </EntityTypeMapping>
    20             </EntitySetMapping>

    最终的模型如图6-19所示。

    图6-19  概念模型,它描述了派生类型BMW和Toyota在数据库表示为单独的表

    原理

      TPC是一个有趣的继承模型,它允许每个派生类实体映射到单独的物理表。从实用的角度来看,表至少有一部分公共的架构。 这个公共的的架构被映射到基类,额外部分的架构映射到派生类实体。为了让TPC继承模型正常工作,实体键在整个相关表(译注:如本示例中的表Toyota和BMW)中必须唯一

      基类实体被标记为抽象类型,它不被映射到任何表。在TPC中,只有派生类型实体被映射到表

      在我们的示例中,我们标记Car实体为抽象类型,不对它进行映射。注意,在代码清单6-35的映射中,我们只映射派生类型BMW和Toyota。我们将公共属性(CarID,Model,Year和Color)移动到了基类实体。派生类只包含属于自己的独特的属性。 实例中,BMW实体的实例有一个额外的属性CollisionAvoidance。

      因为实体Toyota和BMW派生至实体Car,所以,它们变成了相同实体集Cars的一部分。这意味着,实体键必须在包含整个派生类型的实体集中唯一。因为实体被映射到不同的表,所以我有可能会碰到相同的键。为了避免这种情况的发生,我们将每张表的CarId列设置为标识列。对于BMW表,我们将主键的种子(基数)初始化为1并设置递增为2,这将会为主键CarId创建奇数值。对于Toyota表,我们将主键的种子初始化为2并设置增量为2,这将为主键CarId创建偶数值。

      当在使用TPC继承映射建模关系时,在派生中定义关系比在基类中定义更好。这是因为实体框架在运行时不知道关联的另一端是哪张物理表。当然,在我们的示例中,我们提供了一张单独的表(CarDealer),它包含了关系。这就允许我们可以在基类中映射关系到CarDealer表。

      在很多使用TPC继承映射的实际应用,最常见的也许是,处理存档数据。假设你的电子商务网站有多年的订单数据。在每年的年终,你将前面12个月的订单存档在Archive表,并为新的一年准备一张空表。你可以使用这里演示的方法,用TPC为当前和存档的订单建模。

      TPC继承映射跟别的继承映射相比,有一个特别重要的性能优势。当查询一个一派生类型时,产生针对底层数据库表的查询,没有像TPT继承映射中额外的join连接,也没有像TPH中的过滤。对于有几个派生类型的大型数据集或模型,这种性能优势显得至关重要。

      TPC继承映射的缺点是,包含对潜在重复数据的开销,和在整个相关表中确保主键唯一的复杂性。在存档场景中,数据不是重复,只是简单的分布在多张表中。在别的场景中,数据(属性)在相关表中可能会有重复。

      代码清单6-36演示了从模型中插入和获取数据。

    代码清单6-36.从模型中插入和获取数据

     1  using (var context = new Recipe12Context())
     2             {
     3                 var d1 = new Dealer { Name = "All Cities Toyota" };
     4                 var d2 = new Dealer { Name = "Southtown Toyota" };
     5                 var d3 = new Dealer { Name = "Luxury Auto World" };
     6                 var c1 = new Toyota
     7                 {
     8                     Model = "Camry",
     9                     Color = "Green",
    10                     Year = 2014,
    11                     Dealer = d1
    12                 };
    13                 var c2 = new BMW
    14                 {
    15                     Model = "310i",
    16                     Color = "Blue",
    17                     CollisionAvoidance = true,
    18                     Year = 2014,
    19                     Dealer = d3
    20                 };
    21                 var c3 = new Toyota
    22                 {
    23                     Model = "Tundra",
    24                     Color = "Blue",
    25                     Year = 2014,
    26                     Dealer = d2
    27                 };
    28                 context.Dealers.Add(d1);
    29                 context.Dealers.Add(d2);
    30                 context.Dealers.Add(d3);
    31                 context.SaveChanges();
    32             }
    33 
    34             using (var context = new Recipe12Context())
    35             {
    36                 Console.WriteLine("Dealers and Their Cars");
    37                 Console.WriteLine("======================");
    38                 foreach (var dealer in context.Dealers)
    39                 {
    40                     Console.WriteLine("
    Dealer: {0}", dealer.Name);
    41                     foreach (var car in dealer.Cars)
    42                     {
    43                         string make = string.Empty;
    44                         if (car is Toyota)
    45                             make = "Toyota";
    46                         else if (car is BMW)
    47                             make = "BMW";
    48                         Console.WriteLine("	{0} {1} {2} {3}", car.Year,
    49                                            car.Color, make, car.Model);
    50                     }
    51                 }
    52             }

    代码清单6-36的输出如下:

    Dealer: Luxury Auto World
        2014 Blue BMW 310i
    Dealer: Southtown Toyota
        2014 Blue Toyota Tundra
    Dealer: All Cities Toyota
        2014 Green Toyota Camry

    实体框架交流QQ群:  458326058,欢迎有兴趣的朋友加入一起交流

    谢谢大家的持续关注,我的博客地址:http://www.cnblogs.com/VolcanoCloud/

  • 相关阅读:
    Java生鲜电商平台-SpringCloud微服务架构中核心要点和实现原理
    Java生鲜电商平台-SpringCloud微服务架构中网络请求性能优化与源码解析
    Java生鲜电商平台-秒杀系统微服务架构设计与源码解析实战
    超硬核Java学习路线图+学习资源+实战项目汇总,看完以后不用再问我怎么学Java了!
    还没使用过消息队列?这一份书单值得你好好看看!
    学习Spring,看这几本书就够了
    这份书单,想要晋级高级Java工程师的朋友值得一看!
    搞定JVM基本原理和面试题,看看这几本书妥妥够了!
    《自拍教程16》cmd的常用技巧
    《自拍教程15》命令行软件的通用技巧
  • 原文地址:https://www.cnblogs.com/VolcanoCloud/p/4539669.html
Copyright © 2011-2022 走看看