Sometimes upgrading a framework could be a pain. Upgrading NServicebus to 5.X.X from 4.X.X is not any different. There are a lot of changes in the public API and not everything is documented or obvious. Sometimes the error messages could be misleading, too. This was a surprise for me from a mature open source project. So, I decided to document the missing links and save some time for anyone who is facing similar issues.

Documentation

On Particular's site, under the migration tag, you will find only two posts which are fairly long.

The API-diff documents only shows what was removed like below:
The API difference doc
It only shows what was removed. So, if you were using one of the removed types or methods, you are lucky to find a mapping in V5 straight away. In my opinion, users would much appreciate the document that shows how a method looked in V4 and how it is in V5 now. The complete API differences can be found here. It looks like a work in progress and there are signs of improvement.

The upgrade guide from V4 to V5 is a little better but does not cover everything that is changed in V5.

Upgrade Guide

The best form of documentation is the code base. For V4.6.7, you can go here and for the latest here. This is the beauty of the open-source software. The good tests are always more helpful to show the usage than the exhaustive documentation. If so many changes get done in a big bang approach like this release, it can be a time consuming affair for the user.

To be fair, very few open source projects have awesome documentation like Knockoutjs

After upgrading the version, depending on your usage, you may see a ton of compile time errors. What is the reason behind this?

The biggest change in V5

The Configure class has lost most of its useful static methods such as Configure.EndpointName. The BusConfiguration is introduced. An instance of BusConfiguration is being passed in. So, to further illustrate this, if you have your endpoint like below in V4:

public class MyEndpointConfig : IConfigureThisEndpoint, 
                              AsA_Server, 
                              IWantCustomInitialization
{
	public void Init() 
	{
    }
 }

The Init() is a part of IWantCustomInitialization.

In V5,it looks like below

public class MyEndpointConfig : IConfigureThisEndpoint,
                              AsA_Server
{
	public void Customize(BusConfiguration configuration)
    {
    }
}

IWantCustomInitialization is removed IConfigureThisEndpoint is sufficient.

This means you will have to change your endpoints and build them if you are implementing NServicebus Endpoint interfaces.

Configuration Changes

If you had extensions added on to Configure class, they are pretty much useless. You will have to re-implement those on BusConfiguration instead.

Some of the methods that are available on BusConfiguration are :

ImageForNSBConfig

Backwards compatibility and obsolete errors

The way I understand backwards compatibility is, you keep old and new implementations marking the old one with obsolete attribute. It gives users the opportunity to make gradual changes.

The V5 of NSB has methods like below:(Source- NSB github repo)

[Obsolete("Please use `ReadOnlySettings.GetConfigSection<T>` instead. Will be removed in version 6.0.0.", true)]
[EditorBrowsable(EditorBrowsableState.Advanced)]
public static T GetConfigSection<T>()
{
  throw new NotImplementedException();
}

I can imagine the reasons behind not keeping two implementations (probably too much of a hassle) but that error message is not helpful at all. Will be removed in Version 6.0.0? It is removed in this version too,right? Moreover ReadOnlySettings is an interface and does not have GetConfigSection<T> method. It is an extension method of ReadOnlySettings. The extension code file can be found here. The SettingsHolder class implements ReadOnlySettings interface, so you can access it from a property Settings (an instance of SettingsHolder) like below:

public class MyEndpointConfig : IWantToRunWhenConfigurationIsComplete
{
    public void Run(Configure config)
    {
       var configSection = config.Settings.GetConfigSection<TransportConfig>();
    }
}

If you need to access EndpointName then:

public class MyEndpointConfig : IWantToRunWhenConfigurationIsComplete
{
    public void Run(Configure config)
    {
       var endpointName = config.Settings.EndpointName(); // readonly
    }
}

However, it is a readonly property.

Now, why do we need to implement IWantToRunWhenConfigurationIsComplete to access this type of information? IConfigureThisEndpoint has Customize method with instance of BusConfiguration parameter. I tried to do

configuration.GetSettings().GetConfigSection<TransportConfig>();

for config section and

configuration.GetSettings().EndpointName(); 

to get the name.

The prior throws the KeyNotFoundException with a message "The given key (TypesToScan) was not present in the dictionary." The endpoint one also throws the KeyNotFoundException with a message "The given key (EndpointName) was not present in the dictionary." but you can definitely set it there like

configuration.GetSettings().EndpointName("MyEndpointName")`.

Perhaps, this could be a write-only property.

ConfigurationComplete event that used to be in V4 has disappeared. I took it to Stackoverflow. The community and open source nature of the project helped. Again, IWantToRunWhenConfigurationIsComplete comes to the rescue but it was not obvious.
So, the endpoint class may start looking like below in order to make it work:

 public class MyEndpointConfig : IConfigureThisEndpoint
    , AsA_Server
    ,IWantToRunWhenConfigurationIsComplete
{
    public void Customize(BusConfiguration configuration)
    {  
    	configuration.Transactions().Disable();// 
    }

    public void Run(Configure config)
    {
        var configSection = config.Settings.GetConfigSection<TransportConfig>();
        var endpointName = configuration.GetSettings().EndpointName();
    }
}

The follow up post for more changes such as persistence is coming.

Update

The part2 of this series is now online.