Day: March 9, 2011

Handle Dependency in NServiceBus

I was using poor-man’s injection in NServiceBus until this morning I figured out how to handle dependency in NServiceBus. It’s easy, but I never got chance or brave enough to try it out

NServiceBus uses SpringBuilder as default. I want to stick with StructureMap.

Here is official doc for how to wire StructureMap up with NServiceBus: http://sourceforge.net/apps/mediawiki/nservicebus/index.php?title=Additional_containers

    // Default
//    public class EndpointConfig : IConfigureThisEndpoint, AsA_Server
//    {
//    }
    public class EndpointConfigUsingStructuremap : IConfigureThisEndpoint, AsA_Server, IWantCustomInitialization
    {
        #region IWantCustomInitialization Members

        public void Init()
        {
            Configure.With()
                .StructureMapBuilder(new Container(new DependencyRegistry()))
                .XmlSerializer();

            // you can leave the rest of the config off since "AsA_Server" handles it
        }

        #endregion
    }

    public class DependencyRegistry : Registry
    {
        public DependencyRegistry()
        {
            // Manually set
//            For<IFileService>().Use<FileService>();

            // Or auto scan
            Scan(x =>
            {
                x.TheCallingAssembly();
                x.AssemblyContainingType<IFileService>();
                x.WithDefaultConventions();
            });

        }

    }

 public class DbChangeHandler : IMessageHandler<DatabaseChangedCommand>
    {
        private readonly IFileService _fileService;

        public DbChangeHandler(IFileService fileService)
        {
            _fileService = fileService;
        }

        public void Handle(DatabaseChangedCommand message)
        {
         ...
// My old syntax, poor-man/static injection.
//            FileService.TouchFile(message.DatabaseName, path);
            _fileService.TouchFile(message.DatabaseName, path);
        }
    }

Amazing!

Another team from our company is using Ninject which is not in the out-of-box container list supported by NServiceBus (yet) , google found a solution by Aaron Jenson, https://github.com/aaronjensen/nservicebus.objectbuilder.ninject I will get there soon.

Updated on Mar. 11, 2011:

This code from https://gist.github.com/326321 works in Ninject 2.2 and NServiceBus 2.


public class EndpointConfig : IConfigureThisEndpoint, AsA_Server, IWantCustomInitialization
    {
        #region IWantCustomInitialization Members

        public void Init()
        {
            Configure.With()
                .Log4Net<ConsoleAppender>(x => { x.Threshold = Level.Info; }
                ).Log4Net<FileAppender>(x =>
                {
                    x.File = "build_trigger_log.txt";
                    x.Threshold = Level.Info;
                }
                )
                .NinjectBuilder()
                .XmlSerializer();

            // you can leave the rest of the config off since "AsA_Server" handles it
        }

        #endregion
    }

internal class CustomModule : NinjectModule
    {
        public override void Load()
        {
            Bind<IFileService>().To<FileService>();
        }
    }

// NinjectObjectBuilder is from https://gist.github.com/326321
    public static class ConfigureNinjectBuilder
    {
        public static Configure NinjectBuilder(this Configure config)
        {
            ConfigureCommon.With(config, new NinjectObjectBuilder(new StandardKernel(new CustomModule())));
            return config;
        }
    }

Note:

  1. If using NServiceBus Testing framework, constructor inject might not work, should use setter inject instead.
  2. When using autobinding in Ninject, there is a conflict in IMessageSerliazer class, my quick workaround is providing the sample type instead of ScanAssemblyMatch(‘*”), or use Scan WithoutNamespaceOf(“NServiceBus”) to allow NSB use it’s own autobinding on Msmq stuff.
  3. NSB supports both ctor injection and setter injection, but NSB.Testing framework need a default no-arg ctor, so setter injection is the only choice if using NSB.Testing.

 [TestFixture]
    public class DbChangeHandlerTest
    {
        [SetUp]
        public void Setup()
        {
            mockFileService = MockRepository.GenerateMock<IFileService>();

            Test.Initialize();

            systemUnderTest = Test.Handler<DbChangeHandler>()
                .WithExternalDependencies(h => h.file_service = mockFileService);
        }

        IFileService mockFileService;
        Handler<DbChangeHandler> systemUnderTest;

        [Test]
        public void should_call_file_service_to_touch_file()
        {
            systemUnderTest
                .OnMessage<DatabaseChangedCommand>(m => m.DatabaseName = "");

            mockFileService.AssertWasCalled(x => x.TouchFile("", ""), opt => opt.IgnoreArguments());
        }

        [Test]
        public void should_response_to_database_changed_command()
        {
            systemUnderTest
                .OnMessage<DatabaseChangedCommand>(m => m.DatabaseName = "");
        }

        [Test]
        public void should_return_error_code_zero()
        {
            systemUnderTest
                .ExpectReturn(errCode => errCode == 0)
                .OnMessage<DatabaseChangedCommand>(m => m.DatabaseName = "");
        }
    }