我正在评估ninject2,但似乎无法弄清楚如何通过内核进行延迟加载。懒惰加载Ninject
从我所看到的那种失败了使用[注入]属性的目的。 是否可以使用InjectAttribute,但获得延迟加载?每次我实例化一个对象时,我都不想强制完成对象图的构造。
要指定,我真的只是好奇的表现。
我正在评估ninject2,但似乎无法弄清楚如何通过内核进行延迟加载。懒惰加载Ninject
从我所看到的那种失败了使用[注入]属性的目的。 是否可以使用InjectAttribute,但获得延迟加载?每次我实例化一个对象时,我都不想强制完成对象图的构造。
要指定,我真的只是好奇的表现。
更新:我原来的答复是.NET Framework 4的发布(连同Lazy<T>
)之前写的,对方的回答,而稍微跟上时代的,现在仍然是一个有点过时。我在下面留下我的原始答案,以防有人被困在旧版本中,但不会建议使用最新版本的Ninject或.NET。
Ninject Factory Extension是这样做的现代方式。它会自动连接任何参数或属性。您不需要单独的绑定 - 只需按照常规方式设置您的服务,然后扩展就可以处理剩下的事情。
仅供参考,同样的扩展也可以连接自定义工厂接口或Func<T>
参数。与他们不同的是,他们每次都会创建一个新实例,所以它实际上是一个工厂,而不仅仅是懒惰的实例化。只是指出这是因为文档不完全清楚它。
作为一项规则,“对象图的完整建筑”不应该是昂贵的,如果一个类被与依赖注入,它可能无法使用,它可能是一个很好的迹象,表明类有责任太多。如果你仔细想想,除非(a)被注入的依赖本身就是一个惰性的加载器,比如说,它并不是真的可能存在“惰性依赖” .NET 4中的Lazy<T>
类,或(b)所有依赖项的属性和方法都使用延迟实例化。 东西必须注入那里。
你可以完成(一)相对容易地通过使用提供商接口的方法结合(编辑:Ninject不支持与提供商绑定开放仿制药)和结合的开放式泛型类型。假设你没有。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>();
,并绑定懒接口使用开放式泛型拉姆达方法:
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
的实例,但ISomeService
任何秒级依赖性不会被加载,直到MyClass
检查那lazyService
的Value
。
明显的缺点是ILazy<T>
本身没有实现T
,因此MyClass
必须写入接受懒惰依赖关系,如果你想延迟加载的好处。不过,如果创建某种特定的依赖关系的代价非常昂贵,这将是一个不错的选择。我很肯定你会在任何形式的DI,任何库上遇到这个问题。
据我所知,只有这样,才能做到(B),而无需编写代码的山会使用代理生成像Castle DynamicProxy,或使用Ninject Interception Extension注册一个动态的拦截器。这将是非常复杂的,我不认为你会想要去这条路线,除非你必须。
还有就是做这个简单的方法:
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>());
}
}
这对于System.Lazy <> – 2013-09-26 21:07:10
另外,使用类中的属性应该使用Lazy
现在好了,我很困惑。花了最后一小时试图让这个工作,我读到“ToProvider目前不支持开放式泛型”。就我所知,这种方法是无效的。但它已被提升并被接受为答案。 – fearofawhackplanet 2011-05-24 13:17:03
@fear:这可能是我的错,尽管我可以发誓Ninject 2.x确实支持这一点。如果无法让提供者版本正常工作,那么只需绑定到lambda方法 - 除了使用'IContext.GenericArguments'集合来获取类型参数外,几乎完全相同。 – Aaronaught 2011-05-24 14:39:53
@Aaronaught:我使用的是Ninject 2.2,它不适合我。我使用了'Bind(typeof(ILazy <>))。ToMethod(ctx =>(ctx.Kernel.Get(typeof(LazyProvider <>)。MakeGenericType(ctx.GenericArguments))作为IProvider).Create(ctx) );'我认为你绑定到lambda是什么意思?它似乎在做这项工作,但不太可读,显然不检查通用参数是否有效。在这里看到帖子:http://groups.google.com/group/ninject/browse_thread/thread/b2df51230bed8195?pli=1 – fearofawhackplanet 2011-05-25 13:29:29