達成しようとしているのは、私のウェブサイトがメッセージを生成してバス上に置くと、サービスがそれを取り出して、AddedBy/UpdatedByフィールドを自動的に読み込む監査行の異なるスレッドで実行されているNServiceBusとNHibernate EventListeners
これは、ASP.NetアプリケーションにログインしているユーザーからのThread.CurrentPrincipalのメッセージヘッダーにユーザーIDを書き込むNServiceBus IMessageMutatorコンポーネントを使用して行います。 私のサービスでは、IMessageModuleを使用してこのヘッダを抽出し、これをThread.CurrentPrincipalにバインドします。これはうまく動作し、私のメッセージハンドラでは、Thread.CurrentPrincipal.Identity.NameがWebアプリケーションでメッセージを生成したユーザIDに正しくバインドされていることがわかります。
NHibernateのIPreUpdateEventListener/IPreInsertEventListenerを利用して、各エンティティのAddedBy/UpdatedByをDBに書き込む前に設定しています。これはウェブサイト上では完全に動作しますが、NServiceBusサービスでは、リスナーが実行されるスレッドはハンドラが実行されたスレッドとは異なります。つまり、スレッドのCurrentPrincipalはもはや自分のIMessageModuleにバインドされたIDではありません。
NHibernateが私の問題の原因と思われるコールスタックのDistributedTransactionFactoryを使用しているのがわかります。コミットが失敗した場合、メッセージは再試行されず、エラーキューに置かれず、キューからのメッセージの削除に失敗し、更新がDBにロールバックされないようなトランザクション性を失いたくありません。
私はWebを見渡しました。すべての例では、スレッドのCurrentPrincipalを使用して、行を変更したユーザーのIDをバインドしています。私が探しているのは、NHibernateリスナをメッセージハンドラと同じスレッドに保つか、リスナにユーザIDを渡して、DBに書き込まれる前にエンティティにバインドできる方法です。ここで
は私のリスナー、私はそれpublic class EntityPersistenceListener : IPreUpdateEventListener, IPreInsertEventListener
{
public bool OnPreUpdate(PreUpdateEvent @event)
{
var audit = @event.Entity as EntityBase;
if (audit == null)
return false;
var time = DateTimeFactory.GetDateTime();
var name = Thread.CurrentPrincipal.Identity.Name;
Set(@event.Persister, @event.State, "AddedDate", audit.AddedDate);
Set(@event.Persister, @event.State, "AddedBy", audit.AddedBy);
Set(@event.Persister, @event.State, "UpdatedDate", time);
Set(@event.Persister, @event.State, "UpdatedBy", name);
audit.AddedDate = audit.AddedDate;
audit.AddedBy = audit.AddedBy;
audit.UpdatedDate= time;
audit.UpdatedBy = name;
return false;
}
}
で見つかったSetメソッドを省略し、ここでIDを抽出し、現在のスレッドのIDにバインドしNServiceBusメッセージモジュールでてきている
public class TenantAndInstanceInfoExtractor : IMessageModule
{
private readonly IBus _bus;
public TenantAndInstanceInfoExtractor(IBus bus)
{
_bus = bus;
}
public void HandleBeginMessage()
{
var headers = _bus.CurrentMessageContext.Headers;
if (headers.ContainsKey("TriggeredById"))
Thread.CurrentPrincipal = new GenericPrincipal(new GenericIdentity(headers["TriggeredById"]), null);
else
Thread.CurrentPrincipal = new GenericPrincipal(new GenericIdentity(string.Empty), null);
}
public void HandleEndMessage()
{
}
public void HandleError() { }
}
モルガン。私はあなたの要件を完全に理解するのに苦労しています。来週のスカイプコールの時間はありますか?私はsimype.croppですskype – Simon
こんにちはサイモン、はいしてください。私は時間を手配するためにあなたにメールします。 – Morgan