I’m trying to access IMessageSession while using ninject.
We are trying to upgrade from nServiceBus 4.x to 7.x and there are extensive use of IBus. Unfortunately, we can’t really change Ninject for Microsoft dependency injection. Also unfortunately, we need to access IMessageSession to lodge events outside handlers.
Looking at documentation, IMessageSession needs to be injected as externally managed endpoint via EndpointWithExternallyManagedServiceProvider but it looks like it’s made for Microsoft dependency injection.
If you need IMessageSession to be registered in the DI container, you have to register it yourself which might be a bit tricky as the registration of the session needs to happen at configuration time, while the actual instance of the session is only available once the endpoint is started. This can be achieved by something like this:
IMessageSession messageSession = null;
endpointConfiguration
.RegisterComponents(c => c
.ConfigureComponent<IMessageSession>(() => messageSession, DependencyLifecycle.InstancePerCall));
var endpointInstance = await Endpoint.Start(endpointConfiguration);
messageSession = endpointInstance;
// IMessageSession can be injected at this point
instead of the endpointConfiguration.RegisterComponents API you can also use the Ninject API directly if you are using an external Ninject container.
Please also note that Ninject won’t be supported in upcoming major versions of NServiceBus as Ninject does not support the Microsoft.Extensions.DependencyInjection.Abstractions model.
Unfortunately, when I attempt to externally manage the endpoint as per the example, I seem to keep getting “The type initializer for ‘NServiceBus.Unicast.Messages.MessageMetadataRegistry’ threw an exception.” exception. (Unfortunately no luck at UniformSession either as I keep getting Ninject.Activation exception)
Would you mind if I pick you brain a litter further?
I’m starting the endpoint thus:
IMessageSession messageSession = null;
var configuration =
new EndpointConfiguration(ConfigurationManager.AppSettings["ListenQueueName"] ?? typeof(NinjectWebCommon).Namespace);
configuration.RegisterComponents(c => c.ConfigureComponent<IMessageSession>(
() => messageSession, DependencyLifecycle.InstancePerCall));
var sqlConnectionString = ConfigurationManager.AppSettings["SqlConnectionString"] ??
throw new ArgumentNullException("SqlConnectionString");
var transport = configuration.UseTransport<SqlServerTransport>();
transport.ConnectionString(sqlConnectionString);
configuration.SendOnly();
configuration.EnableInstallers();
_endpointInstance = await Endpoint.Start(configuration);
Appreciate any help you can give to spot what I’m missing. Thank you.
seem to keep getting “The type initializer for ‘NServiceBus.Unicast.Messages.MessageMetadataRegistry’ threw an exception.” exception.
that seems to be a different issue. Can you share the full exception details?
The snippet you shared looks fine to me at first glance but seems to not share all the involved code (e.g. messageSession isn’t assigned), can you share a simple project that can reproduce the issue you’re running into?
Unfortunately, the code being migrated is a quite a bit of legacy so I can’t really create a sample project to replicate the issue but I can provide a stack trace, which unfortunately seems to be deep within Ninject.
The type initializer for ‘NServiceBus.Unicast.Messages.MessageMetadataRegistry’ threw an exception.
at NServiceBus.Unicast.Messages.MessageMetadataRegistry.d__7.MoveNext() in /_/src/NServiceBus.Core/Unicast/Messages/MessageMetadataRegistry.cs:line 141
at NServiceBus.Unicast.Messages.MessageMetadataRegistry.RegisterMessageTypesFoundIn(IList1 availableTypes) in /_/src/NServiceBus.Core/Unicast/Messages/MessageMetadataRegistry.cs:line 125 at NServiceBus.EndpointCreator.ConfigureMessageTypes() in /_/src/NServiceBus.Core/EndpointCreator.cs:line 103 at NServiceBus.EndpointCreator.Initialize() in /_/src/NServiceBus.Core/EndpointCreator.cs:line 32 at NServiceBus.EndpointCreator.Create(SettingsHolder settings, Configuration hostingConfiguration) in /_/src/NServiceBus.Core/EndpointCreator.cs:line 25 at NServiceBus.HostCreator.<CreateWithInternallyManagedContainer>d__1.MoveNext() in /_/src/NServiceBus.Core/Hosting/HostCreator.cs:line 79 at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at NServiceBus.Endpoint.<Start>d__1.MoveNext() in /_/src/NServiceBus.Core/Endpoint.cs:line 29 at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.TaskAwaiter1.GetResult()
at Nib.DocumentStore.Api.NinjectWebCommon.d__4.MoveNext() in C:\Users\gene.lee\Documents\Projects\nibnz-nib-documentstore\Nib.DocumentStore.Api\App_Start\NinjectWebCommon.cs:line 84
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
at Nib.DocumentStore.Api.NinjectWebCommon.d__2.MoveNext() in C:\Users\gene.lee\Documents\Projects\nibnz-nib-documentstore\Nib.DocumentStore.Api\App_Start\NinjectWebCommon.cs:line 38
Thank you figured it out. Turns out that I need to set diagnostic path for some reason. Not entirely sure why that killed injection but setting the path seems to have done the trick.
Just a note on injecting the IMessageSession dependency from your choice of container, since you’re dependency is on the interface nothing says that you need to wait for the Endoint.Start(configuration) task to complete prior to registering the component. It’s a trivial bit of coding to implement the interface with an implementation that either takes the awaitable start task and/or the EndpointConfiguration that should be used/scheduled. When the endpoint is finally running (the Task is complete) the calls gets passed to the actual NServiceBus message session underlying your implementation. If the start task hasn’t completed yet, the first calls simply await the task and then continue when NServiceBus is ready.
We do that to good effect and makes our configuration and dependencies on “slower” or eventually initializing components much easier to handle.