c# – 如何删除级联底部的实体 – > EF中的非级联删除链?
对不起,完全无益的标题……我无法将这个问题变成更好的话.
我有以下实体,每个实体都由Id属性标识: >底盘 当我使用POCO时,这些实体并没有什么突破性的.例如,Chassis类的定义如下: public class Chassis { public int Id { get; set; } // Other properties omitted for brevity. public ICollection<Slot> Slots { get; set; } public Chassis() { Slots = new Collection<Slot>(); } } 关系如下: >机箱有许多插槽(CASCADE). 因此,删除机箱时,将删除所有插槽.精细.但是,我还要删除这些插槽中安装的所有卡.不行我正在尝试将一些代码挂钩到OnSavingChanges事件(我在SaveChanges()之前触发),这样当机箱被标记为已删除时,我也会删除它的卡片. 首先,我试过: OnSavingChanges += (x,y) => { var ctx = x as DbContext; var chassis = ctx.ChangeTracker.Entries<Chassis>().Where(e => e.State == EntityState.Deleted); // Delete all cards on a deleted chassis. foreach (var c in chassis) { // Cannot just do c.Slots,as EF seems to empty the nav. property now the // chassis is deleted. var slots = ctx.Slots.Where(s => s.ChassisId == c.Entity.Id).ToList(); foreach (var s in slots) { if (s.Card != null) { ctx.Cards.Remove(s.Card); } } } }; …但是这引发了异常:
然后我尝试添加ctx.Detach(s);在我的内部foreach循环中,停止EF试图保存已经删除的通过级联Slots实体: foreach (var s in slots) { if (s.Card != null) { ctx.Cards.Remove(s.Card); } // Otherwise EF attempts to save the slot,which results in a exception saying the principle // end of the relatioship Chassis_Slots has already been deleted. ctx.Detach(s); } …然后EF随后发出以下例外情况:
…有点让我在岩石和坚硬的地方之间,完全出于想法. 任何人都可以建议一个更成功的方法/方法来做到这一点? 解决方法
这是序列.
>删除机箱后,由于删除级联机制,所有插槽也将被删除.简单吧? 要解决此问题,您需要引入新的事务/上下文. public override int SaveChanges() { var deletedCardIds = new List<int>(); var chassises = ChangeTracker.Entries<Chassis>().Where(e => e.State == EntityState.Deleted); foreach (var chassis in chassises) { var slots = Slots.Where(s => s.ChassisId == chassis.Entity.Id).ToArray(); foreach (var slot in slots) { if (slot.CardId.HasValue && !deletedCardIds.Contains(slot.CardId.Value)) { deletedCardIds.Add(slot.CardId.Value); } } } // Commits original transaction. var originalRowsAffected = base.SaveChanges(); int additionalRowsAffected = 0; if (deletedCardIds.Count > 0) { // Opens new transaction. using (var newContext = new AppContext()) { foreach (var cardId in deletedCardIds) { var deletedCard = newContext.Cards.Find(cardId); if (deletedCard != null) { newContext.Cards.Remove(deletedCard); } } // Commits new transaction. additionalRowsAffected = newContext.SaveChanges(); } } return originalRowsAffected + additionalRowsAffected; } PS >您有两个独立的事务,可能会导致意外行为(atomicity无法保证). UPDATE 今天我才意识到我们可以简单地使用TransactionScope到commit several operations in a single transaction.就像在数据库中执行此代码一样. begin tran delete from dbo.Chassis delete from dbo.Cards commit tran 如果您使用EF6以上,您可以使用Database.BeginTransaction否则使用TransactionScope. public override int SaveChanges() { var deletedCardIds = new List<int>(); var chassises = ChangeTracker.Entries<Chassis>().Where(e => e.State == EntityState.Deleted); foreach (var chassis in chassises) { var cardIds = Slots.Where(s => s.ChassisId == chassis.Entity.Id) .Where(s => s.CardId.HasValue) .Select(s => s.CardId.Value) .ToArray(); deletedCardIds.AddRange(cardIds); } int originalRowsAffected; int additionalRowsAffected; using (var transaction = new TransactionScope()) { originalRowsAffected = base.SaveChanges(); deletedCardIds.Distinct().ToList() .ForEach(id => Entry(new Card { Id = id }).State = EntityState.Deleted); additionalRowsAffected = base.SaveChanges(); transaction.Complete(); } return originalRowsAffected + additionalRowsAffected; } 通过在上面更新的代码中引入事务,SaveChanges既可以全部发生,也不会发生任何事情,现在保证原子性. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |