2012-04-11 8 views
0

ハンドラに例外がスローされないにもかかわらず、メッセージXを何度もやり直すNServiceBusに厄介な問題があります。 NHibernateセッションとNSB ambiantトランザクションを扱ういくつかの情報があります。何のエラーも投げられないので、私は問題の100%ではないので、何をすべきかを決めることはできません。NServiceBusは例外がなくてもメッセージを再試行します

私はNSBはそうのような城ウィンザーで構成されてしまった:

IWindsorContainer container = new WindsorContainer(new XmlInterpreter()); 
container.Install(new ContainerInstaller()); 
container.Install(new UnitOfWorkInstaller(AppDomain.CurrentDomain.BaseDirectory, Castle.Core.LifestyleType.Scoped)); 
container.Install(new FactoryInstaller(AppDomain.CurrentDomain.BaseDirectory)); 
container.Install(new RepositoryInstaller(AppDomain.CurrentDomain.BaseDirectory)); 

Configure.With() 
    .CastleWindsorBuilder(container) 
    .FileShareDataBus(Properties.Settings.Default.DataBusFileSharePath) 
    .MsmqTransport() 
     .IsTransactional(true) 
     .PurgeOnStartup(false) 
    .UnicastBus() 
     .LoadMessageHandlers() 
     .ImpersonateSender(false) 
    .JsonSerializer(); 

UnitOfWorkInstallerはそうのような作業単位(NHibernateのセッション)を登録します。

public void Install(IWindsorContainer container, IConfigurationStore store) 
{ 
    var fromAssemblyDescriptor = AllTypes.FromAssemblyInDirectory(new AssemblyFilter(_installationPath)); 
    container.Register(fromAssemblyDescriptor 
     .IncludeNonPublicTypes() 
     .Pick() 
     .If(t => t.GetInterfaces().Any(i => i == typeof(IUnitOfWork)) && t.Namespace.StartsWith("Magma")) 
     .WithService.AllInterfaces() 
     .Configure(con => con.LifeStyle.Is(_lifeStyleType).UsingFactoryMethod(k => k.Resolve<IUnitOfWorkFactory>().Create()))); 
} 

だから、毎回メッセージが到着すべてのリポジトリで同じ作業単位が使用されます。現在のトランザクションの結果を手動でロールバックするとエラーが発生する(実際にはわからない)NSBがすべてのトランスポート・メッセージの子コンテナを作成し、この子コンテナがメッセージの処理後に破棄されることもわかります。 (_unitOfWorkがコンストラクタ依存性として渡される)

public void Handle(<MessageType> message) 
    { 
     using (_unitOfWork) 
     { 
      try 
      { 
       // do stuff 
       _unitOfWork.Commit(); 
      } 
      catch (Exception ex) 
      { 
       _unitOfWork.Rollback(); 

       // rethrow so the message stays in the queue 
       throw; 
      } 
     } 
    } 

public void Dispose() 
    { 
     if (!_isDisposed) 
     { 
      DiscardSession(); 
      _isDisposed = true; 
     } 
    } 

    private void DiscardSession() 
    { 
     if (_transaction != null && _transaction.IsActive) 
     { 
      _transaction.Dispose(); 
     } 
     if (Session != null) 
     { 
      Session.Dispose(); 
     } 
    } 

私のハンドラは、このように構成されています。問題は、子コンテナが配置されたときに、作業単位がこのように配置されているということですセッションをフラッシュしてトランザクションをコミットする作業単位をコミットしないと、メッセージが最大再試行回数を超えて再試行されたというエラーが発生したことがわかりました。

SoこれはNHibernateセッションとリンクされているようですそれが作成され処分される方法ですが、作業単位内で作成されているので、セッションファクトリを実際に使用することはできません。 IMessageModuleを使用してセッションを作成して破棄することはできますが、最初にエラーの原因を理解できないため、これが適切な方法かどうかはわかりません。要約するので

:すべてのハンドラは、子コンテナに(THXを同じインスタンスを共有することを使用して依存関係のように、

  • は私がところで、作業のスコープユニットを使用しています:私はセットアップをしました子コンテナがすべての一時オブジェクトをそのコンテナ内のシングルトンとして扱うが、作業ユニットは共有されていないことがわかったので、これはスコープ設定の理由です)

  • 私は私のハンドラをusing(_unitOfWork) { }ステートメントにラップして、各処理後に作業ユニットを廃棄します。私は明示的に_unitOfWorkCommitを呼び出さない場合は、作業取得者のユニットが配置され

  • 、NHibernateのセッションも

  • が配置され、メッセージは最大再試行回数を超えて再試行と、エラーがスローされます。

この現象の原因は何ですか。 IMessageModuleはこれに対する答えですか?

+0

エラーキュー内のメッセージを見てください。メッセージヘッダーに例外メッセージはありますか? – stephenl

+0

@stephenlいいえ、私が見る唯一の事は ''と同じメッセージです。それはNHibernateに関連しているようですが、私はNHibernateとNSBの統合を持っていません。 –

