dev*_*ife 13 .net lazy-loading ninject
我正在评估ninject2,但似乎无法弄清楚如何通过内核进行延迟加载.
从我所看到的那种失败了使用[Inject]属性的目的.是否可以使用InjectAttribute但延迟加载?每次我实例化一个对象时,我都讨厌强制完整构建一个对象图.
为了说明,我真的只是对性能感到好奇.
Aar*_*ght 19
更新:我的原始答案是在.NET Framework 4发布之前编写的(另外Lazy<T>),另一个答案虽然稍微更新,但现在仍然有点过时.我将在下面留下我的原始答案,以防任何人被困在旧版本上,但不会建议使用最新版本的Ninject或.NET.
Ninject Factory Extension是这样做的现代方式.它会自动连接任何参数或属性.您不需要单独的绑定 - 只需按常规方式设置服务,扩展程序处理其余部分.
仅供参考,相同的扩展也可以连接自定义工厂接口或Func<T>参数.与它们的不同之处在于它们每次都会创建一个新实例,所以它实际上是一个工厂,而不仅仅是懒惰的实例化.只是指出这一点,因为文档并不完全清楚.
作为一项规则,"对象图的完整构造"不应该那么昂贵,并且如果一个类被注入了它可能不使用的依赖项,那么这可能是一个好的迹象,即该类有太多的责任.
这不是Ninject本身的限制 - 如果你考虑它,除非(a)被注入的依赖本身是一个惰性加载器,例如Lazy<T>类中的,否则它实际上不可能具有"延迟依赖" . NET 4,或(b)所有依赖项的属性和方法都使用延迟实例化. 必须在那里注入一些东西.
您可以通过使用相对容易地完成(a) 提供者界面方法绑定(编辑: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;
}
}
}
Run Code Online (Sandbox Code Playgroud)
然后你可以绑定整个惰性接口 - 所以只需正常绑定接口:
Bind<ISomeService>().To<SomeService>();
Bind<IOtherService>().To<OtherService>();
Run Code Online (Sandbox Code Playgroud)
并使用开放泛型将惰性接口绑定到lambda方法:
Bind(typeof(ILazy<>)).ToMethod(ctx =>
{
var targetType = typeof(LazyLoader<>).MakeGenericType(ctx.GenericArguments);
return ctx.Kernel.Get(targetType);
});
Run Code Online (Sandbox Code Playgroud)
之后,您可以引入延迟依赖参数/属性:
public class MyClass
{
[Inject]
public MyClass(ILazy<ISomeService> lazyService) { ... }
}
Run Code Online (Sandbox Code Playgroud)
这不会使整个操作懒惰- Ninject仍然有实际创建的一个实例LazyLoader,但任何第二级的依赖性ISomeService不会被加载,直到MyClass支票Value的那个lazyService.
明显的缺点是ILazy<T>没有实现T自己,所以MyClass如果你想要延迟加载的好处,必须实际编写接受延迟依赖.然而,如果创建一些特定的依赖是非常昂贵的,这将是一个很好的选择.我很确定你会遇到任何形式的DI,任何库的问题.
据我所知,(b)没有编写代码山的唯一方法是使用像Castle DynamicProxy这样的代理生成器,或使用Ninject Interception Extension注册动态拦截器.这将是相当复杂的,我不认为你想要走这条路线,除非你必须这样做.
Ed *_*pel 17
有一种更简单的方法:
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>());
}
}
Run Code Online (Sandbox Code Playgroud)