Can I use Ninject to instantiate singleton services that nothing depends on?

会有一股神秘感。 提交于 2020-01-11 05:30:21

问题


I have some services in my asp.net mvc application that listen for AMQP messages and invoke methods.

No controllers depend on this, so it won't get instantiated on its own.

I could instantiate it manually, explicitly providing its dependencies with kernel.Get but it feels like I shouldn't have to do that.

Can I make Ninject instantiate classes in singleton scope eagerly even when nothing else depends on it?


回答1:


You cannot have ninject instantiate stuff in case you don't ask it to instantiate something yourself. The simple way is to ask ninject to instantiate things at composition root:

var kernel = new StandardKernel();
kernel.Bind<IFoo>().To<Foo>();
kernel.Load(AppDomain.CurrentDomain.GetAssemblies()); // loads all modules in assemlby
//...
// resolution root completely configured

kernel.Resolve<IFooSingleton>();
kernel.Resolve<IBarSIngleton>();

There is one alternative, actually, which is not the same, but can be used to achieve a similar effect. It requires that there is at least one single other service instantiated soon enough: Ninject.Extensions.DependencyCreation. It works like this:

kernel.Bind<string>().ToConstant("hello");

kernel.Bind<ISingletonDependency>().To<SingletonDependency>()
    .InSingletonScope();
kernel.DefineDependency<string, ISingletonDependency>();

kernel.Get<string>();
// when asking for a string for the first time
// ISingletonDependency will be instantiated.
// of course you can use any other type instead of string

Why

Ninject is unlike some other containers (for example Autofac) not "built" in stages. There's no concept of first creating the bindings, and then creating the kernel to use them. The following is perfectly legal:

kernel.Bind<IFoo>()...
kernel.Get<IFoo>()...
kernel.Bind<IBar>()...
kernel.Get<IBar>()...

so ninject can't possibly know when you want the singletons to be instantiated. With autofac it's clear and easy:

var containerBuilder = new ContainerBuilder();

containerBuilder
    .RegisterType<Foo>()
    .AutoActivate();

var container = containerBuilder.Build(); // now



回答2:


Coming from Guice in Java, I've sorely missed the pattern of eager singletons. They are useful in scenarios where for example modules act as plugins. If you imagine that a service is assembled from modules that are specified in a configuration, you could see a problem of then also trying to specify what this module needs to be auto-instantiated when the application is started.

For me the module is where the composition of the application is defined and separating eager singletons into another place in the code feels more clunky and less intuitive.

Anyway, I've been able to very easily implement this as a layer on top of Ninject, here's the code:

public static class EagerSingleton
{
    public static IBindingNamedWithOrOnSyntax<T> AsEagerSingleton<T>(this IBindingInSyntax<T> binding)
    {
        var r = binding.InSingletonScope();

        binding.Kernel.Bind<IEagerSingleton>().To<EagerSingleton<T>>().InSingletonScope();

        return r;
    }
}

public interface IEagerSingleton { }

public class EagerSingleton<TComponent> : IEagerSingleton
{
    public EagerSingleton(TComponent component)
    {
        // do nothing. DI created the component for this constructor.
    }
}

public class EagerSingletonSvc
{
    public EagerSingletonSvc(IEagerSingleton[] singletons)
    {
        // do nothing. DI created all the singletons for this constructor.
    }
}

After you've created your kernel, add a single line:

kernel.Get<EagerSingletonSvc>(); // activate all eager singletons

You use it in a module like this:

Bind<UnhandledExceptionHandlerSvc>().ToSelf().AsEagerSingleton();


来源:https://stackoverflow.com/questions/31445087/can-i-use-ninject-to-instantiate-singleton-services-that-nothing-depends-on

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!