当使用客户端框架,ORM或类似工具构建查询时,如何为单个事务实现不同的隔离级别是最佳方法(这些工具不支持像WITH(NOLOCK)这样的查询提示)?
假设一个应用程序对于一些复杂而耗时的查询使用了ReadUncommitted级别(对相关风险有很好的认识),并且它应该与NHibernate及其查询条件一起运行(或QueryOver / LINQ,只需不使用字符串拼接!)。
NHibernate不支持with(nolock)提示(除非使用原生SQL,在许多情况下目前正在使用)。
因此,为了替换原生SQL字符串及其繁琐的构建代码,我想使用IsolationLevel.ReadUncommitted的事务来替换“with(nolock)”。
但是,即使在提交/回滚后连接仍保持在更改的隔离级别中,以新级别运行。即使在connection.Close()之后,它返回到连接池并重用更改的隔离级别。
我最初注意到这一点,是因为我测试了使用快照隔离级别打开连接并发送简单查询,如果启用数据库快照模式,则禁用Read Uncommitted(通常没有简单地切换到快照)。测试数据库未启用快照模式,因此我收到了异常,并在catch块中将我的UseReadUncommitted变量设置为'true',但稍后从“新的”/重用的连接查询仍然收到相同的异常。
我编写了一个简单的类来包装事务处理,在using块中自动重置.IsolationLevel。Dispose()但是,这似乎会导致两个额外的往返到DB,并且我不确定更改的隔离级别是否可能在某些情况下“幸存”,并影响其他查询。代码在第一次尝试时工作,它适用于纯ADO.NET连接/事务(如果好的话,我将为NHibernate会话做另一个)。
有什么建议吗?
假设一个应用程序对于一些复杂而耗时的查询使用了ReadUncommitted级别(对相关风险有很好的认识),并且它应该与NHibernate及其查询条件一起运行(或QueryOver / LINQ,只需不使用字符串拼接!)。
NHibernate不支持with(nolock)提示(除非使用原生SQL,在许多情况下目前正在使用)。
因此,为了替换原生SQL字符串及其繁琐的构建代码,我想使用IsolationLevel.ReadUncommitted的事务来替换“with(nolock)”。
但是,即使在提交/回滚后连接仍保持在更改的隔离级别中,以新级别运行。即使在connection.Close()之后,它返回到连接池并重用更改的隔离级别。
我最初注意到这一点,是因为我测试了使用快照隔离级别打开连接并发送简单查询,如果启用数据库快照模式,则禁用Read Uncommitted(通常没有简单地切换到快照)。测试数据库未启用快照模式,因此我收到了异常,并在catch块中将我的UseReadUncommitted变量设置为'true',但稍后从“新的”/重用的连接查询仍然收到相同的异常。
我编写了一个简单的类来包装事务处理,在using块中自动重置.IsolationLevel。Dispose()但是,这似乎会导致两个额外的往返到DB,并且我不确定更改的隔离级别是否可能在某些情况下“幸存”,并影响其他查询。代码在第一次尝试时工作,它适用于纯ADO.NET连接/事务(如果好的话,我将为NHibernate会话做另一个)。
有什么建议吗?
public class TransactionContainerTempIsolationLevel : IDisposable
{
public IsolationLevel OldIsolationLevel { get; private set; }
public IsolationLevel TempIsolationLevel { get; private set; }
public IDbTransaction Transaction { get; private set; }
private readonly IDbConnection _conn;
public TransactionContainerTempIsolationLevel(IDbConnection connection, IsolationLevel tempIsolationLevel)
{
_conn = connection;
LocalIsolationLevel = localIsolationLevel;
var checkTran = _conn.BeginTransaction();
if (checkTran.IsolationLevel == tempIsolationLevel)
{
Transaction = checkTran;
}
else
{
OldIsolationLevel = checkTran.IsolationLevel;
checkTran.Dispose();
Transaction = _conn.BeginTransaction(tempIsolationLevel);
}
}
public void Dispose()
{
Transaction.Dispose();
if (OldIsolationLevel != TempIsolationLevel)
{
using (var restoreTran = _conn.BeginTransaction(OldIsolationLevel))
{
restoreTran.Commit();
}
}
}
}