Использование TransactionScopeOption.Suppress с Sql Server Compact 4

У меня проблемы с подавлением части транзакции с использованием Sql Server CE 4 с Entity Framework и System.Transactions.TransactionScope,

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

Идея состоит в том, чтобы включить innerScope блок (без транзакции), чтобы преуспеть или потерпеть неудачу, не затрагивая outerScope блок ("окружающая" транзакция). Это заявленная цель TransactionScopeOption.Suppress,

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

"Время ожидания SQL Server Compact истекло в ожидании блокировки. Время блокировки по умолчанию составляет 2000 мс для устройств и 5000 мс для настольных компьютеров. Время ожидания блокировки по умолчанию можно увеличить в строке подключения с помощью свойства тайм-аута блокировки ssce: default. [ Session id = 2, Идентификатор потока = 2248, Идентификатор процесса = 13516, Имя таблицы = SomeTable, Тип конфликта = x блокировка (x блоков), Ресурс = PAG (idx): 1046 ]"

[TestMethod()]
[DeploymentItem("MyLocalDb.sdf")]
public void MyLocalDb_TransactionSuppressed()
{
    int count = 0;

    // This is the ambient transaction
    using (TransactionScope outerScope = new TransactionScope(TransactionScopeOption.Required))
    {
        using (MyObjectContext outerContext = new MyObjectContext())
        {
            // Do something in the outer scope
            outerContext.Connection.Open();
            outerContext.AddToSomeTable(CreateSomeTableRow());
            outerContext.SaveChanges();
            try
            {
                // Ambient transaction is suppressed for the inner scope of SQLCE operations
                using (TransactionScope innerScope = new TransactionScope(TransactionScopeOption.Suppress))
                {
                    using (MyObjectContext innerContext = new MyObjectContext())
                    {
                        innerContext.Connection.Open();
                        // This insert will work
                        innerContext.AddToSomeTable(CreateSomeTableRow());
                        innerContext.SaveChanges(); // ====> EXCEPTION THROWN HERE
                        // There will be other, possibly failing operations here
                    }
                    innerScope.Complete();
                }
            }
            catch { }
        }
        outerScope.Complete();
    }

    count = GetCountFromSomeTable();
    // The insert in the outer scope should succeed, and the one from the inner scope
    Assert.AreEqual(2, count);
}

Таким образом, кажется, что "транзакция в области транзакции выполняется с уровнем изоляции, установленным в Serializable", в соответствии с http://msdn.microsoft.com/en-us/library/ms172001

Однако использование следующего фрагмента кода для изменения уровня изоляции TransactionScope не помогает:

public void MyLocalDb_TransactionSuppressed()
{
    TransactionOptions opts = new TransactionOptions();
    opts.IsolationLevel = IsolationLevel.ReadCommitted;
    int count = 0;

    // This is the ambient transaction
    using (TransactionScope outerScope = new TransactionScope(TransactionScopeOption.Required, opts))
    ...

Такое же исключение выдается в том же месте.

Кажется, единственный способ избежать этого - позвонить outerScope.Complete() перед входом в innerScope блок. Но это победит цель.

Что мне здесь не хватает? Благодарю.

2 ответа

AFAIK SQL Server Compact не поддерживает вложенные транзакции.

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

ИМХО это не проблема SQL Compact, TransactionScope или уровень изоляции. Это проблема вашей неправильной логики приложения.

каждый SaveChanges выполняется в транзакции - либо внешняя транзакция определяется TransactionScope или внутренний DbTransaction, Даже если она не создаст транзакцию, каждая команда базы данных имеет свою собственную неявную транзакцию. Если вы используете Suppress для внутреннего блока кода, вы создаете две параллельные транзакции, которые пытаются вставить в одну и ту же таблицу, и, кроме того, первая не может завершиться без завершения второй, а вторая не может завершиться без завершения первого тупика =>.

Причина в том, что команда вставки всегда блокирует часть таблицы, не разрешая новые вставки, пока она не будет зафиксирована или откатана. Я не уверен, что этого можно избежать, изменив уровень изоляции транзакций - если это произойдет, вам, скорее всего, понадобится Read.Uncommitted,

Другие вопросы по тегам