Linq: Удалить и вставить те же значения первичного ключа в TransactionScope - PullRequest
15 голосов
/ 15 сентября 2011

Я хочу заменить существующие записи в БД новыми записями в одной транзакции. Используя 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();

Это тоже не сработало. БД не внесла никаких изменений в Задачи.

EDIT:

Такое поведение, похоже, не имеет ничего общего с транзакциями. В конце я принял крайне неэффективное обновление:

newTasks.ForEach( t =>
{
     Task attached = db.Tasks.Single( i => ... use primary id to look up ... );
     attached.Duration = ...;
     ... more updates, Property by Property ...
}
db.SubmitChanges();

Ответы [ 2 ]

1 голос
/ 27 сентября 2011

Вместо вставки и удаления или создания нескольких запросов вы можете попытаться обновить несколько строк за один проход, выбрав список идентификаторов для обновления и проверив, содержит ли список каждый элемент.

Кроме того, убедитесь, что вы отметили транзакцию как завершенную, чтобы указать диспетчеру транзакций, что состояние всех ресурсов является согласованным, и транзакция может быть зафиксирована.

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

В зависимости от вашего сценария вы можете инвертировать поиск в случае, если ваша таблица чрезвычайно велика, а количество элементов для обновления достаточно мало, чтобы использовать индексирование.В этом случае ваш существующий запрос на обновление должен работать нормально, поэтому я сомневаюсь, что вам нужно будет его инвертировать.

0 голосов
/ 21 октября 2011

У меня была та же проблема в LinqToSql, и я не думаю, что это связано с транзакцией, но с тем, как меняется сессия / контекст.Я говорю это, потому что я исправил проблему, обходя linqtosql для удаления и используя для этого какой-то необработанный sql.Ужасно я знаю, но это сработало, и все в рамках транзакции.

...