0
votes

I am writing a new service, and am using Topshelf and EasyNetQ for the first time. I am not using MVC or WebAPI (which most ninject tutorials/blog posts seem to assume). the key dependencies are Topshelf, Ninject (the mono build version not the nugget package) and EasyNetQ.

I selected ninject based on having used it in MVC projects, and the availability of the Topshelf.ninject library.

So whilst setting up topshelf I call the UseNinject method and pass in a Ninject module:

        public static int Main(string[] args)
    {

        Thread.CurrentThread.Name = "Service Main Thread";


            var exitCode = HostFactory.Run(x =>
            {
                x.UseNinject(new Prototype.Service.Modules.IocModule());

                x.Service<SampleService>(s =>
                {
                    s.ConstructUsingNinject();
                    s.WhenStarted((service, hostControl) => service.Start(hostControl));
                    s.WhenStopped((service, hostControl) => service.Stop(hostControl));
                });
                x.RunAsLocalSystem();
                x.SetDescription("Prototype .NET Micro Service");
                x.SetDisplayName(typeof(SampleService).Namespace);
                x.SetServiceName(typeof(SampleService).Namespace);
                x.UseNLog();
            });
        return (int) exitCode;
        }
    }

and the module looks like this:

public class IocModule : NinjectModule
{
    /// <summary>
    /// Bind Interfaces to implimentations for dependancy injection
    /// </summary>
    public override void Load()
    {
        Bind<ISampleService>().To<SampleService>();
        Bind<IAdvancedBus>().ToMethod(context => BusFactory.CreateMessageBus()).InSingletonScope();
        Bind<IExchange>().ToMethod(context => ExchangeFactory.CreatExchange(Kernel.Get<IAdvancedBus>())).InSingletonScope();
        Bind<IQueue>().ToMethod(context => QueueFactory.CreatQueue(Kernel.Get<IAdvancedBus>())).InSingletonScope();
        Bind<ILogger>().To<Logger.Logger>().InSingletonScope();
        Bind<IMessagePublisher>().To<MessagePublisher>();
        Bind<IMessageConsumer>().To<MessageConsumer>();
        Bind<ISubscriber>().To<SampleSubscriber>();
        Bind<IAutoSubscriberMessageDispatcher>().To<MessageDispatcher>();
        Bind(typeof(IRepository<>)).To(typeof(MongoRepository<>));
        Bind<IHostingEnvironment>().To<HostingEnvironment>();
        Bind<ISampleLogic>().To<SampleBusinessLogicClass>();
    }
}

and a factory:

    public static class QueueFactory
{
    public static IQueue CreatQueue(IAdvancedBus bus)
    {
        var queue = bus.QueueDeclare("QueueName");
        return queue;
    }
}

My issue is with the binding of IExchange and IQueue in the ninject module. As you can see, the factory methods need an instance of the IAdvancedBus, however Kernel is null at runtime. I am trying to pass in the dependency through the method args because it is a static class, and I can't inject through the constructor.

What is the correct way to do this? I can't seem to get a reference to the ninject Kernel, and it just doesn't feel right. I feel like I am falling down a rabbit hole of trying to do it the wrong way. Additionally kernel is marked as obsolete, and I can't see another way to get an instance of an object. Obviously I can't use the dependency resolver as you would in MVC either.

Does topshelf have an equivalent dependency resolver? is there a better pattern for this? I do what to keep things loosely coupled though, so I would like to avoid static classes, singletons or anything else that forces me in to concrete dependencies that are difficult to test.

any ideas?

1

1 Answers

3
votes

the Ninject.IContext (the argument in the lambda) has a reference to the IKernel, so change to:

Bind<IQueue>().ToMethod(context => QueueFactory.CreatQueue(context.Kernel.Get<IAdvancedBus>())).InSingletonScope();