使用Autofac解析构造函数中的通用IEnumerable

时间:2016-04-21 17:10:28

标签: c# ienumerable autofac abstract-factory

所以我有一个抽象的工厂类,我的想法是在这个工厂的构造函数中,我将从 autofac 传递我感兴趣的所有注册服务的IEnumerable在这个工厂中有一个传递参数的方法,代码将遍历IEnumerable并返回匹配的服务。简单的馅饼,抽象的工厂模式。

问题是,我最近在工厂返回的界面中引入了泛型类型,我不知道如何编写构造函数,因此 autofac 知道仍然返回所有已注册的服务,基本上忽略了泛型。

一些代码:

我的界面:

public interface IRunnerServices<in T>
{
    void Create(T entity);
}

界面的一些实现

public class RunnerServiceA : IRunnerServices<A>
{
    public void Create(A entity);
}

public class RunnerServiceB : IRunnerServices<B>
{
    public void Create(B entity);
}

public class RunnerServiceC : IRunnerServices<C>
{
    public void Create(C entity);
}

// Many, many more...

在我的作品根目录中,我注册了这样的服务

builder.RegisterType<RunnerServicesA>().As<IRunnerServices<A>>();
builder.RegisterType<RunnerServicesB>().As<IRunnerServices<B>>();
builder.RegisterType<RunnerServicesC>().As<IRunnerServices<C>>();

就这样你知道,A,B,C都是从同一个基类继承的

public class A : LetterBase
{ }

public class B : LetterBase
{ }

public class C : LetterBase
{ }

现在,我的抽象工厂

public class RunnerServicesFactory
{
    private readonly IEnumerable<IRunnerServices<????>> _candidates;
    public RunnerServicesFactory(IEnumerable<IRunnerServices<????>> candidates)
    {
        _candidates = candidates;
    }

    public IRunnerServices<????> Create(int discriminator)
    {
        return _candidates.First(c => c.Discriminator == discriminator);
    }
}

问题在于我在上面的构造函数和其他声明中提供了什么?让autofac知道它应该传递这个通用的所有注册服务吗?

我已经尝试了这个,但没有运气,因为 autofac 什么都不通过

IEnumerable<IRunnerServices<LetterBase>>

1 个答案:

答案 0 :(得分:0)

为了将RunnerServiceA实施转换为IRunnerService<LetterBase>,您需要使用{generic}关键字修饰符进行IRunnerService<T>协变,这是不可能的,因为您有一种方法可以接受T 1}}参数。

.net不允许您将RunnerServiceA投射到IRunnerService<LetterBase>,您可以尝试执行以下简单代码:

var runnerService = (IRunnerServices<LetterBase>)new RunnerServiceA();
// Throws an InvalidCastException 

一种可能的解决方案是像这样制作IRunnerService<T>协变:

public interface IRunnerServices<out T>
    where T : LetterBase
{
    void Create(LetterBase entity);
}

通过这样做,您可以将RunnerServiceA投射到IRunnerService<LetterBase>并执行此代码:

IEnumerable<IRunnerServices<LetterBase>> runners = new IRunnerServices<LetterBase>[] {
                                                        new RunnerServiceA(),
                                                        new RunnerServiceB()
                                                    };

如果IEnumerable<IRunnerServices<LetterBase>>注册为RunnerService

,那么 Autofac 将能够解析IRunnerServices<LetterBase>

顺便说一句,让我想一想IIndex<TKey, T>

public class RunnerServicesFactory
{
    private readonly IIndex<Char, IRunnerServices<LetterBase>> _candidates;
    public RunnerServicesFactory(IIndex<Char, IRunnerServices<LetterBase>> candidates)
    {
        _candidates = candidates;
    }

    public IRunnerServices<LetterBase> Create(Char discriminator)
    {
        return _candidates[discriminator];
    }
}

并且注册将是这样的:

builder.RegisterType<RunnerServiceA>().Keyed<IRunnerServices<LetterBase>>('A');
builder.RegisterType<RunnerServiceB>().Keyed<IRunnerServices<LetterBase>>('B');
builder.RegisterType<RunnerServiceC>().Keyed<IRunnerServices<LetterBase>>('C');