Подтвердить что ты не робот

EF: Как я могу дважды вызвать SaveChanges внутри транзакции?

Используя Entity Framework (сначала код в моем случае), у меня есть операция, которая требует от меня вызова SaveChanges для обновления одного объекта в БД, а затем SaveChanges снова для обновления другого объекта. (Мне нужно сначала SaveChanges, чтобы решить проблему, когда EF не может определить, какой объект для обновления сначала).

Я пробовал делать:

using (var transaction = new TransactionScope())
{
    // Do something

    db.SaveChanges();

    // Do something else

    db.SaveChanges();

    tramsaction.Complete();
}

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

Теперь я видел сообщения в другом месте, которые описывают, как включить MSDTC, но мне кажется, что мне также необходимо включить доступ к сети и т.д. Это звучит как полный перебор, потому что других баз данных не задействовано, не говоря уже о другие серверы. Я не хочу делать что-то, что сделает мое приложение менее безопасным (или медленнее).

Конечно, должен быть более легкий способ сделать это (в идеале без MSDTC)?

4b9b3361

Ответ 1

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

var objectContext = ((IObjectContextAdapter)db).ObjectContext;

try {
    object.Context.Connection.Open();
    using (var transaction = new TransactionScope()) {
        // Do something

        db.SaveChanges();

        // Do something else

        db.SaveChanges();

        transaction.Complete();
    }
} finally {
    objectContext.Connection.Close();
} 

Ответ 2

Я знаю это как-то вроде позднего ответа, но я нашел полезным поделиться.

Теперь в EF6 это легче понять, используя dbContext.Database.BeginTransaction()

вот так:

using (var context = new BloggingContext())
{
    using (var dbContextTransaction = context.Database.BeginTransaction())
    {
        try
        {
            // do your changes
            context.SaveChanges();

            // do another changes
            context.SaveChanges();

            dbContextTransaction.Commit();
        }
        catch (Exception)
        {
            dbContextTransaction.Rollback();
        }
    }
}

для более подробной информации смотрите this

снова в EF6 Вперед

Ответ 3

Вызывая SaveChanges(), поскольку вы заставляете данные сохраняться в базе данных и EF, чтобы забыть об изменениях, которые она только что сделала.

Фокус в том, чтобы использовать SaveChanges (false), чтобы изменения сохранялись в БД, но EF не забывает об изменениях, которые он делает, делая возможным ведение журнала/повторение.

        var scope = new TransactionScope(
            TransactionScopeOption.RequiresNew,
            new TransactionOptions() { IsolationLevel = IsolationLevel.Serializable }
        );

        using (scope)
        {
            Entities context1 = new Entities();
            // Do Stuff
            context1.SaveChanges(false);

            Entities context2 = new Entities();
            // Do Stuff
            context2.SaveChanges(false);

            scope.Complete();
            context1.AcceptAllChanges();
            context2.AcceptAllChanges();
        }

P.S. Как только у вас откроется более одного соединения внутри транзакции, он будет переходить на DTC.

Ответ 4

Для окончательного ответа, выбранного выше, есть опечатка. Исправленная строка ниже:

objectContext.Connection.Open();

Также необходимо добавить ссылки для System.Data.Entity.Infrastructure и System.Transactions