4

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

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

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

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

«SQL Server Compact, ожидающий блокировки. Время блокировки по умолчанию составляет 2000 мс для устройств и 5000 мс для настольных компьютеров. Тайм-аут блокировки по умолчанию может быть увеличен в строка сеанса с использованием свойства тайм-аута блокировки ssce: default. [Идентификатор сеанса = 2, Идентификатор потока = 2248, Идентификатор процесса = 13516, Имя таблицы = SomeTable, Тип конфликта = блокировка x (x блоков), Resource = 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); 
} 

Таким образом, представляется, что„сделка в области транзакции выполняется с уровнем изоляции, установленным в Сериализуемый“, в соответствии с 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. Но это победит цель.

Что мне здесь не хватает? Спасибо.

ответ

1

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

+0

Спасибо за ответ.Но мой пример выше работает отлично, если, например, исходная вставка находится в другой таблице. Если я вставляю в 'SomeOtherTable' в' outerScope', код работает так, как ожидалось. Таким образом, проблема связана с блокировкой на уровне таблицы. – user1425515

+1

Это параллельные транзакции, которые поддерживаются SQLce, но ваш исходный тестовый код создает вложенные файлы, и, как сказал Хосе, они не поддерживаются. – Vanja

0

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

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

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

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

+0

Как явственно заявил, это не настоящая прикладная логика. Это «упрощенный код [...] из единичного теста, демонстрирующего проблему». Проблема в том, что таблица заблокирована, и я спросил, как этого избежать. – user1425515

+1

* Проблема в том, что таблица заблокирована, и я спросил, как этого избежать. * Вы избежите этого, только если эти две транзакции не будут ждать друг друга. Это просто способ работы базы данных. –

+0

Итак, вы говорите, что существует способ * НЕТ *, чтобы предотвратить однострочную вставку от выполнения блокировки на уровне таблицы в SQL CE? – user1425515

Смежные вопросы