转载 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. preference activity框架

    从android3.0开始preference框架做了重大改变 框架由一下四部分组成 1.preference screen布局 一个xml文件,指定了要显示的Preference控件. 每个控件应当 ...

  2. [GIF] Colors in GIF Loop Coder

    In this lesson we cover the different methods for defining and animating colors in GIF Loop Coder. f ...

  3. android应用版本更新功能---完整版

    源码下载地址:csdn下载地址:http://download.csdn.net/download/csdn576038874/9526085 博客园下载地址:http://files.cnblogs ...

  4. Linux常用系统调用

    转载 http://www.ibm.com/developerworks/cn/linux/kernel/syscall/part1/appendix.html#icomments 按照惯例,这个列表 ...

  5. 混合文件系统(ramdisk+jffs)

    背景知识: 一.Ramdisk文件系统: 1.掉电丢失 2.读写速度高 3.数据存储到内存 二.jffs文件系统 1.掉电不丢失 2.可存储于NOR NAND,但是适用于NOR 3.数据存储于flas ...

  6. js广告浮动

    一个广告框在指定区域,有定位属性的父级区域内,一直向右向左移动,如果碰到左右边框,反向,如果碰到上下边距,反向,实现在指定框中浮动的效果. <!doctype html> <html ...

  7. 设置repeater每行多少个的方法

    前台代码: <asp:ScriptManagerProxy ID="ScriptManagerProxy1" runat="server">< ...

  8. store procedure 翻页

    store procedure 翻页例子 .turn page CREATE PROCEDURE pageTest --用于翻页的测试 --需要把排序字段放在第一列 ( )=null, --当前页面里 ...

  9. 响应式框架中,table表头自动换行的解决办法

    最近在用bootstrap开发网站,在处理一张table的时候发现,通过PC端查看样式正常,在手机上查看时,因为屏幕小,表格被压缩的厉害,表头和数据变形如下图 后来网上找了一下,发现一个好用的CSS属 ...

  10. 当使用VS CODE 时,如果窗口中打开的文件无法识别HTML的话,可以使用以下方法添加要识别的文件类型

    找到该文件并修改\Microsoft VS Code\resources\app\extensions\html\package.json{ "name": "html& ...