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();

Linq:在TransactionScope内删除和插入相同的主键值

您可以选择要更新的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来解决这个问题。