2016-07-06 47 views
0

我想要實現的是我的網站發出一條消息,並將它放在總線上,一個服務將它拾起並寫入數據庫,並通過審計自動填充AddedBy/UpdatedBy字段該行。在不同的線程上運行的NServiceBus和NHibernate EventListeners

我通過使用NServiceBus IMessageMutator組件將用戶ID寫入來自我的ASP.Net應用程序中登錄用戶的Thread.CurrentPrincipal的消息頭中。 在我的服務中,我使用一個IMessageModule來提取這個頭並將其綁定到Thread.CurrentPrincipal。這很好,在我的消息處理程序中,我可以看到Thread.CurrentPrincipal.Identity.Name正確綁定到在Web應用程序中引發消息的用戶標識。

利用NHibernate的IPreUpdateEventListener/IPreInsertEventListener我將每個實體的AddedBy/UpdatedBy設置寫入數據庫之前。這在網站上完美運行,但在我的NServiceBus服務中,偵聽器運行的線程與處理程序運行的線程不同,這意味着線程的CurrentPrincipal不再是IMessageModule中綁定的ID。

我可以看到NHibernate在調用堆棧中使用DistributedTransactionFactory,我懷疑這是造成我的問題的原因。我不想失去事務性,如果提交失敗,則不會重新嘗試或放置在錯誤隊列中,並且如果從隊列中移除消息失敗,並且更新不會回滾到數據庫。

我瀏覽過網頁,所有例子都利用線程的CurrentPrincipal來綁定修改行的用戶的ID。我正在尋找的是一種將NHibernate偵聽器保留在與消息處理程序相同的線程上的方法,或將用戶ID傳遞給偵聽器,以便在將實體寫入數據庫之前綁定到實體。

這裏是我的聽衆,我省略了它

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;    
    } 
} 

發現設置方法這裏是NServiceBus消息模塊中提取ID,並將其綁定到當前線程的身份

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() { } 
} 
+1

Morgan。我無法完全理解您的要求。你下週有時間撥打Skype電話嗎?我是「simon.cropp」上的Skype – Simon

+0

嗨西蒙,是的請。我會通過電子郵件安排時間。 – Morgan

回答

0

謝謝西蒙的全力幫助。在廣泛討論我的問題並討論NServiceBus如何在內部工作之後,我深入瞭解了您的見解併爲NServiceBus實現了一個工作單元模塊。

發生了什麼是我們依靠每條消息創建的事務將我們的NHibernate會話提交給數據庫。這發生在利用線程池的分佈式事務控制器(特別是它發生在這裏的NHibernate.Transaction.AdoNetWithDistributedTransactionFactory.DistributedTransactionContext)上。

通過使用NServiceBus的IManageUnitsOfWork接口,我能夠在代碼示例中顯式提交我們的事務與消息處理程序在同一個線程上。

作爲未來讀者的一個便箋,這裏最好的解決方案是不使用Thread.CurrentPrincipal,因爲這個解決方案在多線程環境中失敗了,就像它對我來說一樣。

public class HiJumpNserviceBusUnitOfWork : IManageUnitsOfWork 
{ 
    private readonly IUnitOfWork _uow; 

    public HiJumpNserviceBusUnitOfWork(IUnitOfWork uow) 
    { 
     _uow = uow; 
    } 

    public void Begin() 
    { 
     _uow.ClearCache(); 
     _uow.BeginTransaction(); 
    } 

    public void End(Exception ex = null) 
    { 
     if (ex != null) 
     { 
      _uow.ClearCache(); 
     } 
     else 
     { 
      _uow.CommitTransaction(); 
     } 
    } 
} 
相關問題