转载 http://www.th7.cn/Program/net/201301/122153.shtml

Code First如何处理类之间的继承关系。Entity Framework Code First有三种处理类之间继承关系的方法,我们将逐一介绍这三种处理方法。

1.Table Per Hierarchy(TPH): 只建立一个表,把基类和子类中的所有属性都映射为表中的列。

2.Table Per Type(TPT): 为基类和每个子类建立一个表,每个与子类对应的表中只包含子类特有的属性对应的列。

3.Table Per Concrete Type(TPC):为每个子类建立一个表,每个与子类对应的表中包含基类的属性对应的列和子类特有属性对应的列。

1.Table Per Hierarchy(TPH)

在这种处理方式中,Entity Framework Code First为基类和所有子类建立一个表,基类和子类中的所有属性都映射为表中的一个列。Entity Framework Code First默认在这个表中建立一个叫做Discriminator的列,类型是nvarchar,长度是128。Entity Framework Code First会在存储基类或子类的时候,把类名作为Discriminator列的值。

在我们前面的示例程序中,由于我们要记录订单是被谁创建的,以及是被谁批准的,我们新增了一个SalesPerson类。

public class SalesPerson    {     
   public string EmployeeID { get; set; }    
    public string Name { get; set; }    
    public string Gender { get; set; }    
    public DateTime HiredDate { get; set; }  
  }

并且在Order类中增加了两个SalesPerson的实例用于记录订单的创建人和批准人。

public SalesPerson CreatedBy { get; set; }public SalesPerson ApprovedBy { get; set; }

我们后来细化了我们的业务流程:订单是由销售员创建的;当客户要求的订单折扣过高时,需要销售经理的审批;经理每个月都有固定的折扣审批总额。销售员和销售经理都属于销售人员。这是一个典型的继承关系。

根据我们细化之后的业务流程,我们创建了两个新的类, SalesMan和SalesManager

public class SalesMan : SalesPerson {  
   public decimal DiscountLimit { get; set; } 
}
public class SalesManager : SalesPerson {   
  public decimal DiscountAmountPerMonth { get; set; }
 }

由于创建订单的时候涉及到了复杂的业务逻辑,需要为订单指定Customer和SalesMan, 我们新建了一个factory类用于创建订单。

public static class OrderFactory{   
     public static Order CreateNewOrder(Customer customer, SalesMan createUser)        {            Order order = new Order();  
          order.Customer = customer;   
         order.CreatedDate = DateTime.Now;      
      order.CreatedBy = createUser;   
         order.ApprovedBy = null;    
        return order;         }
}

我们新建一个单元测试方法用于测试我们新的销售人员继承关系以及新的订单factory类。

[TestMethod]
public void CanAddOrderWithSalesMan() {  
          OrderSystemContext unitOfWork = new OrderSystemContext();            ProductRepository productRepository = new ProductRepository(unitOfWork);            OrderRepository orderRepository = new OrderRepository(unitOfWork);            CustomerRepository customerRepository = new CustomerRepository(unitOfWork);            SalesMan salesman = new SalesMan {
 EmployeeID = "2012001",
 Gender = "M",
 Name = "Eric", 
HiredDate = DateTime.Parse("2010-5-19") };  
Customer customer = customerRepository.GetCustomerById("120104198403082113");           
 Order order = OrderFactory.CreateNewOrder(customer, salesman);            order.AddNewOrderItem(productRepository.GetProductCatalogById(1).GetProductInStock(), 5100);            
orderRepository.AddNewOrder(order);          
  unitOfWork.CommitChanges(); }

执行完我们的测试程序之后,我们可以打开SQL Server去看一下Code First默认情况下是如何处理类之间的继承关系的。

Code First默认会把基类和子类的所有属性都映射成一个表中的列,并且会增加一个Discriminator列标识存进去的是哪个类的实例。

如果你不喜欢Discriminator这个有点奇怪的名字,你可以自己定义Discriminator列的名字以及它的类型。我们使用map方法定义该列的名字和类型。我们可以将它命名为Title。

public class SalesPersonValueObjectConfiguration: EntityTypeConfiguration<SalesPerson>    {
        public SalesPersonValueObjectConfiguration()     
   {            HasKey(p => p.EmployeeID).Property(p => p.EmployeeID).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);            Property(p => p.Name).IsRequired().HasMaxLength(100);   
         Property(p => p.Gender).IsRequired().HasMaxLength(1);     
       Map<SalesMan>(salesman => { salesman.Requires("Title").HasValue("SalesMan"); });   
         Map<SalesManager>(manager => { manager.Requires("Title").HasValue("Sales Manager"); });        }    }

