NHibernate Listener access to message context in NServiceBus 7.2

We are still blocked by the fact that we cannot access the IMessageHandlerContext in the EventListeners for NHibernate. I have been digging around this for days and I tried various workarounds but it seems impossible to fix it without being able to publish events inside the listener (same correlation id etc). I tried intercepting the saves/updates and publish the events myself but the problem is with cascading inserts/updates now.
Do you have any recommendations?

Reza,

if the async local doesn’t work and NHibernate really does a thread switch then also your previous code around V5 would have executed on the bus flavor that is not part of the incoming message transaction and therefore messages would go out even if the message was rolled back. Have you verified that?

Like Szymon mentioned with the information we have it is very difficult to help you. Maybe you can package up the code you have into a zip or upload it to some drive and then open a ticket with support at particular dot net pointing to that code so that we can have a closer look.

FYI I did for example check the load event listener and they are not executed on a different thread. So resolving the message handler context in such a listener should be possible by using the async local trick mentioned at the beginning of this conversation.

Regards
Daniel

I am not sure of the message roll back but I know normally it worked in v5 without a problem as we used IBus in the listener to publish events. In the new versions we do not have the instance of IBus in the container so we need to use AsyncLocal as suggested to get IMessageHandlerContext instance but for some reason it is null sometimes. I will try it again.

AsyncLocal value is null when the Listener event is raise due to cascading inserts or updates in NHibernate. For example if we have an entity User and the user has Logins property, every time user gets updated, there maybe new Login items persisted via NHibernate. In this case, it seems like they are on different threads and there is no way to access IMessageHandlerContext.

Also, I tried using UniformSession but that doesn’t help either as the events/messages published/sent using UniformSession don’t have the correct headers.

@rezajp At this point I think we’ve reached the limit of what we’re able to help you with here. Please open a support case pointing to this thread so that we can schedule a call with you. We’ll be able to take a look directly at your code that way and hopefully get you unstuck.

Thanks. I have kind of come up with a solution but I am not sure if it is valid (it works though, for now). Could you please let me know what you think?

 var context = ContextAccessor.GetTransportReceiveContext(); // this is ITransportReceiveContext instance using AsyncLocal and IBehavior in the pipeline. IMessageHandlerContext instance using AsyncLocal was null here
        var options = new PublishOptions();
        if (context.Message.Headers.ContainsKey(LoggingConstants.CorrelationIdKey))
        {
            options.SetHeader(LoggingConstants.CorrelationIdKey,
                context.Message.Headers[LoggingConstants.CorrelationIdKey]);
        }

        if (context.Message.Headers.ContainsKey(LoggingConstants.InternalCorrelationIdKey))
        {
            options.SetHeader(LoggingConstants.InternalCorrelationIdKey,
                context.Message.Headers[LoggingConstants.InternalCorrelationIdKey]);
        }

        var bus = ServiceLocator.Resolve<IUniformSession>(); 

        bus.Publish(@event, options).ConfigureAwait(false);

Based on that code I think I finally understand why message handler context was null. It happened because the code that is executed happens outside the stage that is concerned with the message handler context. The transport receive context stage is the stage that lives the longest and therefore you are no longer seeing that problem. This means your code is exactly executed in a different stage. So if you could find out where it is executed you could use that concrete context and then even use it to do the publish and thus reduce the need for uniform session.

I am not sure at what stage. It is in the NHibernate listener. I am attaching Insert/Update/Delete listeners to NHibernate so just before sending the sql command I can do certain things such as raise NSB events or set the modified date/ user. Could you clarify what you mean by stage or point me to the documentation?

Hi Reza

This documentation page explains the various stages

Regards
Daniel