使用 IsolationLevel.ReadUncommitted 时超时 SqlException
Timeout SqlException when using IsolationLevel.ReadUncommitted
我正在尝试编写一个集成测试来测试我的 FluentNHibernate 映射配置。测试的基本思路是插入一些数据,然后读回,并确保插入的数据与读回的数据匹配。我不想在每次测试 运行 时都将测试数据添加到数据库中,所以我尝试使用 t运行saction 插入数据,而 t运行saction 仍然打开,我尝试读回未提交的数据(使用单独的会话)。计划是,一旦做出断言,我将回滚围绕数据插入的 t运行saction,使我的数据库保持与测试 运行 之前相同的状态。不幸的是,我在尝试读取数据时遇到以下错误:
NHibernate.Exceptions.GenericADOException : could not load an entity: [CQRSTutorial.DAL.EventDescriptor#51ff4d15-ddbc-4157-8652-a7a10177a6e3][SQL: SELECT eventdescr0_.Id as Id1_2_0_, eventdescr0_.EventType as EventT2_2_0_, eventdescr0_.Data as Data3_2_0_ FROM Events eventdescr0_ WHERE eventdescr0_.Id=?] ----> System.Data.SqlClient.SqlException : Execution Timeout Expired. The timeout period elapsed prior to completion of the operation or the server is not responding.
我专门创建了一个测试来突出这个问题,如下所示:
[TestFixture, Category(TestConstants.Integration)]
public class TimeoutExpiredTest
{
private ISession _writeSession;
public static ISessionFactory CreateSessionFactory(IsolationLevel isolationLevel = IsolationLevel.Unspecified)
{
var msSqlConfiguration = MsSqlConfiguration
.MsSql2012
.IsolationLevel(isolationLevel)
.ConnectionString(x => x.FromConnectionStringWithKey("CQRSTutorial"));
var cfg = new CustomAutomappingConfiguration();
return Fluently
.Configure()
.Database(msSqlConfiguration)
.Mappings(m =>
{
m.AutoMappings.Add(
AutoMap.AssemblyOf<EventDescriptor>(cfg)
.UseOverridesFromAssemblyOf<EventDescriptorMapping>());
})
.BuildSessionFactory();
}
[SetUp]
public void SetUp()
{
_writeSession = CreateSessionFactory().OpenSession();
_writeSession.BeginTransaction();
}
[Test]
public void InsertAndReadTabOpened()
{
var objectToStoreInDbTemporarily = GetObjectToStoreInDbTemporarily();
_writeSession.SaveOrUpdate(objectToStoreInDbTemporarily);
_writeSession.Flush(); // the data needs to go to the database (even if only temporarily), rather than just staying in an NHibernate cache.
var readSession = CreateSessionFactory(IsolationLevel.ReadUncommitted).OpenSession();
var retrievedEventDescriptor = readSession.Get<EventDescriptor>(objectToStoreInDbTemporarily.Id); // this line throws the exception after 30 seconds.
// If previous line worked, I could then assert inserted values match retrieved values at this point.
}
[TearDown]
public void TearDown()
{
_writeSession.Transaction?.Rollback();
}
public class CustomAutomappingConfiguration : DefaultAutomappingConfiguration
{
public override bool ShouldMap(Type type)
{
return type.Name == typeof(EventDescriptor).Name; // we're only mapping this class for now.
}
}
为简洁起见,我省略了创建对象的代码。最初的测试达到了它的目的——它迫使我必须为有问题的对象正确定义 NHibernate 映射。但是最初的测试是在每个测试 运行 中插入新行,这感觉很脏而且没有必要 - 因此这种 t运行saction / rollback 方法。
查看 SQL Profiler,问题似乎出在读取端 - 读取会话使用 IsolationLevel.ReadCommitted 而不是 IsolationLevel.ReadUncommitted。知道为什么读取会话不使用测试中指定的 IsolationLevel 吗?
啊,我刚刚又看了一眼,我想知道是不是因为readSession.Get()
没有明确的交易。因此,NH 正在使用具有读提交隔离级别的隐式事务吗?也许将 readSession.Get()
包装在具有读取未提交隔离级别的显式事务中?
正如 David Osborne 所说,您也需要为阅读会话打开一个事务。
但是你所做的事情值得商榷。您依赖 SQL 服务器的内部实现细节来使您的测试成功。不能保证它目前有效。
更好地实施适当的测试设置和拆卸,进行必要的清理。您的测试将更加可靠。
另一种解决方案是共享两个会话之间的连接,方法是在打开会话时提供它,或者通过从另一个会话生成一个会话。这样他们的实体缓存将不会被共享,但他们将能够在不提交数据的情况下读取彼此的数据,因为他们将在同一个事务中。
另一种解决方案是使用单个事务范围进行测试,删除 NHibernate 事务。两个会话将在同一个事务中,并且能够读取彼此的数据。但这可能会促进事务分布式,然后您的设置将需要支持它。 (需要在测试主机上启用 MSDTC。)
我正在尝试编写一个集成测试来测试我的 FluentNHibernate 映射配置。测试的基本思路是插入一些数据,然后读回,并确保插入的数据与读回的数据匹配。我不想在每次测试 运行 时都将测试数据添加到数据库中,所以我尝试使用 t运行saction 插入数据,而 t运行saction 仍然打开,我尝试读回未提交的数据(使用单独的会话)。计划是,一旦做出断言,我将回滚围绕数据插入的 t运行saction,使我的数据库保持与测试 运行 之前相同的状态。不幸的是,我在尝试读取数据时遇到以下错误:
NHibernate.Exceptions.GenericADOException : could not load an entity: [CQRSTutorial.DAL.EventDescriptor#51ff4d15-ddbc-4157-8652-a7a10177a6e3][SQL: SELECT eventdescr0_.Id as Id1_2_0_, eventdescr0_.EventType as EventT2_2_0_, eventdescr0_.Data as Data3_2_0_ FROM Events eventdescr0_ WHERE eventdescr0_.Id=?] ----> System.Data.SqlClient.SqlException : Execution Timeout Expired. The timeout period elapsed prior to completion of the operation or the server is not responding.
我专门创建了一个测试来突出这个问题,如下所示:
[TestFixture, Category(TestConstants.Integration)]
public class TimeoutExpiredTest
{
private ISession _writeSession;
public static ISessionFactory CreateSessionFactory(IsolationLevel isolationLevel = IsolationLevel.Unspecified)
{
var msSqlConfiguration = MsSqlConfiguration
.MsSql2012
.IsolationLevel(isolationLevel)
.ConnectionString(x => x.FromConnectionStringWithKey("CQRSTutorial"));
var cfg = new CustomAutomappingConfiguration();
return Fluently
.Configure()
.Database(msSqlConfiguration)
.Mappings(m =>
{
m.AutoMappings.Add(
AutoMap.AssemblyOf<EventDescriptor>(cfg)
.UseOverridesFromAssemblyOf<EventDescriptorMapping>());
})
.BuildSessionFactory();
}
[SetUp]
public void SetUp()
{
_writeSession = CreateSessionFactory().OpenSession();
_writeSession.BeginTransaction();
}
[Test]
public void InsertAndReadTabOpened()
{
var objectToStoreInDbTemporarily = GetObjectToStoreInDbTemporarily();
_writeSession.SaveOrUpdate(objectToStoreInDbTemporarily);
_writeSession.Flush(); // the data needs to go to the database (even if only temporarily), rather than just staying in an NHibernate cache.
var readSession = CreateSessionFactory(IsolationLevel.ReadUncommitted).OpenSession();
var retrievedEventDescriptor = readSession.Get<EventDescriptor>(objectToStoreInDbTemporarily.Id); // this line throws the exception after 30 seconds.
// If previous line worked, I could then assert inserted values match retrieved values at this point.
}
[TearDown]
public void TearDown()
{
_writeSession.Transaction?.Rollback();
}
public class CustomAutomappingConfiguration : DefaultAutomappingConfiguration
{
public override bool ShouldMap(Type type)
{
return type.Name == typeof(EventDescriptor).Name; // we're only mapping this class for now.
}
}
为简洁起见,我省略了创建对象的代码。最初的测试达到了它的目的——它迫使我必须为有问题的对象正确定义 NHibernate 映射。但是最初的测试是在每个测试 运行 中插入新行,这感觉很脏而且没有必要 - 因此这种 t运行saction / rollback 方法。
查看 SQL Profiler,问题似乎出在读取端 - 读取会话使用 IsolationLevel.ReadCommitted 而不是 IsolationLevel.ReadUncommitted。知道为什么读取会话不使用测试中指定的 IsolationLevel 吗?
啊,我刚刚又看了一眼,我想知道是不是因为readSession.Get()
没有明确的交易。因此,NH 正在使用具有读提交隔离级别的隐式事务吗?也许将 readSession.Get()
包装在具有读取未提交隔离级别的显式事务中?
正如 David Osborne 所说,您也需要为阅读会话打开一个事务。
但是你所做的事情值得商榷。您依赖 SQL 服务器的内部实现细节来使您的测试成功。不能保证它目前有效。
更好地实施适当的测试设置和拆卸,进行必要的清理。您的测试将更加可靠。
另一种解决方案是共享两个会话之间的连接,方法是在打开会话时提供它,或者通过从另一个会话生成一个会话。这样他们的实体缓存将不会被共享,但他们将能够在不提交数据的情况下读取彼此的数据,因为他们将在同一个事务中。
另一种解决方案是使用单个事务范围进行测试,删除 NHibernate 事务。两个会话将在同一个事务中,并且能够读取彼此的数据。但这可能会促进事务分布式,然后您的设置将需要支持它。 (需要在测试主机上启用 MSDTC。)