在EF CodeFirst中生成继承的DBS(与视图一样)

时间:2016-12-23 08:07:22

标签: c# entity-framework ef-code-first

这是我第一次使用代码第一种方法。我有一个用户表,用户可以是普通用户和卖家两种类型,其中卖家从用户那里继承了一些额外的选项(与其他表的关系)。

我想知道我是否可以做类似的事情:

public class Seller: User
{
    public virtual ICollection<WorkingDay> WorkingDays {get; set;}
    ....
} 

然后在ApplicationDbContext中添加:

public DbSet<Seller> Sellers { get; set; }

1 个答案:

答案 0 :(得分:1)

是的,您可以在Entity Framework中使用继承。但是,数据库通常不支持继承。在数据库中,继承被模拟。有几种策略用于模拟继承。

假设您有三个类:

public class MyBaseItem {...}
public class MyDerived1Item : MyBaseItem {...}
public class MyDerived2Item : MyBaseItem {...}

每个具体类别的策略表(TPC) 如果我不需要创建MyBaseItem对象,只需要MyDerived1Items或MyDerived2Items,我最经常使用这个。为了防止意外创建MyBaseItem对象,我声明了MyBaseItem abstract。

数据库将有两个表:一个是MyDerived1Items,另一个是MyDerived2Itemss。两个表都将包含所有MyBaseItem属性的列。

Fluent API用于通知数据库构建器使用此策略。

public MyDbContext : DbContext
{
    public DbSet<MyDerived1Item> MyDerived1Items {get; set;}
    public DbSet<MyDerived2Item> MyDerived2Items {get; set;}

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<MyDerived1Item>().Map(m =>
        {
            m.MapInheritedProperties();
            m.ToTable("MyDerived1Items");
        });

        modelBuilder.Entity<MyDerived2Item>().Map(m =>
        {
            m.MapInheritedProperties();
            m.ToTable("MyDerived2Items");
        });  
    }
}  

添加/更改/删除数据库中的项目将始终意味着访问一个表,除非您要对所有派生项目的MyBaseItems属性执行某些操作。在这种情况下,您必须将每个Derived表的元素转换为MyBaseItem并连接所有派生表,如下所示:

IQueryable<MyBaseItem> someBaseItems =
    dbContext.MyDerived1Items.Where(...).Cast<MyBaseItem>()
    .Concat(dbContext.MyDerived2Items.Where(...).Cast<MyBaseItem>();

以后添加MyDerived3Items表很容易。 MyDerived1Items和MyDerived2Items表格不得不改变。

但是,向MyBaseItem添加一个属性意味着向所有派生类表添加一个字段。

每种类型的策略表(TPT)

创建三个表。每种类型一张桌子。带有派生项的表具有包含基本项的表的外键。

如果您创建MyDerived1Item,则该对象将放在两个表中。基类的MyBaseItem属性将位于MyBaseItems表中,MyDerived1Item属性将位于MyDerived1Items表中,以及MyBaseItem中相应项的外键。

我不喜欢这种方法,因为它看起来非常像一对多的关系。在数据库中有可能有一个基本项,其中两个派生项具有相同的外键,这不是您的意图。我不确定你是否可以阻止将这样的对象添加到数据库中。

添加/搜索/更改/删除项目总是意味着使用两个表格,这会使这些操作变慢。

但是,此方法的优点是您可以创建MyBaseItems。因此,如果您需要这样做,请考虑这种策略。

How to implement Table Per Type (TPT)

每个层次结构表(TPH)

此策略是实体框架的默认策略。

数据库将有一个表,其中包含MyBaseItem,MyDerived1Item和MyDerived2Item的所有属性。未使用的列设置为NULL。如果MyDerived1Items与MyDerived2Items几乎相同,那么就不会有多余的列。但是,如果这两个类非常不同,那么将会有很多列设置为null。

因为你将有一个表,你将有一个DbSet:

public class MyDbContext: DbContext
{
    public DbSet<MyBaseItem> MyItems { get; set; }
}

因为这是默认策略,所以不需要流畅的API。

如果您需要仅使用基本属性的查询:

IQueryable<MyBaseItem> baseItems = dbContext.MyItems
    .Where(...);

如果您只需要MyDerived1Items:

IQueryable<MyDerived1Item> myItems = dbContext.MyItems
    .OfType<MyDerived1Items>()
    .Where(...);

如果您期望大量查询仅使用基本属性,请考虑此策略,因为这只访问一个表。如果您的派生类非常不同,请重新考虑它。