What I am trying to achieve is my website raises a message and puts it on the bus, a service picks it up and writes to the database with auditing that automatically populates the AddedBy/UpdatedBy field of the row.
I do this by using the NServiceBus IMessageMutator component which writes the user ID to the message headers from Thread.CurrentPrincipal which comes from the logged in user in my ASP.Net application. In my service I use a IMessageModule to extract this header and bind this to the Thread.CurrentPrincipal. This works great and during my message handler I can see Thread.CurrentPrincipal.Identity.Name is correctly bound to the user ID that raised the message in the web application.
Utilising the IPreUpdateEventListener/IPreInsertEventListener of NHibernate I am setting the AddedBy/UpdatedBy of each entity before it is written to the DB. This works on the website perfectly but in my NServiceBus service the thread that the listener runs on is different to the thread that handler ran on, which means the CurrentPrincipal of the thread is no longer the ID bound in my IMessageModule.
I can see NHibernate is using a DistributedTransactionFactory in the call stack which I suspect is the cause of my issue. I don't want to lose transactionality such that if the commit failed the message is not re-tried or put on the error queue and if the removal of the message from the queue fails and the update does not roll back to the DB.
I have looked around the web and all the examples utilise the thread's CurrentPrincipal to bind the id of the user that modified the rows. What I am looking for is a way to either keep the NHibernate listener on the same thread as the message handler or pass the user ID to the listener so it can be bound on to the entity before it is written to the DB.
Here is my listener, I have omitted the Set method found in it
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;
}
}
And here is the NServiceBus Message module that extracts the id and binds it to the current thread's identity
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() { }
}