【问题标题】:Using TransactionScopeOption.Suppress with Sql Server Compact 4将 TransactionScopeOption.Suppress 与 Sql Server Compact 4 一起使用
【发布时间】:2012-05-30 08:52:12
【问题描述】:

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

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

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

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

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

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

但是,使用下面的代码 sn -p 来改变 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))
    ...

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

似乎避免这种情况的唯一方法是在进入innerScope 块之前调用outerScope.Complete()。但这会破坏目的。

我在这里缺少什么? 谢谢。

【问题讨论】:

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


    【解决方案1】:

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

    【讨论】:

    • 感谢您的回复。但是,例如,如果初始插入到另一个表中,我上面的示例就可以完美地工作。如果我在outerScope 中插入SomeOtherTable,代码将按预期工作。所以问题出在表级锁上。
    • 这些是并行事务 - SQLce 支持,但您的原始测试代码创建了嵌套,正如 Jose 所说,它们不受支持。
    【解决方案2】:

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

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

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

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

    【讨论】:

    • 正如我明确指出的,这不是实际的应用程序逻辑。它是“来自演示问题的单元测试的简化代码 [...]”。问题是表被锁定了,我问如何避免。
    • 问题是表被锁定了,我问如何避免这种情况只有当这两个事务不会互相等待时,你才能避免这种情况。这就是数据库的工作方式。
    • 那么,您是说有 NO 方法可以防止单行插入在 SQL CE 中执行表级锁定?
    • 我是说我不知道​​如何实现它,因为它在概念上是错误的——它破坏了核心数据库行为。您想要实现的是允许在没有锁定的情况下进行并发插入,这可能导致数据丢失或不一致。如果您想确定是否有可能,请创建一个关于 SQL CE 的新问题(跳过与问题完全无关的 .net 和 EF)。此外,如果您找到了必须在直接 SQL 中执行它的方式,而不是在 EF 中。您应该更改您的应用程序逻辑并按顺序执行两个事务。
    • 感谢您的帮助。我将尝试一个更简洁的问题。但是,您的主张——同时插入同一个表必须引入一个表锁来强制一致性——是不正确的。它一直在数据库中发生,绝对不是“概念上的错误”。
    猜你喜欢
    • 2011-10-01
    • 2014-10-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多