Linq:在TransactionScope内删除和插入相同的主键值
本文关键字:键值 插入 TransactionScope 删除 Linq | 更新日期: 2023-09-27 18:08:35
我想在一个事务中用新记录替换DB中的现有记录。使用TransactionScope,我有
using ( var scope = new TransactionScope())
{
db.Tasks.DeleteAllOnSubmit(oldTasks);
db.Tasks.SubmitChanges();
db.Tasks.InsertAllOnSubmit(newTasks);
db.Tasks.SubmitChanges();
scope.Complete();
}
程序抛出
System.InvalidOperationException: Cannot add an entity that already exists.
经过一些尝试和错误之后,我发现罪魁祸首是在删除和插入之间没有任何其他执行指令。如果我在第一个SubmitChanges()和InsertAllOnSubmit()之间插入其他代码,一切都会正常工作。有人能解释一下为什么会这样吗?
我尝试了另一个更新对象:
IEnumerable<Task> tasks = ( ... some long query that involves multi tables )
.AsEnumerable()
.Select( i =>
{
i.Task.Duration += i.LastLegDuration;
return i.Task;
}
db.SubmitChanges();
这个也不起作用。
编辑:此行为似乎与事务无关。最后,我采用了非常低效的Update:
newTasks.ForEach( t =>
{
Task attached = db.Tasks.Single( i => ... use primary id to look up ... );
attached.Duration = ...;
... more updates, Property by Property ...
}
db.SubmitChanges();
您可以选择要更新的Id列表并检查列表是否包含每个项,从而尝试一次更新多个行,而不是插入和删除或进行多个查询。
另外,确保将事务标记为完成,以向事务管理器表明所有资源的状态是一致的,并且可以提交事务。
Dictionary<int,int> taskIdsWithDuration = getIdsOfTasksToUpdate(); //fetch a dictionary keyed on id's from your long query and values storing the corresponding *LastLegDuration*
using (var scope = new TransactionScope(TransactionScopeOption.Required))
{
var tasksToUpdate = db.Tasks.Where(x => taskIdsWithDuration.Keys.Contains(x.id));
foreach (var task in tasksToUpdate)
{
task.duration1 += taskIdsWithDuration[task.id];
}
db.SaveChanges();
scope.Complete();
}
根据您的场景,您可以在表非常大且要更新的项数量相当少的情况下反转搜索,以利用索引。如果是这种情况,您现有的更新查询应该可以正常工作,所以我怀疑您是否需要反转它。
我在LinqToSql中遇到了同样的问题,我不认为这与事务有关,而是与会话/上下文如何合并变化有关。我这样说是因为我通过绕过linqtosql进行删除并使用一些原始sql来解决这个问题。