EntityFramework Core 中的映射继承

Posted

技术标签:

【中文标题】EntityFramework Core 中的映射继承【英文标题】:Mapping inheritance in EntityFramework Core 【发布时间】:2016-10-20 12:26:31 【问题描述】:

我正在使用 EntityFramework Core、Code First 和 Fluent Api 来定义模型数据库,并且我已经关注了地图继承策略的情况:

public class Person

    public int Id  get; set; 
    public string Name  get; set; 


public class User : Person

    public string UserName  get; set; 
    public string Password  get; set; 


public class Employee : Person

    public decimal Salary  get; set; 


public class Customer:Person

    public long DiscountPoints  get; set; 

业务逻辑

在这种情况下,用户、员工和客户都是人,但员工和客户也是用户,员工可以成为客户。每种类型都用于不同的应用上下文。

我实现这种方式是为了避免不必要地使用来自其他应用程序上下文的值。

问题:

    映射数据库模型的最佳实践是什么?按层次结构类型或按类型表?考虑到对许多人来说,TPT 通常是一种反模式,并且会在以后导致严重的性能问题。 EF issues #2266

    一个。如果是TPH,如何对多种类型使用discriminator field?

    b.如果是 TPT,在 EF Core 1.0 中如何使用这个策略?

    它是业务模型的最佳架构吗?

感谢您的关注和合作

【问题讨论】:

【参考方案1】:

我在玩 EF 核心时简要研究了这一点,已经习惯了 EF6,并且广泛使用了 table-per-type。

1a) 我的经验是,如果您只是将每个实体类型作为 DbSet 添加到您的上下文中,则默认映射会做得很好。如有必要,您可以在 DbContext 的 OnModelBuilding 覆盖中进行配置:

protected override void OnModelCreating(ModelBuilder modelBuilder)
    
        modelBuilder.Entity<Person>()
            .HasDiscriminator<string>("person_type")
            .HasValue<Employee>("employee")
            .HasValue<Customer>("customer");
    

1b) 目前您不能将 TPT 与 EF 核心一起使用。它计划在未来发布,目前在EF core roadmap 上被列为高优先级

2) 如果我们采用严格的 DDD 原则,那么业务层应该尽可能地模拟/镜像现实生活领域,并且不受应用程序数据层的影响。就我个人而言,我认为您在上面列出的继承是现实生活中的一面镜子。但是,EF 核心团队似乎处于“优先组合胜过继承”阵营,这可能会导致领域模型如下:

public class Person

  public int Id  get; set; 
  public string Name  get; set; 


public class User

  public int Id get;set;
  public Person Person get; set;
  public int PersonId get;set;
  public string UserName  get; set; 
  public string Password  get; set; 


public class Employee

  public int Id get; set;
  public User User get; set;
  public int UserId get;set;
  public decimal Salary  get; set; 


public class Customer

  public int Id get;set;
  public User User get; set;
  public int UserId get;set;
  public long DiscountPoints  get; set; 

然后这些实体将存储在单独的表中,它们之间具有前键关系。员工向客户的转变也将涉及创建一个新客户,该客户具有与员工相同的用户属性,例如

public void CreateCustomerFromEmployee(int employeeId) 
  var employee = context.Employees.Where(e => e.Id == employeeId).SingleOrDefault();

  context.Customers.Add(new Customer() 
    
        UserId = employee.UserId,
        DiscountPoints = 0
    );

  context.SaveChanges();

【讨论】:

感谢您的回答 Alex,但我的意图是将子类视为一个独特的人,以便在一个独特的点更改诸如“联系人信息”之类的值。

以上是关于EntityFramework Core 中的映射继承的主要内容,如果未能解决你的问题,请参考以下文章

EF Core中的多对多映射如何实现?

无法使用 OData、EF Core 和 AutoMapper 映射 List<> 导航属性

EntityFramework 7 更名为EntityFramework Core(预发布状态)

使用 EntityFramework.Core 从自引用表加载完整的层次结构

从零开始学 ASP.NET Core 与 EntityFramework Core 目录

EntityFramework 映射片段问题