我周末花了很多时间试图弄明白这一点。
我有一个包含Subscription表的现有数据库,我想使用TPH继承策略映射到我域中的实体。
订阅有几种类型,每种类型都有自己的特点,因此需要继承。这是域名的简化版本:
public class Subscription
{
public int CustomerID { get; set; }
public virtual Customer Customer { get; set; }
public int SubscriptionID { get; set; }
public string Name { get; set; }
}
public class BookSubscription:Subscription
{
public string Book { get; set; }
}
public class TVSubscription : Subscription
{
public string Channel { get; set; }
}
这些实体在上下文中映射如下:
public DbSet<Customer> Customers { get; set; }
public DbSet<Subscription> Subscriptions { get; set; }
public DbSet<TVSubscription> TVSubscriptions { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Customer>().ToTable("tblCustomer");
modelBuilder.Entity<Customer>().HasKey(t => t.CustomerID);
modelBuilder.Entity<Subscription>()
.Map<Subscription>(m =>
{
m.ToTable("tblSubscription");
m.Requires("ServiceTypeId").HasValue(1);
})
.Map<TVSubscription>(m =>
{
m.Requires("ServiceTypeId").HasValue(20);
})
.Map<BookSubscription>(m =>
{
m.Requires("ServiceTypeId").HasValue(5);
}
);
modelBuilder.Entity<Subscription>().HasKey(s => s.SubscriptionID);
base.OnModelCreating(modelBuilder);
}
正如所料,当查询任何派生类的上下文时,EF使用ServiceTypeId作为鉴别器正确构造查询。当我尝试在Customer实体中包含派生类时出现问题。这有效:
public class Customer
{
public int CustomerID { get; set; }
public string Name { get; set; }
public virtual ICollection<Subscription> Subscriptions{ get; set; }
}
这不起作用:
public class Customer
{
public int CustomerID { get; set; }
public string Name { get; set; }
public virtual ICollection<Subscription> Subscriptions{ get; set; }
public virtual ICollection<TVSubscription> TvSubscriptions { get; set; }
}
这两个都没有:
public class Customer
{
public int CustomerID { get; set; }
public string Name { get; set; }
public virtual ICollection<TVSubscription> TvSubscriptions { get; set; }
}
当我检查分析器时,我可以看到,在这两种情况下,EF都会添加_id后缀,并尝试查询除customerID之外的不存在的customer_id列。我试图为派生类型显式映射外键,但EF当时抱怨:
外键组件&#39; CustomerID&#39;不属于'TVSubscription&#39;类型的声明属性。验证它是否未从模型中明确排除,并且它是有效的原始属性。
CustomerID在派生类中没有位置,但即使我尝试在那里移动它也不起作用(我怀疑因为EF然后尝试使用另一种继承策略进行映射)。
现在,我知道我可以使用这样的东西:
public IEnumerable<TVSubscription> TvSubscriptions
{
get { return Subscriptions.OfType<TVSubscription>(); }
}
但是,使用此方法,EF会查询db以获取整个订阅集,然后在本地过滤它以仅返回TVSubscriptions。这肯定不理想,因为客户可以拥有数百个订阅,并且我希望EF只能查询并返回特定类型的订阅 - 就像我在使用dbContext时已经做的那样。
所以问题是,如何在Customer类中包含TVSubscriptions列表并且只对那些人进行EF查询?
如果无法做到这一点,有人可以解释当我向Customer实体添加派生类集合时EF正在做出什么样的假设。