Why is NServiceBus configuration broken after usin

2019-06-24 06:34发布

问题:

I have a Console app within which you can specify parameters, based on the parameters specified various handlers will be loaded. For instance:

prgm.exe nyse 
prgm.exe nasdaq

The goal is that within my code I have INyseHandlers and INasdaqHandlers and in the first case only any handlers extending the former are loaded, similarly for the case of the latter. The goal is to have one program which can listen to various or all sources depending on how it is run. To achieve this, I have set up my interfaces as mentioned above. Then in my configuration set up:

var configuration = new BusConfiguration();
configuration.InitializeStepBusConventions(); // extension method, not the problem

// Load all the handlers specified in command line arguments
if (!Args.Contains("any") && Args.Length != 0)
{
    List<Type> handlersToLoad = new List<Type>();

    foreach (var argument in Args)
    {
        Console.WriteLine("Adding {0} subscribers to loaded handlers. . .", argument.ToUpper());

        switch (argument)
        {
            case "nyse":
                AddToHandlerList(handlersToLoad, typeof(INyseProcessor));
                break;
            case "nasdaq":
                AddToHandlerList(handlersToLoad, typeof(INasdaqProcessor));
                break;        
        }
    }

    configuration.TypesToScan(handlersToLoad);
}

configuration.UseContainer<NinjectBuilder>(c => c.ExistingKernel(Kernel));
configuration.EndpointName(ConfigurationManager.AppSettings[Defaults.Project.DefaultEndPointName]);
NServiceBus.Logging.LogManager.Use<NLogFactory>();

Bus.Create(configuration).Start();  

And where:

private void AddToHandlerList(List<Type> handlersToLoad, Type interfaceType)
{
    List<Type> classesWhichExtendInterface = Assembly.GetExecutingAssembly().GetTypes().Where(t => interfaceType.IsAssignableFrom(t)).ToList();
    classesWhichExtendInterface.Remove(interfaceType);

    handlersToLoad.AddRange(classesWhichExtendInterface);
} 

Types are loaded as expected, that List is fine. But when I run this and get to the Bus.Start line I get the following error:

The given key (NServiceBus.LocalAddress) was not present in the dictionary.

Without the type loading, default behavior works fine and all the processors within the assembly are loaded. Why am I getting this error after running the TypesToScan() line?

EDIT: Here is the extension method:

config.UseSerialization<JsonSerializer>();
config.UseTransport<RabbitMQTransport>();

config.UsePersistence<InMemoryPersistence>();
config.EnableInstallers();

return config;

回答1:

Your exception happens here

localAddress = Address.Parse(Settings.Get<string>("NServiceBus.LocalAddress"));

Settings get the "NServiceBus.LocalAddress" kvp set by a transport. Since you are not using the "Core" transport (MSMQ), I might suspect that your transport assembly types need to be included in TypesToScan, because of this:

ForAllTypes<Feature>(TypesToScan, t => featureActivator.Add(t.Construct<Feature>()));

I had a similar issue with using SQL Server transport, when I send a list of assemblies to With(assemblies) and did not include NServiceBus assemblies there, the transport failed to initialize. As soon as I added NServiceBus.* assemblies, everything started to work.



回答2:

I wanted to add an answer for this for anyone who might find a similar situation to what I had, where I had this exact error when using NServiceBus.Testing to test a saga.

When you run Test.Initialize to set up your unit test, you can actually pass it some BusConfiguration setup, wherein you can specify which assemblies to scan. I usually use the following as a standard setup, which prevents certain assembly loading issues if you're debugging a test (at least with NUnit):

Test.Initialize(conf => conf.AssembliesToScan(assembly.GetExecutingAssembly()))

However, for a reason unknown to me, today I was setting up a saga test and received the NServiceBus.LocalAddress error mentioned in this question. I managed to solve that by adding an additional parameter to AssembliesToScan, as follows:

Test.Initialize(conf => conf.AssembliesToScan(
            Assembly.GetExecutingAssembly(),
            Assembly.GetCallingAssembly()));

Hope this helps someone.