2017-06-29 6 views
1

FluentNHibernateマッピング設定をテストする統合テストを作成しようとしています。テストの基本的な考え方は、いくつかのデータを挿入してから読み取って、挿入されたデータが読み取ったデータと一致することを確認することです。テストを実行するたびにテストデータをデータベースに追加したくないので、トランザクションを使用してデータを挿入しようとしていますが、そのトランザクションはまだ開いていますが、コミットされていないデータを(別のセッションを使用して) 。プランは、アサーションが作成されると、データの挿入前後でトランザクションをロールバックし、テストを実行する前と同じ状態にデータベースを残します。残念ながら、しかし、私はデータを読み取ろうとした時点で、次のエラーを取得しています:IsolationLevel.ReadUncommittedを使用した場合のタイムアウトSqlException

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プロファイラを見ると、読み取り側に問題があるようです。読み取りセッションは、IsolationLevel.ReadUncommittedではなくIsolationLevel.ReadCommittedを使用しています。読み取りセッションがテストで指定されたIsolationLevelを使用しない理由は何ですか?

+0

'BeginTransaction()'の呼び出しが 'ReadUncommitted'分離レベルのトランザクションを確実に開いていることを確認しましたか?設定を作成するときに指定されていますが、チェックする価値はあると思います。さらに、 'uncommitted'読み取りレベルでSQL Serverでクエリを実行して、SQL Serverが期待どおりに動作するかどうかを確認しましたか? –

+0

ありがとう、David。私はSQLプロファイラをチェックインしましたが、write-sessonのトランザクションオープンを見ることができます。これはReadCommittedとして開きますが、それは問題ありません。しかし、Read-Session(ReadUncommittedとして設定しようとした)が、プロファイラーにReadCommittedとして表示されているため、タイムアウトになります。また、このテストのデバッグ中に、トランザクション分離レベルReadUncommittedを使用している間に、SQL Server Management Studioでコミットされていないデータを確認することができました。ですから、今調べるべき唯一の理由は、read-sessionが、指定されたReadUncommittedの代わりにReadCommittedとしてオープンしている理由です。 –

+0

テスト設定からこの行を変更してください: '_writeSession.BeginTransaction();'を '_writeSession.BeginTransaction(IsolationLevel.ReadUncommitted);に変更して、それがどのような効果を持っているかを見てください。構成レベルで分離レベルを設定しても、目的の効果が得られない可能性があります。 –

答えて

0

ああ、私はもう一度見ましたが、readSession.Get()に明示的なトランザクションがないためですか?したがって、NHは、読み取りコミットされた分離レベルを持つ暗黙的なトランザクションを使用していますか?たぶんreadSession.Get()を、コミットされていない読み取りレベルで明示的に処理することができますか?

0

David Osborne氏によると、読込セッションでもトランザクションを開く必要があります。

しかし、あなたがしていることはかなり議論の余地があります。あなたは、テストを成功させるために、SQLサーバーの内部実装の詳細に頼っています。それが現在働いているという事実は保証されません。

必要なクリーンアップを実行して、適切なテストのセットアップとティアダウンを実装することをよくしてください。あなたのテストははるかに信頼性が高くなります。

もう1つの解決策は、2つのセッション間でセッションを開くときにセッションを提供するか、セッションを開いてセッションを共有することです。この方法では、エンティティキャッシュは共有されませんが、コミットされていないデータは同じトランザクション内にあるため、互いに読み取ることができます。

また、もう1つの解決策は、テストを行うために単一のトランザクションスコープを使用し、NHibernateトランザクションを削除することです。どちらのセッションも同じトランザクション内にあり、互いのデータを読み取ることができます。しかし、これにより、トランザクションが分散され、セットアップがそれをサポートする必要があります。 (テストホストでMSDTCを有効にする必要があります)

+0

ありがとうフレデリック - はい、セッション間の共有接続は、これを解決する別の実行可能な方法のように聞こえます。私はこれをできるだけ軽量にしておきたいので、MSDTCを避けています(MSDTCを使用していませんでした)。 私は内部SQLサーバーの実装の詳細に頼っていますが、明らかにする?また、今後の統合テストごとに最小限のセットアップ/ティアダウンが必要なソリューションを探していました。将来のすべてのテストで最小限の労力で活用できる汎用ソリューションを考案したかったのです。 –

関連する問題