Sim*_*sen 10 ef-code-first entity-framework-4.1
我正在使用EF4.1代码优先和TPT(每类型表)继承.我有这样的结构
public class Customer
{
public virtual ICollection<Product> Products {get; set;}
}
public class Product
{
[Required]
public int Id { get; set; }
[Required]
public virtual Customer {get; set;}
public decimal Price { get; set; }
}
public class SpecializedProduct : Product
{
public string SpecialAttribute { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
当我删除客户时,我希望删除与该客户关联的所有产品.我可以在Customer和Product之间指定一个WillCascadeOnDelete(true):
modelBuilder.Entity<Customer>().HasMany(e => e.Products).WithRequired(p => p.Customer).WillCascadeOnDelete(true);
Run Code Online (Sandbox Code Playgroud)
但是,由于在我尝试删除客户时,SpecializedProduct和Product之间存在异常关键关系,因此会出现异常:
DELETE语句与REFERENCE约束"SpecializedProduct _TypeConstraint_From_Product_To_SpecializedProduct"冲突.冲突发生在数据库"Test",表"dbo.SpecializedProduct",列"Id"中.该语句已终止.
如果我在SpecializedProduct _TypeConstraint_From_Product_To_SpecializedProduct约束上手动设置一个删除级联,它可以工作,但我希望能够使用模型构建器或代码中的其他方式指定它.这可能吗?
提前致谢!
最好的祝福
西蒙
Mor*_*avi 10
说到数据库,TPT继承是通过基类(例如Product)和所有派生类(例如SpecializedProduct)之间的共享主键关联实现的.现在,当您删除Customer对象而不提取其Products属性时,EF不知道此Customer有一堆产品也需要根据您的要求删除.如果通过根据需要标记客户 - 产品关联来启用级联删除,则数据库将负责从产品表中删除子记录,但如果此子记录是SpecializedProduct,那么SpecializedProduct上的相关行将不会被删除,因此你得到的例外.所以基本上以下代码不起作用:
// This works only if customer's products are not SpecializedProduct
Customer customer = context.Customers.Single(c => c.CustomerId == 1);
context.Customers.Remove(customer);
context.SaveChanges();
Run Code Online (Sandbox Code Playgroud)
此代码将导致EF将以下SQL提交到数据库:
exec sp_executesql N'delete [dbo].[Customer] where ([CustomerId] = @0)',N'@0 int',@0=1
Run Code Online (Sandbox Code Playgroud)
也就是说,没有办法在Product和SpecializedProduct表之间启用级联删除,这就是EF Code First如何实现TPT继承而你无法覆盖它.
那么解决方案是什么?
一种方法是您已经想到的,手动切换Product和SpecializedProduct表之间的级联,以避免在使用SpecializedProducts删除客户时出现异常.
第二种方法是让EF在您移除客户时处理客户的SpecializedProducts.就像我之前说过的那样,这是因为客户对象没有被正确获取,并且EF不了解客户的SpecializedProducts,这意味着通过正确获取客户对象,Ef将开始跟踪客户的关联并提交必要的SQL语句以确保在删除客户之前删除每个相关记录:
Customer customer = context.Customers
.Include(c => c.Products)
.Single(c => c.CustomerId == 1);
context.Customers.Remove(customer);
context.SaveChanges();
Run Code Online (Sandbox Code Playgroud)
因此,EF将向数据库提交以下SQL语句,以便按顺序完全删除所有内容:
exec sp_executesql N'delete [dbo].[SpecializedProduct] where ([Id] = @0)',N'@0 int',@0=1
exec sp_executesql N'delete [dbo].[Product] where (([Id] = @0) and ([Customer_CustomerId] = @1))',N'@0 int,@1 int',@0=1,@1=1
exec sp_executesql N'delete [dbo].[Customer] where ([CustomerId] = @0)',N'@0 int',@0=1
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1626 次 |
| 最近记录: |