[英]C#: How to bind another implementation of my interface at runtime using ninject?
[英]Ninject dynamically bind to implementation
關於SO的幾個問題很相似,但不完全是我要尋找的問題。 我想基於運行時條件進行Ninject綁定,這在啟動時並不為人所知。 關於用於動態綁定的SO的其他問題圍繞基於配置文件等的綁定-我需要在處理特定實體的數據時根據數據庫值有條件地進行綁定。 例如,
public class Partner
{
public int PartnerID { get; set; }
public string ExportImplementationAssembly { get; set; }
}
public interface IExport
{
void ExportData(DataTable data);
}
在其他地方,我有2個實現IExport的dll。
public PartnerAExport : IExport
{
private readonly _db;
public PartnerAExport(PAEntities db)
{
_db = db;
}
public void ExportData(DataTable data)
{
// export parter A's data...
}
}
然后是伙伴B;
public PartnerBExport : IExport
{
private readonly _db;
public PartnerBExport(PAEntities db)
{
_db = db;
}
public void ExportData(DataTable data)
{
// export parter B's data...
}
}
當前的Ninject綁定是;
public class NinjectWebBindingsModule : NinjectModule
{
public override void Load()
{
Bind<PADBEntities>().ToSelf();
Kernel.Bind(s => s.FromAssembliesMatching("PartnerAdapter.*.dll")
.SelectAllClasses()
.BindDefaultInterfaces()
);
}
}
因此,如何設置綁定以使我可以做到;
foreach (Partner partner in _db.Partners)
{
// pseudocode...
IExport exportModule = ninject.Resolve<IExport>(partner.ExportImplementationAssembly);
exportModule.ExportData(_db.GetPartnerData(partner.PartnerID));
}
這可能嗎? 看起來應該是這樣,但是我不知道該怎么做。 上面的現有綁定配置可用於靜態綁定,但我需要在運行時可以解決的問題。 以上可能嗎,還是我只需要繞過Ninject並使用老式的反射來加載插件? 如果是這樣,我如何使用該方法通過Ninject解析任何構造函數參數,就像靜態綁定的對象一樣?
更新:我已經使用BatteryBackupUnit
的解決方案更新了我的代碼,現在我有了以下內容:
Bind<PADBEntities>().ToSelf().InRequestScope();
Kernel.Bind(s => s.FromAssembliesMatching("PartnerAdapter.*.dll")
.SelectAllClasses()
.BindDefaultInterfaces()
.Configure(c => c.InRequestScope())
);
Kernel.Bind(s => s.FromAssembliesMatching("PartnerAdapter.Modules.*.dll")
.SelectAllClasses()
.InheritedFrom<IExportService>()
.BindSelection((type, baseTypes) => new[] { typeof(IExportService) })
);
Kernel.Bind<IExportServiceDictionary>().To<ExportServiceDictionary>().InSingletonScope();
ExportServiceDictionary dictionary = KernelInstance.Get<ExportServiceDictionary>();
在2個測試模塊中實例化導出實現可以正常工作,並且實例化PADBEntites
上下文也很好。 但是,我的服務層中的所有其他綁定現在不再對系統的其余部分起作用。 同樣,如果將PADBEntities
變量/ ctor參數更改為ISomeEntityService組件,則無法綁定導出層。 似乎我缺少配置綁定以完成這項工作的最后一步。 有什么想法嗎?
錯誤:“激活ISomeEntityService時出錯。沒有匹配的綁定可用,並且類型不可自綁定”
更新2 :最終我使用BatteryBackupUnit
的解決方案進行了一些反復試驗,盡管我對籃球跳得不太滿意。 任何其他更簡潔的解決方案都歡迎。
我更改了原來的約定綁定;
Kernel.Bind(s => s.FromAssembliesMatching("PartnerAdapter.*.dll")
.SelectAllClasses()
.BindDefaultInterfaces()
);
更冗長和明確;
Bind<IActionService>().To<ActionService>().InRequestScope();
Bind<IAuditedActionService>().To<AuditedActionService>().InRequestScope();
Bind<ICallService>().To<CallService>().InRequestScope();
Bind<ICompanyService>().To<CompanyService>().InRequestScope();
//...and so on for 30+ lines
不是我最喜歡的解決方案,但它適用於基於顯式和基於約定的綁定,但不適用於兩個約定。 誰能看到我在綁定時出錯了嗎?
更新3 :忽略更新2中綁定的問題。似乎我在Ninject中發現了一個錯誤,該錯誤與在引用的庫中具有多個綁定模塊有關。 即使從未通過斷點命中,對模塊A的更改也將使用其他模塊B明確地中斷項目。
重要的是要注意,雖然實際的“條件匹配”是運行時條件,但實際上您實際上預先知道了可能的匹配集(至少在構建容器時是在啟動時)-這可以通過使用約定來證明。 這就是條件/上下文綁定的含義(在Ninject WIKI中進行了描述,並涵蓋了幾個問題)。 因此,您實際上不需要在任意運行時進行綁定,而只需要在任意時間進行解析/選擇(解析實際上可以提前完成=>盡早失敗)。
這是一個可能的解決方案,其特點是:
IExport
) IExport
。
internal interface IExportDictionary
{
IExport Get(string key);
}
internal class ExportDictionary : IExportDictionary
{
private readonly Dictionary<string, IExport> dictionary;
public ExportDictionary(IEnumerable<IExport> exports)
{
dictionary = new Dictionary<string, IExport>();
foreach (IExport export in exports)
{
dictionary.Add(export.GetType().Assembly.FullName, export);
}
}
public IExport Get(string key)
{
return dictionary[key];
}
}
成分根:
// this is just going to bind the IExports.
// If other types need to be bound, go ahead and adapt this or add other bindings.
kernel.Bind(s => s.FromAssembliesMatching("PartnerAdapter.*.dll")
.SelectAllClasses()
.InheritedFrom<IExport>()
.BindSelection((type, baseTypes) => new[] { typeof(IExport) }));
kernel.Bind<IExportDictionary>().To<ExportDictionary>().InSingletonScope();
// create the dictionary immediately after the kernel is initialized.
// do this in the "composition root".
// why? creation of the dictionary will lead to creation of all `IExport`
// that means if one cannot be created because a binding is missing (or such)
// it will fail here (=> fail early).
var exportDictionary = kernel.Get<IExportDictionary>();
現在,可以將IExportDictionary
注入到任何組件中,並像“ required”一樣使用:
foreach (Partner partner in _db.Partners)
{
// pseudocode...
IExport exportModule = exportDictionary.Get(partner.ExportImplementationAssembly);
exportModule.ExportData(_db.GetPartnerData(partner.PartnerID));
}
我想基於運行時條件進行Ninject綁定,這在啟動時並不為人所知。
防止在構建對象圖時做出運行時決策。 這會使您的配置復雜化,並使您的配置難以驗證。 理想情況下,對象圖應該是固定的,並且在運行時不應改變形狀。
取而代之的是,通過將其移到IExport
的代理類中,在...運行時決策運行時。 此類代理的實際外觀取決於您的實際情況,但以下是一個示例:
public sealed class ExportProxy : IExport
{
private readonly IExport export1;
private readonly IExport export2;
public ExportProxy(IExport export1, IExport export2) {
this.export1 = export1;
this.export2 = export2;
}
void IExport.ExportData(Partner partner) {
IExport exportModule = GetExportModule(partner.ExportImplementationAssembly);
exportModule.ExportData(partner);
}
private IExport GetExportModule(ImplementationAssembly assembly) {
if (assembly.Name = "A") return this.export1;
if (assembly.Name = "B") return this.export2;
throw new InvalidOperationException(assembly.Name);
}
}
也許您正在處理一組動態確定的程序集。 在這種情況下,您可以為代理提供導出提供程序委托。 例如:
public sealed class ExportProxy : IExport
{
private readonly Func<ImplementationAssembly, IExport> exportProvider;
public ExportProxy(Func<ImplementationAssembly, IExport> exportProvider) {
this.exportProvider = exportProvider;
}
void IExport.ExportData(Partner partner) {
IExport exportModule = this.exportProvider(partner.ExportImplementationAssembly);
exportModule.ExportData(partner);
}
}
通過為代理提供Func<,>
您仍然可以在注冊ExportProxy
(組合根)的位置進行決策,在該位置可以查詢系統的程序集。 這樣,您可以在容器中IExport
注冊IExport
實現,從而提高配置的可驗證性。 如果你注冊的所有IExport
使用一鍵實現,你可以做了以下簡單的注冊ExportProxy
kernel.Bind<IExport>().ToInstance(new ExportProxy(
assembly => kernel.Get<IExport>(assembly.Name)));
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.