簡體   English   中英

Autofac 注入通用接口的 IEnumerable

[英]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是一個復合接口,其中包含DbContextAppSettings以及我希望在每個控制器中使用的其他一些常用功能。

在大多數情況下,這已經足夠了。 然而,有時為了支持 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;
    }
}

出於顯而易見的原因,當前的構造函數無法做到這一點。

但是有沒有辦法通過IIndexIEnumerable獲得我想要的字典,我可以構建<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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM