IMessageSession Not Sending A Message To Queue

We cannot upgrade to the latest versions of the .Net Framework because we are still dependent on MSMQ. I wrote a .Net 4.8.1 web api. I am using the Microsoft.Extensions to be Core like. That said I have a service configuration registration class coded like this:

using System.Web.Http;
using Microsoft.Extensions.Hosting;
using NHibernate.Cfg;
using NServiceBus;

public class ServiceConfig
{
    private static IHostBuilder builder;

    public static void Register(HttpConfiguration config)
    {
        builder = Host.CreateDefaultBuilder();
        builder.UseNServiceBus(HostBuilderContext => {
            var endpointConfiguration = new EndpointConfiguration("XXXX.JobApp.WebService");
            // configure endpoint here
            var transport = endpointConfiguration.UseTransport<MsmqTransport>();
            transport.Routing().DisablePublishing();
            endpointConfiguration.SendOnly();
            endpointConfiguration.UseSerialization<XmlSerializer>();
            return endpointConfiguration;
        });
        
        builder.ConfigureLogging();
        builder.AddConfigurationBindings();

        builder.ConfigureServices(x => x.AddSingleton<IPing, Ping>());
        builder.ConfigureServices(x => x.AddTransient<IHireApplicantDataValidator, HireApplicantDataValidator>());
        builder.ConfigureServices(x => x.AddTransient<IHireEligibilityVerifier, HireEligibilityVerifier>());
        builder.ConfigureServices(x => x.AddTransient<HireProcessController>());
        builder.ConfigureServices(x => x.AddTransient<RehireEligibleController>());
        var host = builder.Build();
        
        host.RunAsync();
        
        var resolver = new DependencyResolver(host.Services);
        config.DependencyResolver = resolver;
    }

In my controller I dependency inject IMessageSession like this:

private readonly ApplicationSettings applicationSettings;
private readonly IHireApplicantDataValidator hireApplicantDataValidator;
private readonly IHireEligibilityVerifier hireEligibilityVerifier;
private readonly ILogger<HireProcessController> log;
private readonly IMessageSession messageSession;
private int storeNumber;

public HireProcessController(ILogger<HireProcessController> singletonLog,
    IOptionsSnapshot<ApplicationSettings> optionsApplicationSettings, IMessageSession singletonMessageSession
    , IHireApplicantDataValidator transientHireApplicantDataValidator
    , IHireEligibilityVerifier transientHireEligibilityVerifier)
{
    log = singletonLog ?? throw new ArgumentNullException(nameof(ILogger<HireProcessController>));
    applicationSettings = optionsApplicationSettings.Value ??
                          throw new ArgumentNullException(nameof(ApplicationSettings));
    messageSession = singletonMessageSession ??
                     throw new ArgumentNullException(nameof(singletonMessageSession));

    hireApplicantDataValidator = transientHireApplicantDataValidator ??
                                 throw new ArgumentNullException(nameof(ILogger<HireProcessController>));
    hireEligibilityVerifier = transientHireEligibilityVerifier ??
                              throw new ArgumentNullException(nameof(transientHireEligibilityVerifier));
}

I created a very simple Get method to ping the controller and send a message. It looks like this:

        public IHttpActionResult Get()
        {
            var testMessage = new Test();
            testMessage.MessageLiteral = "JobApp Api HireProcess controller is responding";
            try
            {
                var cancellationToken = new CancellationToken();
                messageSession.SendLocal(testMessage, cancellationToken);
            }
            catch (Exception e)
            {
                log.LogCritical(e.Message);
                return InternalServerError();
            }

            log.LogInformation(testMessage.MessageLiteral);
            return Ok(testMessage.MessageLiteral);
        }

The problem is I do not get any message at all sent to the queue defined as “XXXX.JobApp.WebService” I was expecting the queue to be automatically created as well. I feel like I am missing something very simple?

I am using NServiceBus 8.0 because it is the last package that is 4.8.1 compatible. Also using NServicebus.Extensions.Hosting.

I think my problem might be that the queue is set up to be “SendOnly”. However when I remove that line I get the following service provider error:

Cannot access a disposed object.
Object name: ‘IServiceProvider’.’

Why would one have anything to do with the other?
If I have to set up a send and receive queue how do I do that with configuration and and IMessageSession?

@JackInTheBoxBen I noticed in your last snippet that you are not awaiting the SendLocal. All send/publish API’s since NServiceBus 6 are async and require the await keyword. Not awaiting can result in undeterministic behavior. Especially in this context any resource created as part of the dependency injection scope for this request could be disposed.

I see a similar error for host.RunAsync() which also needs to be awaited and very likely that is the main problem as

Can you test again but now properly awaiting asynchronous methods like SendLocal and RunAsync?

Please thoroughly review any compiler warnings as often such errors are detected by the compiler.

Queues are not created by a send-only endpoint. This code that combines SendOnly with SendLocal is a strange combination as what process will now process the message?

Queues can be create automatically when adding endpointConfiguration.EnableInstallers() and only the queues that are owned (processes message off) by that endpoint will be created.

I think this is a typo but NServiceBus 8.x.x still targets .NET Framework. The latest version is 8.2.0:

If you really want that in a web application then do not use SendOnly and add EnableInstallers. However, we usually do not recommend to run installers as part of a regular startup sequence but during deployment.

See the following guidance for recommendations on installers:

Thank you for the help. Your suggestions worked. I made the sendlocal method async and used await. I tried to do the same with the serviceconfig method but I had a problem with it not executing my dependency resolver? Currently everything looks okay without it. I also explicitly used EnableInstallers on the configuration and it built the queue for me. I ended up removing the “sendonly” as you are right it seemed like a strange combination, This was left over from the original code. The original code did not send a test message on the ping. The other thing I had to specify was the error queue. You are also correct that I am using NServiceBus 8.2.0.

@JackInTheBoxBen Unfortunately this does not contain enough information. That code does not seem to be NServiceBus related. What is the behavior that you expect that is not happening? Please be as specific as you can.

– Ramon

I am sorry if my response wasn’t very clear. I had to write a custom dependency resolver because the version of asp.net that I am using does not do it automatically like the later versions do. Maybe there is a better way?

My code looks like this.

Serviceconfig with just the relevant code

 host.RunAsync();

 var resolver = new DependencyResolver(host.Services);
 config.DependencyResolver = resolver;

My custom dependency resolver:

using System;
using System.Collections.Generic;
using System.Web.Http.Dependencies;
using Microsoft.Extensions.DependencyInjection;

namespace Jitb.Employment.Web.Api.JobApp.Services
{
    public class DependencyResolver : IDependencyResolver
    {
        private readonly IServiceProvider provider;
        private readonly IServiceScope scope;

        public DependencyResolver(IServiceProvider provider)
        {
            this.provider = provider;
        }

        internal DependencyResolver(IServiceScope scope)
        {
            provider = scope.ServiceProvider;
            this.scope = scope;
        }

        public IDependencyScope BeginScope()
        {
            return new DependencyResolver(provider.CreateScope());
        }

        public object GetService(Type serviceType)
        {
            return provider.GetService(serviceType);
        }

        public IEnumerable<object> GetServices(Type type)
        {
            return provider.GetServices(type);
        }

        public void Dispose()
        {
            scope?.Dispose();
        }
    }
}

Try changing the order:

var resolver = new DependencyResolver(host.Services);
config.DependencyResolver = resolver;

await host.RunAsync();

Also, what version of NServiceBus are you migrating from?
We have upgrade guides if that helps, see Upgrade Guides • NServiceBus • Particular Docs
Also, it may be good to review all your handler code to ensure proper async/await usage.

Regards
John

Changing the order worked. It made total sense after I thought about it. The host isn’t being ran yet so everything needs to be resolved before then
? Thank you. I will take a look at the upgrade guides and review for proper async/await usage.