代码之家  ›  专栏  ›  技术社区  ›  Ronan Moriarty

使用IsolationLevel.ReadUncommitted时超时SqlException

  •  2
  • Ronan Moriarty  · 技术社区  · 7 年前

    我正试图编写一个集成测试来测试我的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映射。但是,最初的测试是在每次测试运行时插入新行,这让人感觉脏而且没有必要——因此采用了这种事务/回滚方法。

    2 回复  |  直到 7 年前
        1
  •  0
  •   David Osborne    7 年前

    啊,我刚刚看了一下,我想知道是不是因为 readSession.Get() readSession.Get() 在具有读取未提交隔离级别的显式事务中?

        2
  •  0
  •   Frédéric    7 年前

    正如David Osborne所说,您也需要为读取会话打开一个事务。

    但是你正在做的是相当有争议的。您的测试成功依赖于SQL server的内部实现细节。目前它的工作情况无法保证。

    另一种解决方案可以是共享两个会话之间的连接,或者在打开会话时提供连接,或者从另一个会话派生一个会话。这样,他们的实体缓存将不会共享,但他们将能够在不提交数据的情况下读取彼此的数据,因为他们将在同一事务中。

    另一种解决方案是使用单个事务范围进行测试,删除NHibernate事务。两个会话将位于同一事务中,并且能够读取彼此的数据。但这可能会将事务升级为分布式,然后您的设置将需要支持它。(需要在测试主机上启用MSDTC。)