>底盘
>老虎机
>卡片
当我使用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).
>卡有很多插槽,因为卡可以是多插槽卡(SET NULL).
>插槽必须属于机箱(pubic int ChassisId {get; set;})
>一个插槽没有卡(public int?CardId {get; set;}).
因此,删除机箱时,将删除所有插槽.精细.但是,我还要删除这些插槽中安装的所有卡.不行我正在尝试将一些代码挂钩到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); } } } };
…但是这引发了异常:
An exception of type ‘System.Data.Entity.Infrastructure.dbupdateException’ occurred in EntityFramework.dll but was not handled in user code
Additional information: Unable to insert or update an entity because the principal end of the ‘Chassis_Slots’ relationship is deleted.
然后我尝试添加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随后发出以下例外情况:
Additional information: The DELETE statement conflicted with the REFERENCE constraint “FK_dbo.Slots_dbo.Cards_CardId”. The conflict occurred in database “…”,table “dbo.Slots”,column ‘CardId’.
The statement has been terminated.
…有点让我在岩石和坚硬的地方之间,完全出于想法.
解决方法
>删除机箱后,由于删除级联机制,所有插槽也将被删除.简单吧?
>然后你拦截保存更改,你想在同一个事务单个SaveChanges上手动删除Card,我不确定EF将如何生成SQL查询,即使稍后添加了卡删除语法,但是当我检查它时使用profiler,它首先删除了卡片(应该是机箱首先).
>会发生什么,Unchanged Slot(将被删除级联自动删除)更改为Modified Slot.
>为什么?因为当您同时删除Card时,相应的Slot的CardId必须设置为null.
>现在最终结果是Slot被修改,但机箱已被删除.
要解决此问题,您需要引入新的事务/上下文.
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既可以全部发生,也不会发生任何事情,现在保证原子性.
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。