【问题标题】:Lazy Loading with Ninject使用 Ninject 延迟加载
【发布时间】:2011-02-02 01:08:01
【问题描述】:

我正在评估 ninject2,但似乎无法弄清楚除了通过内核之外如何进行延迟加载。

据我所知,这违背了使用 [Inject] 属性的目的。 是否可以使用 InjectAttribute 但延迟加载?每次实例化一个对象时,我都不想强制构建一个对象图。

具体来说,我真的只是对性能感到好奇。

【问题讨论】:

    标签: .net lazy-loading ninject


    【解决方案1】:

    更新:我的原始答案是在 .NET Framework 4 发布之前编写的(连同Lazy<T>),而另一个答案虽然更新了一些,但仍然是现在有点过时了。我将在下面留下我原来的答案,以防有人卡在旧版本上,但不建议将其与最新版本的 Ninject 或 .NET 一起使用。

    Ninject Factory Extension 是执行此操作的现代方式。它会自动连接任何参数或属性。您不需要为此单独绑定 - 只需以通常的方式设置您的服务,然后扩展处理其余部分。

    仅供参考,同样的扩展也可以连接自定义工厂接口,或Func<T> 参数。与这些不同的是,它们每次都会创建一个新实例,因此它实际上是一个工厂,而不仅仅是惰性实例化。只是指出这一点,因为文档并不完全清楚。


    作为一项规则,“对象图的完整构建”不应该那么昂贵,如果一个类被注入了它可能不会使用的依赖项,这可能是一个好兆头,表明该类有太多的责任。

    这并不是 Ninject 本身的真正限制——如果你仔细想想,它实际上不可能有一个“惰性依赖”,除非 (a) 被注入的依赖本身就是一个惰性加载器,例如Lazy<T> .NET 4 中的类,或 (b) 所有依赖项的属性和方法都使用延迟实例化。 一些东西必须注入那里。

    您可以通过使用提供者接口方法绑定(ed:Ninject 不支持具有提供者绑定的开放泛型)相对轻松地完成(a)并绑定打开泛型类型。假设您没有 .NET 4,则必须自己创建接口和实现:

    public interface ILazy<T>
    {
        T Value { get; }
    }
    
    public class LazyLoader<T> : ILazy<T>
    {
        private bool isLoaded = false;
        private T instance;
        private Func<T> loader;
    
        public LazyLoader(Func<T> loader)
        {
            if (loader == null)
                throw new ArgumentNullException("loader");
            this.loader = loader;
        }
    
        public T Value
        {
            get
            {
                if (!isLoaded)
                {
                    instance = loader();
                    isLoaded = true;
                }
                return instance;
            }
        }
    }
    

    然后你就可以绑定整个惰性接口了——所以只要像往常一样绑定接口:

    Bind<ISomeService>().To<SomeService>();
    Bind<IOtherService>().To<OtherService>();
    

    并使用开放泛型将惰性接口绑定到 lambda 方法:

    Bind(typeof(ILazy<>)).ToMethod(ctx =>
    {
        var targetType = typeof(LazyLoader<>).MakeGenericType(ctx.GenericArguments);
        return ctx.Kernel.Get(targetType);
    });
    

    之后,你可以引入惰性依赖参数/属性:

    public class MyClass
    {
        [Inject]
        public MyClass(ILazy<ISomeService> lazyService) { ... }
    }
    

    这不会使 整个 操作变得懒惰 - Ninject 仍然必须实际创建 LazyLoader 的实例,但 @ 的任何 二级 依赖项在MyClass 检查ValuelazyService 之前,不会加载987654335@。

    明显的缺点是ILazy&lt;T&gt; 本身并没有实现T,所以如果你想要延迟加载的好处,就必须实际编写MyClass 以接受延迟依赖。然而,如果创建某些特定依赖项的成本非常高,这将是一个不错的选择。我很确定您会在任何形式的 DI、任何库中遇到这个问题。


    据我所知,在不编写大量代码的情况下执行 (b) 的唯一方法是使用像 Castle DynamicProxy 这样的代理生成器,或者使用 Ninject Interception Extension 注册动态拦截器。这会很复杂,我认为你不会想走这条路,除非你必须这样做。

    【讨论】:

    • 现在我很困惑。花了最后一个小时试图让它工作,我读到“ToProvider 目前不支持开放泛型”。因此,据我所知,这种方法无效。然而,它已被赞成并被接受为答案。
    • @fear:这可能是我的错误,尽管我可以发誓 Ninject 2.x 确实支持这一点。如果您无法使提供程序版本正常工作,则只需绑定到 lambda 方法 - 几乎相同,只是您使用 IContext.GenericArguments 集合获取类型参数。
    • @Aaronaught:我使用的是 Ninject 2.2,但它对我不起作用。我使用Bind(typeof(ILazy&lt;&gt;)).ToMethod(ctx =&gt; (ctx.Kernel.Get(typeof(LazyProvider&lt;&gt;).MakeGenericType(ctx.GenericArguments)) as IProvider).Create(ctx)); 让它工作,我认为这就是绑定到 lambda 的意思?它似乎在做这项工作,但不是很易读,显然没有检查通用参数是否有效。请参阅此处的帖子:groups.google.com/group/ninject/browse_thread/thread/…
    • @fear:如果您要绑定到一个方法,那么使用提供程序没有任何意义 - 只需设置您的 lambda 以直接调用构造函数或调用 Kernel.Get 方法在目标类型上。 (你想知道一些非常有趣的事情——你链接到的那个帖子是你真正发布的,看看日期,显然我从来没有尝试过使用开放的通用提供程序,我只是认为它会起作用。)
    • 我想指出你现在可以使用ninject.extensions.factorygithub.com/ninject/ninject.extensions.factory/wiki/Lazy自动绑定惰性类型
    【解决方案2】:

    有一个更简单的方法来做到这一点:

    public class Module : NinjectModule
    {
        public override void Load()
        {
            Bind(typeof(Lazy<>)).ToMethod(ctx => 
                    GetType()
                        .GetMethod("GetLazyProvider", BindingFlags.Instance | BindingFlags.NonPublic)
                        .MakeGenericMethod(ctx.GenericArguments[0])
                        .Invoke(this, new object[] { ctx.Kernel }));
        }
    
        protected Lazy<T> GetLazyProvider<T>(IKernel kernel)
        {
            return new Lazy<T>(() => kernel.Get<T>());
        }
    }
    

    【讨论】:

    • 另外,use类中的属性应该使用Lazy而不是T。
    猜你喜欢
    • 2019-05-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-05
    • 2014-08-12
    • 2014-01-19
    • 2017-02-03
    • 1970-01-01
    相关资源
    最近更新 更多