转载 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. JS操作JSON总结(转)

    JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式,采用完全独立于语言的文本格式,是理想的数据交换格式.同时,JSON是 JavaScript 原生格式,这意 ...

  2. shell 实例脚本

    例1: #!/bin/bashsum=0;for i in {1..100..2}do let "sum+=i"doneecho "the sum is $sum&quo ...

  3. List IEnumerable

    //按部门汇总            IEnumerable<WeekReportWithDepartmentInfo> report = summary            .Grou ...

  4. Ubuntu上VNC 配置

    Ubuntu下VNC配置文章分类:操作系统通过将服务器配置成VNC SERVER,可以让其他主机使用图形方式登录这台服务器. 在ubuntu下配置vnc server很简单,方法如下: 服务器端: 1 ...

  5. 在文件地理数据库中使用 SQL 进行报告和分析 (转)

    ================以下摘自ArcGIS10.1帮助=================== 文件地理数据库允许在 QueryDef 中通过 SubFields(字段列表)方法使用表达式和别 ...

  6. JVM中的Stack和Heap

    Stack: 是内存指令区.Java基本数据类型,Java指令代码,常量都保存在stack中,方法是指令也保存在stack中. 由于stack是内存是顺序分配,而且定长,不存在内存回收问题.存取速度快 ...

  7. 2013调试sql的方法

    view-sql server object explorer- 连接数据库-成功以后再服务器点击允许debug-在存储过程里面添加断点即可

  8. 使用ROW_NUMBER进行的快速分页

    DECLARE @pageSize INT ; DECLARE @pageIndex INT ; SET @pageSize = 5 SET @pageIndex =2 ; --第二页,每页显示5条数 ...

  9. PHP session 失效不传递的解决办法

    PHP中,session不能传递到下一个页面去,一般有两种情况: 我们先写个php文件:<?=phpinfo()?>, 传到服务器去看看服务器的参数配置. 转到session部分,看到se ...

  10. Visual C++ 打印编程技术-打印基础知识

    打印机介绍 1.打印术语 *: 1 英寸= 2.54 厘米(cm)= 25.4 毫米(mm) cpi (Characters Per Inch): 每英寸内所含的字符数,用来表示字符的大小.间距 cp ...