Upgrading NServiceBus to V5 from V4 - Part2

This is a continuation of my previous NServiceBus upgrade post.

Logging

The logging functionality that used to be in NServiceBus.Core is moved to a separate set of nuget packages such as NServiceBus.CommonLogging, NServicebus.Log4net and NServiceBus.NLog

SetLoggingLibrary from V4 is removed from V5. LogManager.Use<Log4NetFactory>() from the NServicebus.Log4Net will get the job done for Log4Net implementations. The obsolete error message clearly states that.

EndpointName

In order to stop the machine name from being appended, I thought the line below was sufficient.

configuration.ScaleOut().UseSingleBrokerQueue();

It didn't work. Then, I stumbled on this Stackoverflow post and this Github issue. It looks like RabbitMQ, SQLServer and ActiveMQ transports override that setting and try to create a queue with a machine name at the end of it even though one without the machine name exists.

To disable this behavior, you can do something like below (I am using SqlServerTransport for the sake of an example, the same should work for RabbitMQ and ActiveMQ).

configuration.UseTransport<SqlServerTransport>().DisableCallbackReceiver();

StructureMap

It is a good idea to upgrade the dependent packages if they are being used. So, for the StructureMap, after upgrading the NServiceBus.StructureMap package, the configuration that looked like below in V4

Configure.StructureMapBuilder(ObjectFactory.Container)

is like this in V5

configuration.UseContainer<StructureMapBuilder>(b=>b.ExistingContainer([container]));

It is clearly stated in the the error message from StructureMapBuilder extension method. This is an example of a good message.

Assembly Scanning

NServiceBus scans the directory where the assembly with the class that implements IConfigureThisEndPoint resides. This has to be done with care because it is very easy to fall into a dependency hell hole, and the endpoint will not come up because it may start scanning too many assemblies and their dependencies. It can be configured with a finite set of assemblies.

In V4, Configure.With([ListOfAssemblies]) used to be a way to pass the list of assemblies. In V5, configuration.AssembliesToScan(listOfAssemblies) will get the job done where configuration is an instance of BusConfiguration. The name makes more sense in V5. It can take IEnumberable<Assembly>,IIncludesBuilder or IExcludesBuilder.

I think the IIncludesBuilder approach is handy because the list of assemblies will be finite and the rest of the assemblies are excluded at the time of bringing the endpoint up. You can also do some patterns matching.

var listOfAssemblies = AllAssemblies.Matching("YourNameSpace.").And("SomethingElse");
configuration.AssembliesToScan(listOfAssemblies);

More on assembly scanning can be found at this Particular documentation link. This page makes me optimistic that the documentation will eventually catch up.

If too many assemblies are included, the dependency check can spiral into a hole. If too few are included, then you may see errors like No handlers found for the message type or Could not determine type for node like this google groups discussion

MSMQ utilties

The MsmqUtilities class is not public anymore in V5. I don't think it was ever meant to be. It is however allowed to be copied if needed. The V4 source is here and the latest is here

Persistence and features

Particular has provided three types of persistence implementations. InMemory persistence comes with core nuget. NHibernate has its own NServicebus.NHibernate nuget so does the NServiceBus.RavenDB

While using persistence, the order is important. The last option wins. It is highly recommended to take a look at this documentation link

If you want to roll out your own, you can't do it as a BusConfiguration extension because it won't work. It could be implemented as features. The endpoint below is using MyFancyPeristence

 public class MyEndpointConfig
    :IConfigureThisEndpoint
    ,AsA_Server
    ,IWantToRunWhenConfigurationIsComplete
{
    public void Customize(BusConfiguration configuration)
    {
        configuration.UsePersistence<MyFancyPeristence>().For(Storage.Subscriptions, Storage.Timeouts);
    }
    public void Run(Configure config)
    {
        //read settings here.
        //var settings = config.Settings;
    }
}

We are demanding the MyFancyPersistence to provide implementations at least for subscriptions and timeouts. The Storage enum looks like below in NSB codebase as of today.

public enum Storage
{
    Timeouts = 1,
    Subscriptions = 2,
    Sagas = 3,
    GatewayDeduplication = 4,
    Outbox = 5,
}

(source - NSB Storage enum (subject to change))

MyFancyPersistence (besides inheriting from PersistenceDefinition) declares default features with Defaults method and what it can support with Supports method.

public class MyFancyPeristence :PersistenceDefinition
{
    public MyFancyPeristence()
    {
        Defaults(s => s.EnableFeatureByDefault<MyDefaultFeature>());
        Supports(Storage.Timeouts, s => s.EnableFeatureByDefault<MyTimeoutsFeature>());
        Supports(Storage.Subscriptions, s => s.EnableFeatureByDefault<MySubscriptionFeature>());
    }
}

The individual features look like below. Again, this is a simplistic implementation.

public class MyDefaultFeature :Feature
{
    protected override void Setup(FeatureConfigurationContext context)
    {
        var settings = context.Settings; //instance of ReadOnlySettings to get endpointname,etc
        var pipeline = context.Pipeline; //instance of PipelineSettings to register steps in NSB pipeline
        var items = context.Container; //intance of IConfigureComponents to ConfigureComponents
    }
}

public class MyTimeoutsFeature :Feature
{
    protected override void Setup(FeatureConfigurationContext context)
    {
        //Configure components that implement timeouts implementation into fav storage
    }
}
public class MySubscriptionFeature :Feature
{
    protected override void Setup(FeatureConfigurationContext context)
    {
        //Configure components that implement subscription implementation into fav storage
    }
}

For more detailed implementations, please take a look at NHibernate, InMemory or RavenDb.

Conclusion

In my opinion, most of these changes are good changes and make sense. They provide more flexibility like this stackoverflow post. If you are an early adopter, you will have to deal with the documentation that is catching up and a lot of changes in the public API. The open source nature of the project overcomes all of these even if it can be a little time consuming to dig for little changes. I hope these posts help and save some time and grief.



comments powered by Disqus