不同隔离级别的内部TransactionScope,如何实现?

4

目前的 TransactionScope 实现缺乏在嵌套范围中更改隔离级别的能力。

MSDN 指出:当使用嵌套的 TransactionScope 对象时,如果它们想要加入环境事务,则所有嵌套的范围必须配置为使用完全相同的隔离级别。如果嵌套的 TransactionScope 对象尝试加入环境事务,但指定了不同的隔离级别,则会抛出 ArgumentException 异常。

然而,SQL Server 允许我们随时更改隔离级别,为什么 TransactionScope 不允许呢?我不明白。

在 BCL 中是否有关于嵌套 SQL 事务及其隔离级别的标准,禁止这种行为?我的选择是什么?我肯定不能设计类库并推广隔离级别,只是为了能够使用它们。

如果方法 A() 需要一个 快照 级别,并调用方法 B(),该方法需要 读取已提交 级别。 方法 A() 在由我开发的 LibraryA 中,方法 B() 在由虚构公司开发的 LibraryB 中。如何使 A() 调用 B() 而不会出现 ArgumentException
3个回答

3
这更多是理论问题而非特定产品的问题。基本上,原始事务的概念具有所有ACID属性:原子性、一致性、隔离性和持久性。

http://databases.about.com/od/specificproducts/a/acid.htm

现在,我们来考虑一下“隔离级别”的含义:基本上,出于性能或其他原因,我们可能选择放弃数据库对ACID性的某些或全部保证。隔离和原子性是密切相关的,就像彼此的对偶一样。一旦破坏其中一个,另一个就会受到影响。
将事务拆分为可通过特定SQL语句单独表达的部分很常见,但为了使保证起作用,您需要将它们包装在具有足够隔离级别的整个事务中。
现在,如果您的一部分事务需要更高程度的隔离,则整个事务也需要,否则它可能会在敏感部分中断。相反,如果某个部分被赋予较低级别的隔离,则很可能该部分的适当功能需要较少的隔离。 (不要让我想出何时可能是真的这种情况的好例子。)
无论如何,数据库服务器无法判断实际需求是否兼容,因此它完全放弃解决问题。
真正应该发生的是人们开发他们所需的事务。我知道这并不总是容易的,但考虑到您的业务数据模式属于您自己,随意组合SQL组件并不容易。
在处理集中式数据库(如SQL Server)的情况下,最佳实践是设计整个事务,然后在注意到设计中的共性时,可以在编写复杂代码之前将其因素化。如果您确实需要协调不同的数据存储库(或类似存储设备),则需要分布式事务管理器。这是一个独立的产品,甚至比数据库服务器更难正确使用。但对于像自动取款机这样的东西来说是必要的,它们要么给你钱,要么不给你钱,而且这两个必须匹配。祝好运!

谢谢您的回复。但我不明白的是,如果SQL Server允许更改事务的隔离级别,那么为什么框架会禁止它呢?有时候,出于优化目的,您可能确实需要在某些子语句中使用这个功能,而我真的不想自己用SQL语句来更改隔离级别。 - Ivan Zlatanov
3
TransactionScope不仅限于与SQL Server一起使用,它还可以允许在进程/系统之间进行分布式事务。因此,它比SQL Server允许的更严格,可能是为了简化确保系统之间一致性的复杂性而支持分布式事务。 - AaronLS
1
@AaronLS 从来没有这么想过。非常有道理。 - Ivan Zlatanov

0

我曾经遇到过这个问题,我想异常选项可以澄清所有问题,但你不能实现它,只能做类似的事情。事实上,每个连接只允许一个隔离级别,如果你切换隔离级别,那么整个隔离级别会改变,直到嵌套完成。

查看this Stackpost

阅读隔离备注


0

是的。您可以使用不同隔离级别嵌套TransactionScopes

正如您的引用所说:

如果要加入环境事务,则所有嵌套范围必须配置为使用完全相同的隔离级别

因此,如果B需要与环境事务不同的隔离级别,请确保它不加入环境事务,使用TransactionScopeOption.RequiresNew

void A()
{
    var options = new TransactionOptions() 
    {
        IsolationLevel = IsolationLevel.Snapshot 
    }
    using (var transaction = new TransactionScope(TransactionScopeOption.Required, options))
    {
        B();
    }

}

void B()
{
    var options = new TransactionOptions()
    {
        IsolationLevel = IsolationLevel.ReadCommitted,
    };
    // RequiresNew is the important bit
    using (var transaction = new TransactionScope(TransactionScopeOptions.RequiresNew, options))
    {
        // Do stuff
    }

}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接