EF Core Db-first模式下不使用级联删除如何删除父子关联记录
EF Core DbFirst模式下多关联表批量删除外键冲突解决方案
采用Db-first开发模式。
我对EF并不陌生,但整理了这个示例,因为我不确定删除操作中如何处理复杂的父子外键(FK)关联场景。我当前实际开发的代码涉及10~12张存在外键关联的互相关联表,需要批量删除其中一批数据,因此这个示例很有代表性。
删除记录时,执行顺序是否重要?在TSQL中确实存在顺序要求(我原本认为需要先删除父记录再删除子记录)。
如下示例代码运行后我得到了下方报错,显然是因为Book表存在指向Warehouse表的外键:
注意:
{"DELETE语句与REFERENCE约束"FK_Book_Warehouse"冲突。该冲突发生于数据库"AdHoc",表"dbo.Book",列"WarehouseKey"。
语句已终止。"}
其他说明
- 如果每个关联实体都需要基于独立的查询获取,级联删除无法生效。
- 需要使用join查询,因为要删除的记录超过10万条,使用where/contains的写法会过于混乱。
- 更希望所有操作在一次SaveChanges中完成,以便使用TransactionScope将所有操作包裹在同一个事务中。
- 我采用Db-first工作流,使用EF Core POCO generator反向工程工具生成模型,因此不建议手动修改生成的模型类,因为表结构变更重新生成模型时手动修改的内容会丢失。
报错示例代码
public bool DeleteComplex() { bool retval = false; string bookfilter = "Big Fish"; // 根据书名获取要删除的作者列表 List<Author> AuthorList = (from a in ctx.Book join b in ctx.Author on a.AuthorKey equals b.Id where a.Title.Contains(bookfilter) select b).ToList(); ctx.Author.RemoveRange(AuthorList); // 根据书名获取要删除的仓库列表 List<Warehouse> WarehouseList = (from a in ctx.Book join b in ctx.Warehouse on a.WarehouseKey equals b.Id where a.Title.Contains(bookfilter) select b).ToList(); ctx.Warehouse.RemoveRange(WarehouseList); // 根据书名删除书籍 List<Book> BookList = (from a in ctx.Book where a.Title.Contains(bookfilter) select a).ToList(); ctx.Book.RemoveRange(BookList); int results = ctx.SaveChanges(); if(results > 0) { retval = true; } return retval; }
[更新:可正常运行的最终代码]
调整后的差异在于:先删除子记录(或查询得到的子记录集合)后调用SaveChanges持久化,再删除依赖的父集合后调用SaveChanges。同时将所有操作包裹在事务中,可以实现全部成功或全部回滚的原子性。
public bool DeleteComplex() { bool retval = false; string bookfilter = "Big Fish"; // 根据书名获取要删除的书籍列表 List<Book> BookList = (from a in ctx.Book where a.Title.Contains(bookfilter) select a).ToList(); // 根据书名获取要删除的作者列表 List<Author> AuthorList = (from a in ctx.Book join b in ctx.Author on a.AuthorKey equals b.Id where a.Title.Contains(bookfilter) select b).ToList(); // 根据书名获取要删除的仓库列表 List<Warehouse> WarehouseList = (from a in ctx.Book join b in ctx.Warehouse on a.WarehouseKey equals b.Id where a.Title.Contains(bookfilter) select b).ToList(); // 先删除子表记录并持久化 ctx.Book.RemoveRange(BookList); int results = ctx.SaveChanges(); if (results > 0) { retval = true; } // 再删除父表依赖记录 ctx.Author.RemoveRange(AuthorList); ctx.Warehouse.RemoveRange(WarehouseList); results = ctx.SaveChanges(); if (results > 0) { retval = true; } else { retval = false; } return retval; } // 事务包裹调用示例 try { // 测试代码 //var results = service.GetDoTest(); //Console.WriteLine(results); using (TransactionScope transaction = new TransactionScope()) { var results2 = service.DeleteComplex(); Console.WriteLine(results2); transaction.Complete(); } } catch(Exception ex) { logger.Error($"Ran into error: {ex.ToString()}"); }
内容的提问来源于stack exchange,提问作者bitshift
相关产品推荐
相关产品推荐

