[英]Autofac inject IEnumerable of generic interfaces
所以,我認識到 StackOverflow 充斥着這個問題,但通常沒有人解釋他們為什么要這樣做。 我希望通過這樣做一個更好的答案浮到頂部。
這家伙做了一些接近我想要的東西: 從 Autofac 容器解析通用接口的 IEnumerable但不完全是。
我承認IGenericInterface<ObjectA>
不等同於IGenericInterface<ObjectB>
。
話雖如此,我很樂意將每個IService<T>
注入到單個構造函數中,以便我可以構建查找。 老實說,我想構建一個與DbContext.Set<T>
非常相似的實現
我的問題中的幾個關鍵參與者。
public interface IService<TMessage> where TMessage: IContractDTO
{
IQueryable<TMessage> BuildProjection();
}
目前我一次注射這些
public class SomeController : BaseODataController<SomeEntityDTO>
{
public SomeController(IControllerServiceContext context, IService<SomeEntityDTO> service)
: base(context, service)
{
}
//DO STUFF
}
IControllerServiceContext
是一個復合接口,其中包含DbContext
、 AppSettings
以及我希望在每個控制器中使用的其他一些常用功能。
在大多數情況下,這已經足夠了。 然而,有時為了支持 EntityA 的邏輯,我可能需要對 B 進行快速查找。我寧願使用IService<SomeEntityB>
的BuildProjections()
實現而不是在控制器 A 中構建冗余。如果我注入每個例如,我有一些會以這種方式成為 8 或 9 參數構造函數
所以我開始思考如果我能夠將IServiceLookup
添加到IControllerServiceContext
那么我將擁有我需要的一切。
我開始走這條路:
public class ServiceLookup<TContract>: IServiceLookup where TContract: BaseClass, IContractDTO
{
public ServiceLookup(IEnumerable<IService<TContract>> services)
{
//Build _Services
}
private readonly IDictionary<Type, object> _services;
public IService<TMessage> Service<TMessage>() where TMessage : class, IContractDTO
{
return (IService<TMessage>)(GetService(typeof(TMessage)));
}
private object GetService(Type type)
{
_services.TryGetValue(type, out var service);
return service;
}
}
出於顯而易見的原因,當前的構造函數無法做到這一點。
但是有沒有辦法通過IIndex
或IEnumerable
獲得我想要的字典,我可以構建<type, object>
字典,其中 object 是我的各種IService<T>
?
服務查找是基於讀取DbContext
代碼並簡化DbContext
的邏輯而DbContext.Set
,它也是由IDictionary<Type, object>
驅動的。
如果通過某種解析器參數我可以獲得所有IService<T>
s,提取T
類型,並將它們添加到該列表中,我就可以參加比賽了。
編輯:我知道我可以將構建每個服務所需的參數注入ServiceLookup
並手動構建我的列表,這甚至可能是更好的答案......健壯,我從根本上很好奇是否可能
Edit2 :我希望能夠在實現中做的事情看起來像這樣:
public SomeController(IControllerServiceContext context, IServiceLookup lookup)
: base(context, service)
{
public SomeMethod() {
var x = lookup.Service<EntityOneDTO>().BuildProjections().FirstOrDefault();
var y = lookup.Service<EntityTwoDTO>().BuildProjections().FirstOrDefault();
//Do Logic that requires both EntityOne and EntityTwo
}
}
假設您有以下類型:
public class MessageA { }
public class MessageB { }
public interface IService<TMessage> { }
public class ServiceA : IService<MessageA> { }
public class ServiceB : IService<MessageB> { }
你有一個控制器,你想根據你想要的任何東西獲得一個IService<MessageA>
。
第一個解決方案是注入您可能需要的所有IService
:
public class Controller
{
public Controller(IService<MessageA> serviceA, IService<MessageB> serviceB)
{
this._serviceA = serviceA;
this._serviceB = serviceB;
}
private readonly IService<MessageA> _serviceA;
private readonly IService<MessageB> _serviceB;
public void Do()
{
IService<MessageA> serviceA = this._serviceA;
}
}
如果您的消息類型很少,它會起作用,但如果您的消息類型很少,則不起作用。
因為IService<T>
是通用的, Do
有沒有簡單的方法來兩個世界混合。 第一個解決方案是引入一個非通用接口
public interface IService { }
public interface IService<TMessage> : IService { }
並像這樣注冊這些類型:
builder.RegisterType<ServiceA>().As<IService<MessageA>>().As<IService>();
builder.RegisterType<ServiceB>().As<IService<MessageB>>().As<IService>();
然后你可以有一個IEnumerable<IService>
。 類似的東西:
public interface IServiceLookup
{
IService<TMessage> Get<TMessage>();
}
public class ServiceLookup : IServiceLookup
{
public ServiceLookup(IEnumerable<IService> services)
{
this._services = services
.ToDictionary(s => s.GetType()
.GetInterfaces()
.First(i => i.IsGenericType
&& i.GetGenericTypeDefinition() == typeof(IService<>))
.GetGenericArguments()[0],
s => s);
}
private readonly Dictionary<Type, IService> _services;
public IService<TMessage> Get<TMessage>()
{
// you should check for type missing, etc.
return (IService<TMessage>)this._services[typeof(TMessage)];
}
}
然后在控制器中注入IServiceLookup
。
此解決方案的缺點是它會創建所有 IService 的實例,以避免您可以注入IEnumerable<Func<IService>>
另一種解決方案是將IComponentContext
注入ServiceLookup
。 ComponentContext
是一種Autofac類型,您可以從中解析服務。
public class ServiceLookup : IServiceLookup
{
public ServiceLookup(IComponentContext context)
{
this._context = context;
}
private readonly IComponentContext _context;
public IService<TMessage> Get<TMessage>()
{
return this._context.Resolve<IService<TMessage>>();
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.