我正在評估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