这是我第一次使用代码第一种方法。我有一个用户表,用户可以是普通用户和卖家两种类型,其中卖家从用户那里继承了一些额外的选项(与其他表的关系)。
我想知道我是否可以做类似的事情:
public class Seller: User
{
public virtual ICollection<WorkingDay> WorkingDays {get; set;}
....
}
然后在ApplicationDbContext中添加:
public DbSet<Seller> Sellers { get; set; }
答案 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(...);
如果您期望大量查询仅使用基本属性,请考虑此策略,因为这只访问一个表。如果您的派生类非常不同,请重新考虑它。