database 如何使用 Entity Framework CF 在父实体之前删除子实体?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/9620422/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to delete child entities before parent with Entity Framework CF?
提问by quakkels
I am trying to use EF code-first to delete a db record (deleteMe) and it's children (deleteMe.Prices).
我正在尝试使用 EF code-first 来删除 db 记录 ( deleteMe) 及其子记录 ( deleteMe.Prices)。
foreach (var deleteMe in deleteThese)
{
// Delete validation
if(CanDeleteItem(deleteMe.ItemId))
{
db.Entry(deleteMe).State = EntityState.Deleted;
foreach (var item in deleteMe.Prices)
{
db.Entry(item).State = EntityState.Deleted; // cascade delete
}
}
}
db.SaveChanges();
However, Entity Framework seems to be unable to track the fact that the child records should be deleted before the parent. I get the error:
但是,实体框架似乎无法跟踪应该在父记录之前删除子记录的事实。我收到错误:
The DELETE statement conflicted with the REFERENCE constraint "ItemPrice_Item".
The conflict occurred in database "DEVDB", table "dbo.ItemPrices", column 'Item_ItemId'.
The statement has been terminated.
DELETE 语句与 REFERENCE 约束“ItemPrice_Item”冲突。
冲突发生在数据库“DEVDB”、表“dbo.ItemPrices”、“Item_ItemId”列中。
该语句已终止。
How would I execute this delete in EF?
我将如何在 EF 中执行此删除?
回答by quakkels
I ended up finding a quick line that'd do it for me:
我最终找到了一个可以为我做的快速线路:
foreach (var deleteMe in deleteThese)
{
// Delete validation
if(CanDeleteItem(deleteMe.ItemId))
{
///
deleteMe.Prices.ToList().ForEach(p => db.ItemPrices.Remove(p));
///
db.Entry(deleteMe).State = EntityState.Deleted;
}
}
db.SaveChanges();
回答by Eng. Samer T
EF6
EF6
context.Children.RemoveRange(parent.Children)
回答by Ladislav Mrnka
Cascade delete in EF is dependent on cascade delete configured in relation in the database so if you don't have cascade delete configured in the database you must first load all item prices to your application and mark them as deleted.
EF 中的级联删除依赖于在数据库中配置的级联删除,因此如果您没有在数据库中配置级联删除,您必须首先将所有商品价格加载到您的应用程序并将它们标记为已删除。
回答by SventoryMang
Well the most easiest solution would be to iterate through prices first and call save changes, then set the entry to delete for deleteMe and call save changes again, but have you checked out this: Entity framework code first delete with cascade? It seems to be what you want.
那么最简单的解决方案是首先遍历价格并调用保存更改,然后将条目设置为 deleteMe 的删除并再次调用保存更改,但是您是否检查过:实体框架代码首先使用级联删除?这似乎是你想要的。
Curious though also why you just aren't removing the entities from the context to delete but instead setting the entry state?
很好奇为什么你只是不从上下文中删除实体来删除而是设置条目状态?
Another option is to set cascade delete http://blogs.msdn.com/b/alexj/archive/2009/08/19/tip-33-how-cascade-delete-really-works-in-ef.aspx
另一种选择是设置级联删除http://blogs.msdn.com/b/alexj/archive/2009/08/19/tip-33-how-cascade-delete-really-works-in-ef.aspx
Do something like this (not tested but hopefully you get the jist):
做这样的事情(未经测试,但希望你能明白):
using (TransactionScope scope = new TransactionScope())
{
foreach (var deleteMe in deleteThese)
{
// Delete validation
if(CanDeleteItem(deleteMe.ItemId))
{
foreach (var item in deleteMe.Prices)
{
db.Entry(item).State = EntityState.Deleted; // cascade delete
}
db.SaveChanges();
db.Entry(deleteMe).State = EntityState.Deleted;
}
}
db.SaveChanges();
scope.Complete();
}
Additionally you could call:
此外,您可以致电:
db.Prices.Remove(item);
and
和
db.DeleteMes.Remove(deleteMe);
instead of setting the entry state. Not sure if there is a difference behind the scenes between the two though.
而不是设置入口状态。不确定两者在幕后是否存在差异。
回答by marvelTracker
Cascade delete in Entity framework is tricky thing, as you need to be sure about deletion entity object graph.It is better to always write a integration test for these cascade deletes.
实体框架中的级联删除是一件棘手的事情,因为您需要确定删除实体对象图。最好始终为这些级联删除编写集成测试。
If you try to delete parent entity in EF, it will try to execute delete statements for any child entities in current dbcontext. As a result, it will not initialize any child entities which have not been loaded. This will lead to RDBMS runtime error which violate the foreign key constraint. To be in safe side ensure all dependent entities loaded to current dbcontext before deleting.
如果您尝试删除 EF 中的父实体,它将尝试为当前 dbcontext 中的任何子实体执行删除语句。因此,它不会初始化任何尚未加载的子实体。这将导致违反外键约束的 RDBMS 运行时错误。为了安全起见,请确保在删除之前将所有依赖实体加载到当前 dbcontext。
回答by Artie Leech
I had a similar issue and for me, it looked like I hadn't correctly established the relationship between Parent and Child in their respective classes.
我有一个类似的问题,对我来说,看起来我没有在各自的班级中正确建立父子之间的关系。
My fix was to add the attributes specified below to the Child class, for the property that represented its Parent's Id
我的解决方法是将下面指定的属性添加到 Child 类中,用于表示其父 ID 的属性
public class Child
{
[Key, Column(Order = 1)]
public string Id { get; set; }
[Key, ForeignKey("Parent"), Column(Order = 2)] // adding this line fixed things for me
public string ParentId {get; set;}
}
public class Parent
{
[Key, Column(Order = 1)]
public string Id { get; set; }
...
public virtual ICollection<Child> Children{ get; set; }
}
回答by Matt Allen
The following works quite efficiently. For each relational table in your database add the following (At your context file).
以下工作非常有效。对于数据库中的每个关系表,添加以下内容(在您的上下文文件中)。
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder
.Entity<TableA>()
.HasMany(x => x.TableB)
.WithRequired(x => x.TableA)
.WillCascadeOnDelete();
modelBuilder
.Entity<TableC>()
.HasMany(x => x.TableD)
.WithRequired(x => x.TableC)
.WillCascadeOnDelete();
modelBuilder
.Entity<TableE>()
.HasMany(x => x.TableF)
.WithRequired(x => x.TableE)
.WillCascadeOnDelete(); }
Then in your code, don't forget to load these tables, before you delete
然后在您的代码中,在删除之前不要忘记加载这些表
context.TableA.Load();
context.TableB.Load();
context.TableC.Load();
context.TableD.Load();
context.TableE.Load();
context.TableF.Load();
var tableAEntity= TableA.Where(x => x.Condition == [yourcondition].FirstOrDefault();
context.TableA.Remove(tableAEntity);
context.SaveChanges();
This will delete the entity (record) from the main entry table and all the connected table records (related through FK) quite fast and efficiently (Even if the relationship cascades deeply at multiple levels).
这将非常快速有效地从主条目表和所有连接的表记录(通过 FK 相关)中删除实体(记录)(即使关系在多个级别深入级联)。
回答by Matthew Hudson
If your object is self-referencing, you can delete both many-to-many and one-to-many children using the method below. Just remember to call db.SaveChanges() afterwards :)
如果您的对象是自引用的,您可以使用以下方法删除多对多和一对多子项。请记住之后调用 db.SaveChanges() :)
[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public ActionResult DeleteConfirmed(int id)
{
Object obj = this.db.Objects.Find(id);
this.DeleteObjectAndChildren(obj);
this.db.Objects.Remove(obj);
this.db.SaveChanges();
return this.Json(new { success = true });
}
/// <summary>
/// This deletes an object and all children, but does not commit changes to the db.
/// - MH @ 2016/08/15 14:42
/// </summary>
/// <param name="parent">
/// The object.
/// </param>
private void DeleteObjectAndChildren(Object parent)
{
// Deletes One-to-Many Children
if (parent.Things != null && parent.Things.Count > 0)
{
this.db.Things.RemoveRange(parent.Things);
}
// Deletes Self Referenced Children
if (parent.Children != null && parent.Children.Count > 0)
{
foreach (var child in parent.Children)
{
this.DeleteObjectAndChildren(child);
}
this.db.Objects.RemoveRange(parent.Children);
}
}

