【问题标题】:Can I use Ninject to instantiate singleton services that nothing depends on?我可以使用 Ninject 实例化没有任何依赖关系的单例服务吗?
【发布时间】:2015-07-16 03:52:24
【问题描述】:

我的 asp.net mvc 应用程序中有一些服务用于侦听 AMQP 消息并调用方法。

没有控制器依赖于此,因此它不会自行实例化。

我可以手动实例化它,通过kernel.Get 显式提供它的依赖项,但感觉我不应该这样做。

我是否可以让 Ninject 在单例范围内急切实例化类,即使没有其他东西依赖它?

【问题讨论】:

  • 当引导(应用程序启动)时,只需调用一次你的单例,它就会被创建。
  • @QualityCatalyst 是的,但要做到这一点,我必须明确提供我所有的单例依赖项。它所有的依赖都绑定在 Ninject 中,这使得 Ninject 很容易自己构建它。
  • 如果你有一个带有注入(或构造函数)的属性,它将被自动实例化,你不必自己解决依赖关系。
  • @QualityCatalyst 我不明白我会怎么做?
  • Inject 属性放在属性上方,如下所示:[Inject]。或者将类(或相关接口)传递给您在引导后直接调用的构造函数。

标签: c# singleton ninject


【解决方案1】:

如果你不要求它自己实例化某些东西,你就不能让 ninject 实例化东西。 简单的方法是让 ninject 在组合根处实例化事物:

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>();

其实还有一种选择,虽然不一样,但是可以达到类似的效果。它要求至少有一个其他服务足够快地实例化:Ninject.Extensions.DependencyCreation。 它的工作原理是这样的:

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

为什么

Ninject 不像其他一些容器(例如 Autofac)不是分阶段“构建”的。没有先创建绑定,然后创建内核以使用它们的概念。以下是完全合法的:

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

所以 ninject 不可能知道何时您希望单例实例化。用autofacit's clear and easy

var containerBuilder = new ContainerBuilder();

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

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

【讨论】:

  • 像这样的kernel.Resolve&lt;IFooSingleton&gt;();真的有任何影响吗,因为实例只在迭代时解决?
  • @JanGassen 我不太明白你的问题。 kernel.Resolve&lt;...&gt; ,使用单例,它第一次创建一个实例,并为每次进一步调用返回“旧”实例。所以第一次调用的效果是实例化 + 缓存(确保在释放内核之前它不会被垃圾收集)。
  • 我认为IResolutionRoot.Resolve 返回一个惰性求值的枚举,这意味着迭代项只会在迭代时创建。如果这是真的,仅仅调用Resolve 可能不会创建一个实例。通过查看代码,这可能取决于配置的绑定。
  • @JanGassen 一些 Resolve 重载可能。但是Resolve&lt;IFooSingleton&gt; 不会返回IEnumerable。它返回一个IFooSingleton。不能进行惰性求值。
【解决方案2】:

来自 Java 中的 Guice,我非常怀念渴望单例的模式。它们在例如模块充当插件的情况下很有用。如果您想象服务是由配置中指定的模块组装而成的,那么您可能会看到一个问题,即在启动应用程序时还要尝试指定该模块需要自动实例化的内容。

对我来说,模块是定义应用程序组合的地方,将急切的单例分隔到代码中的另一个位置感觉更笨拙且不太直观。

无论如何,我已经能够非常轻松地将其实现为 Ninject 之上的一个层,代码如下:

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.
    }
}

创建内核后,添加一行:

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

你在这样的模块中使用它:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-12-12
    • 2021-08-10
    • 2021-12-08
    • 1970-01-01
    • 2018-01-15
    • 1970-01-01
    • 2023-04-05
    • 1970-01-01
    相关资源
    最近更新 更多