Map方法中传入的类型参数是子类的类名,Requires用于指定Discriminator列的名字,HasValue用于指定它的类型和每个子类对应的值。

我们可以重新执行我们的测试程序,然后打开SQL Server,去看一下新建的数据库表结构。

这个列的类型不仅可以是字符串,还可以是bit标志位,比如说我们把区分salesman和salemanager的列设为bit型,列的名字叫做IsManager.

Map<SalesMan>(salesman => { salesman.Requires("IsManager").HasValue(false); });
Map<SalesManager>(manager => { manager.Requires("IsManager").HasValue(true); });

我们只需要把HasValue中传入的值变为true和false,Code First会自动把IsManager列的类型设置为bit。

2.Table Per Type(TPT)

在这种处理方式中,Entity Framework Code First会为每个基类和子类建立一个表,子类的表中只包含子类特有的属性。

我们可以使用Map方法强制让Code First使用TPT方式,因为Code First默认使用的是TPC方式。

public class SalesPersonValueObjectConfiguration: EntityTypeConfiguration<SalesPerson>    {        public SalesPersonValueObjectConfiguration()        {            HasKey(p => p.EmployeeID).Property(p => p.EmployeeID).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None); 
           Property(p => p.Name).IsRequired().HasMaxLength(100);     
       Property(p => p.Gender).IsRequired().HasMaxLength(1);  
          Map<SalesMan>(salesman => { salesman.ToTable("SalesMan"); });            Map<SalesManager>(manager => { manager.ToTable("Manager"); });        } 
   }

我们通过使用ToTable方法,让Code First为每个子类型建立一个表,表的名字就是ToTable方法中传入的参数值,子类对应的表中的主键与基类对应的表中的主键名字相同,同时它还是指向基类对应的表的外键。

我们还使用上面的那个测试方法来测试一下Code First按照TPT的方式建立的数据表结构。

3.Table Per Concrete Type(TPC)

在这种处理方式中,Entity Framework Code First为每一个子类建立一个表,在子类对应的表中除了子类特有的属性外还有基类的属性对应的表。

和TPT一样,我们也需要通过Map方法进行设置。

public class SalesPersonValueObjectConfiguration: EntityTypeConfiguration<SalesPerson>    {   
     public SalesPersonValueObjectConfiguration()        {    
        HasKey(p => p.EmployeeID).Property(p => p.EmployeeID).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);   
         Property(p => p.Name).IsRequired().HasMaxLength(100);     
       Property(p => p.Gender).IsRequired().HasMaxLength(1);       
     Map<SalesMan>(salesman => { salesman.ToTable("SalesMan"); 
salesman.MapInheritedProperties(); });       
     Map<SalesManager>(manager => { manager.ToTable("Manager"); manager.MapInheritedProperties(); });        }    }

通过MapInheritedProperties方法就可以强制Code First使用TPC方式。

我们重新编译之后执行我们原来的测试方法,可以得到不同的数据表结构,Code First不会为基类建立表,而是为每个子类都建立一个表,将子类的内容和基类的内容都存储到各个子类对应的表中。

PS:如果你的基类是abstract,效果也是一样的。

最后需要探讨的一个问题是我们在实际项目中应该使用哪种方式呢?

1.不推荐使用TPC(Type Per Concrete Type),因为在TPC方式中子类中包含的其他类的实例或实例集合不能被映射为表之间的关系。你必须通过手动地在类中添加依赖类的主键属性,从而让Code First感知到它们之间的关系,而这种方式是和使用Code First的初衷相反的。

2.从查询性能上来说,TPH会好一些,因为所有的数据都存在一个表中,不需要在数据查询时使用join。

3.从存储空间上来说,TPT会好一些,因为使用TPH时所有的列都在一个表中,而表中的记录不可能使用所有的列,于是有很多列的值是null,浪费了很多存储空间。

4.从数据验证的角度来说,TPT好一些,因为TPH中很多子类属性对应的列是可为空的,就为数据验证增加了复杂性。

