我正在尝试编写一个测试我的FluentNHibernate映射配置的集成测试。测试的基本思想是插入一些数据,然后将其读回,并确保插入的数据与读回的数据相匹配。我不希望在每次测试运行时都将测试数据添加到数据库中,因此我尝试使用事务插入数据,并且当该事务仍处于打开状态时,我尝试读取未提交的数据(使用单独的会话)。计划是,一旦断言完成,我将围绕插入数据回滚事务,使我的数据库处于测试运行之前的状态。不幸的是,我在尝试读取数据时遇到以下错误:
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映射。但是最初的测试是在每次测试运行时插入新行,感觉很脏且不必要 - 因此这种事务/回滚方法。
查看SQL事件探查器,似乎问题出在Read端 - Read会话使用的是IsolationLevel.ReadCommitted而不是IsolationLevel.ReadUncommitted。任何想法为什么读取会话不会使用测试中指定的IsolationLevel?
答案 0 :(得分:0)
啊,我刚看了一眼,我想知道是不是因为readSession.Get()
没有明确的交易。因此,NH正在使用具有读提交隔离级别的隐式事务?也许将readSession.Get()
包装在具有读取未提交隔离级别的显式事务中?
答案 1 :(得分:0)
正如David Osborne所说,你也需要为你的阅读课程开设一个交易。
但你所做的事情是值得商榷的。您依靠SQL服务器的内部实现细节来使测试成功。它目前无效的事实将无法保证。
更好地实施正确的测试设置和拆卸,进行必要的清理。您的测试将更加可靠。
另一种解决方案可能是在两个会话之间共享连接,可以通过在打开会话时提供它,也可以通过从另一个会话中生成一个会话来共享。这样他们的实体缓存就不会被共享,但是他们将能够在没有提交的情况下读取彼此的数据,因为它们将在同一个事务中。
另一个解决方案是使用单个事务范围来进行测试,删除NHibernate事务。两个会话都在同一个事务中,并且能够读取彼此的数据。但这可能会促使交易分发,然后您的设置需要支持它。 (需要在测试主机上启用MSDTC。)