使用TransactionScopeOption.Suppress与Sql Server Compact 4

时间:2012-05-30 08:52:13

标签: c# .net entity-framework sql-server-ce system.transactions

我在使用带有实体框架和System.Transactions.TransactionScope的Sql Server CE 4时无法抑制部分事务。

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

我们的想法是在不影响innerScope块("环境"事务)的情况下,使outerScope块(没有事务)成功或失败。这是TransactionScopeOption.Suppress的明确目的。

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

" SQL Server Compact超时等待锁定。设备的默认锁定时间为2000毫秒,桌面的默认锁定时间为5000毫秒。可以使用ssce:default lock timeout属性在连接字符串中增加默认锁定超时。 [Session id = 2,Thread id = 2248,Process id = 13516,Table name = SomeTable,Conflict type = x lock(x blocks),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);
}

因此,似乎"事务范围中的事务执行时隔离级别设置为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 个答案:

答案 0 :(得分:1)

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

答案 1 :(得分:0)

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

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

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

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