Entity Framework Code First 映射继承关系的更多相关文章

  1. Entity Framework Code First关系映射约定

    本篇随笔目录: 1.外键列名默认约定 2.一对多关系 3.一对一关系 4.多对多关系 5.一对多自反关系 6.多对多自反关系 在关系数据库中,不同表之间往往不是全部都单独存在,而是相互存在关联的.两个 ...

  2. Entity Framework Code First主外键关系映射约定

    本篇随笔目录: 1.外键列名默认约定 2.一对多关系 3.一对一关系 4.多对多关系 5.一对多自反关系 6.多对多自反关系 在关系数据库中,不同表之间往往不是全部都单独存在,而是相互存在关联的.两个 ...

  3. Entity Framework Code First关系映射约定【l转发】

    本篇随笔目录: 1.外键列名默认约定 2.一对多关系 3.一对一关系 4.多对多关系 5.一对多自反关系 6.多对多自反关系 在关系数据库中,不同表之间往往不是全部都单独存在,而是相互存在关联的.两个 ...

  4. Entity Framework Code First属性映射约定

    Entity Framework Code First与数据表之间的映射方式有两种实现:Data Annotation和Fluent API.本文中采用创建Product类为例来说明tity Fram ...

  5. 补习知识:Entity Framework Code First属性映射约定

    Entity Framework Code First与数据表之间的映射方式有两种实现:Data Annotation和Fluent API.本文中采用创建Product类为例来说明tity Fram ...

  6. Entity Framework Code First属性映射约定 转载https://www.cnblogs.com/libingql/p/3352058.html

    Entity Framework Code First属性映射约定   Entity Framework Code First与数据表之间的映射方式有两种实现:Data Annotation和Flue ...

  7. MVC使用Entity Framework Code First,用漂亮表格显示1对多关系

    部门和职员是1对多关系.用一个表格列出所有部门,并且在每行显示该部门下的所有职员名称.如下: 部门和职员的Model: using System.Collections.Generic; namesp ...

  8. 使用 Entity Framework Code First

    使用 Entity Framework Code First 在家闲着也是闲着,继续写我的[ASP.NET MVC 小牛之路]系列吧.在该系列的上一篇博文中,在显示书本信息列表的时候,我们是在程序代码 ...

  9. Entity Framework Code first(转载)

    一.Entity Framework Code first(代码优先)使用过程 1.1Entity Framework 代码优先简介 不得不提Entity Framework Code First这个 ...

随机推荐

  1. css 行内元素和块级元素

    1. 块级元素默认在新行开始,如常见的div和p标签,行内元素默认在同行开始显示,如a,span标签 2.块级元素一般用于做容器,可容纳行内和块级元素,可设置width和height,行内元素只能容纳 ...

  2. C# - 系统类 - Object类

    Object类 ns:System 此类是所有.NET Framework中的类的基类 Type类就派生自Object类 C#提供了object关键字来表示一个类实例的类型 而无需使用Object作为 ...

  3. 解锁Dagger2使用姿势(一)

    毫无疑问,Dagger2的 上手是有门槛的,有门槛是因为它里边的概念多,用起来复杂,可是一旦你学会了Dagger2的使用,你一定会爱不释手的.与ButterKnife和AndroidAnnotatio ...

  4. Spring MVC 3.0.5+Spring 3.0.5+MyBatis3.0.4全注解实例详解(二)

    在上一篇文章中我详细的介绍了如何搭建maven环境以及生成一个maven骨架的web项目,那么这章中我将讲述Spring MVC的流程结构,Spring MVC与Struts2的区别,以及例子中的一些 ...

  5. 深入理解计算机系统第二版习题解答CSAPP 2.7

    下面的函数将输出什么结果? const char *s = "abcdef"; show_bytes((byte_pointer) s, strlen(s)); 其中字母'a'~' ...

  6. 图解I/O的五种模型

    1.1 五种I/O模型 1)阻塞I/O 2)非阻塞I/O 3)I/O复用 4)事件(信号)驱动I/O 5)异步I/O 1.2 为什么要发起系统调用? 因为进程想要获取磁盘中的数据,而能和磁盘打交道的只 ...

  7. 万网免费主机wordpress快速建站教程-wordpress下载及安装

    进入wordpress官网(http://cn.wordpress.org)下载最新的wordpress安装程序,下载完成后解压到任意电脑目录. 解压完毕后,使用FTP管理工具上传安装文件至主机htd ...

  8. VS2012 直接浏览网页时报错

    VS2012 直接浏览网页时报错  "托管管道模式不能为集成" 只要在configuration文件里面添加   <system.webServer>     < ...

  9. (转)ASP.net中Timer无刷新定时器.

    Timer控件要实现无刷新,得用到ajax技术 首先得添加一个ScriptManager控件,然后再添加一个UpdatePanel用于存放Timer控件内容的,就可以实现无刷新了.下面是详细的内容: ...

  10. AndroidListview 滑动过程中图片显示重复错乱解决方案

    主要分析Android中Listview滚动过程造成的图片显示重复.错乱.闪烁的原因及解决方法,顺便跟进Listview的缓存机制. 1.原因分析 Listview item 缓存机制:为了使得性能更 ...