答えて

0

私はそれを少し狭めたと思う...私はusing(_unitOfWork)_unitOfWork.Commit()および_unitOfWork.Rollback()のすべてを削除し、NHibernateのセッションがNSBトランザクションスコープに参加していたので、NSB TransactionScopeがトランザクションをコミットまたはロールバックするようにしました。

また、NHibernateセッションのトランザクション(Session.Transaction)を、Session.BeginTransaction()を通して参照を取得する代わりに使用し始めました。私はUoWの実装をコピー/ペーストしているので、違いを見ることができます(古いコードはコメントにあります)。

私の変更がセッションのトランザクションを使用していて、トランザクション内で世話をしているので、フラッシュを削除しているかどうかわかりません。問題を解決したようです...私は明示的に呼び出す必要はありませんCommitメソッドを使用して、メッセージが正常に処理されるようにします。私のUoW実装は次のとおりです。

public class NHibernateUnitOfWork : INHibernateUnitOfWork 
{ 
    //private ITransaction _transaction; 
    private bool _isDisposed; 
    private bool _isInError; 

    public ISession Session { get; protected set; } 

    public NHibernateUnitOfWork(ISession session) 
    { 
     Contract.Requires(session != null, "session"); 
     Session = session; 

     //_transaction = Session.BeginTransaction(); 

     // create a new transaction as soon as the session is available 
     Session.BeginTransaction(); 
     _isDisposed = false; 
     _isInError = false; 
    } 

    public void MarkCreated(Object entity) 
    { 
     // assert stuff 

     try 
     { 
      Session.SaveOrUpdate(entity); 
      //Session.Flush(); 
     } 
     catch (HibernateException ex) 
     { 
      HandleError(); 
      throw; 
     } 
    } 

    public void MarkUpdated(Object entity) 
    { 
     // assert stuff 

     try 
     { 
      Session.Update(entity); 
      //Session.Flush(); 
     } 
     catch (HibernateException ex) 
     { 
      HandleError(); 
      throw; 
     } 
    } 

    public void MarkSavedOrUpdated(Object entity) 
    { 
     // assert stuff 

     try 
     { 
      Session.SaveOrUpdate(entity); 
      //Session.Flush(); 
     } 
     catch (HibernateException) 
     { 
      HandleError(); 
      throw; 
     } 
    } 

    public void MarkDeleted(Object entity) 
    { 
     // assert stuff 

     try 
     { 
      Session.Delete(entity); 
      //Session.Flush(); 
     } 
     catch (HibernateException ex) 
     { 
      HandleError(); 
      throw; 
     } 
    } 

    public void Commit() 
    { 
     // assert stuff 

     try 
     { 
      //Session.Flush(); 
      //_transaction.Commit(); 
      Session.Transaction.Commit(); 
     } 
     catch (HibernateException ex) 
     { 
      HandleError(); 
      throw; 
     } 
    } 

    public void Rollback() 
    { 
     // assert stuff 

     try 
     { 
      //if (!_transaction.WasRolledBack) 
      //{ 
      // _transaction.Rollback(); 
      //} 
      Session.Transaction.Rollback(); 
     } 
     catch (HibernateException ex) 
     { 
      HandleError(); 
      throw; 
     } 
    } 

    public void Dispose() 
    { 
     if (!_isDisposed) 
     { 
      DiscardSession(); 
      _isDisposed = true; 
     } 
    } 

    private void DiscardSession() 
    { 
     //if (_transaction != null && _transaction.IsActive) 
     //{ 
     // _transaction.Dispose(); 
     //} 
     if (Session != null) 
     { 
      try 
      { 
       // rollback all uncommitted changes 
       if (Session.Transaction != null && Session.Transaction.IsActive) 
       { 
        Session.Transaction.Rollback(); 
       } 
       //Session.Clear(); 
       Session.Close(); 
      } 
      catch (Exception) 
      { } 
      finally 
      { 
       Session.Dispose(); 
      } 
     } 
    } 

    private void HandleError() 
    { 
     _isInError = true; 
     //if (_transaction != null && _transaction.IsActive) 
     //{ 
     // _transaction.Rollback(); 
     //} 
     if (Session.Transaction != null && Session.Transaction.IsActive) 
     { 
      Session.Transaction.Rollback(); 
     } 
    } 

    // assert methods 
} 

これは意味がありますか?私はまだ最初にエラーの原因を知ることはできませんが、トランザクションスコープが終了する前にNHibernate Sessionを破棄しなければならないようです。

+0

私は、UoWが一時的に設定されている場合にのみ動作していることに気付きました。スコープとして設定されている場合、動作していません。私はCastle Windsorが子コンテナをサポートしていると思っていましたが、うまくいかないようです...ハンドラが2つの依存関係を持つ場合、1つはUoW自体であり、もう1つはUoWに依存するリポジトリです同じではありません。 –

関連する問題