4

我在使用带有实体框架的 Sql Server CE 4 和System.Transactions.TransactionScope.

下面的简化代码来自演示问题的单元测试。

这个想法是使innerScope块(没有事务)成功或失败而不影响outerScope块(“环境”事务)。这是TransactionScopeOption.Suppress.

但是,代码失败了,因为似乎整个SomeTable表都被第一次插入锁定了outerScope。在代码中指示的位置,将引发此错误:

“SQL Server Compact 等待锁定超时。设备的默认锁定时间为 2000 毫秒,桌面为 5000 毫秒。可以使用 ssce:默认锁定超时属性在连接字符串中增加默认锁定超时。[会话 id = 2,线程 id = 2248,进程 id = 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);
}

因此,根据http://msdn.microsoft.com/en-us/library/ms172001,似乎“事务范围内的事务在隔离级别设置为 Serializable 的情况下执行”

但是,使用以下代码片段来更改 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))
    ...

在相同的位置抛出相同的异常。

It seems the only way to avoid this is to call outerScope.Complete() before entering the innerScope block. But this would defeat the purpose.

What am I missing here? Thanks.

4

2 回答 2

1

AFAIK SQL Server Compact 不支持嵌套事务。

于 2012-05-30T09:30:36.503 回答
0

你为什么要这样做?如果我查看您的代码,则在第一个事务范围内运行第二个事务范围和按顺序运行它们之间没有区别。

恕我直言,这不是 SQL CompactTransactionScope或隔离级别的问题。这是您错误的应用程序逻辑的问题。

每个都SaveChanges在事务中运行 - 由定义的外部事务TransactionScope或内部事务DbTransaction。即使它不会创建事务,每个数据库命令都有自己的隐式事务。如果您在内部代码块上使用 Suppress,您将创建两个并发事务,它们试图插入同一个表,而且第一个事务在没有完成第二个的情况下无法完成,第二个在没有完成第一个 => 死锁的情况下无法完成。

原因是插入命令总是锁定表的一部分,在提交或回滚之前不允许新的插入。我不确定是否可以通过更改事务隔离级别来避免这种情况 - 如果可以,您很可能需要Read.Uncommitted.

于 2012-05-30T09:43:00.140 回答