使用AutoFac基于构造函数参数名称传递不同的实例

时间:2017-10-27 08:49:23

标签: c# dependency-injection inversion-of-control autofac

我有这样的课程(WebApi 2 Controller):

class DatabaseAnalysisController (IDatabase databaseOne, 
    IDatabase databaseTwo) : ApiController

这样的课程:

class Database : IDatabase

现在我有两个实例:

Database databaseOne = new Database("one");
Database databaseTwo = new Database("two");

我不希望DatabaseAnalysis对Autofac有任何依赖。但是我想使用Autofac来根据构造函数的参数名称进行注入。

我正在使用Autofac.WebApi注册我的控制器。

builder.RegisterApiControllers(Assembly.GetExecutingAssembly());

但之后这不起作用,因为databaseOnedatabaseTwo都是IDatabase

builder.RegisterInstance(new Database("one")).As<IDatabase>();

Autofac是否可以将databaseOne注入databaseOne的构造函数DatabaseAnalysis(以及databaseTwo中的databaseTwo)?

如果是这样,怎么样? :P

2 个答案:

答案 0 :(得分:3)

由于您有多个具有相同接口和类型的实例 - 您需要使用名称注册它们:

builder.RegisterInstance(new Database("one")).Named<IDatabase>("databaseOne");
builder.RegisterInstance(new Database("two")).Named<IDatabase>("databaseTwo");

然后您可以注册DatabaseAnalysisController,如下所示:

builder.RegisterType<DatabaseAnalysis>().WithParameter(
    (pi, ctx) => pi.ParameterType == typeof(IDatabase), 
    (pi, ctx) => ctx.ResolveNamed<IDatabase>(pi.Name));

您可以使用扩展方法执行此类注册:

public static class AutofacExtensions {
    public static IRegistrationBuilder<TLimit, TReflectionActivatorData, TStyle> WithNamedParameter<TLimit, TReflectionActivatorData, TStyle>(this IRegistrationBuilder<TLimit, TReflectionActivatorData, TStyle> registration, Type targetType)
        where TReflectionActivatorData : ReflectionActivatorData {
        return registration.WithParameter(
            (pi, ctx) => pi.ParameterType == targetType,
            (pi, ctx) => ctx.ResolveNamed(pi.Name, targetType));
    }
}

要为所有控制器执行此操作,请执行以下操作:

builder.RegisterApiControllers().WithNamedParameter(typeof(IDatabase));

答案 1 :(得分:2)

不是使注入的依赖项依赖于构造函数参数名称,处理这些依赖项的另一种方法就是使接口通用。然后,通用结束类型(T)将确定要注入的IDatabase<T>

interface IDatabase<T> {}
class Database1 : IDatabase<Database1>
class Database2 : IDatabase<Database2>

在您的服务中:

class SomeService1
{
    public SomeService1(IDatabase<Database1> database)
    {
        // set database to private variable
    }
}

class SomeService2
{
    public SomeService2(IDatabase<Database2> database)
    {
        // set database to private variable
    }
}

可以注册如下:

builder.RegisterType<IDatabase<Database1>>().As<Database1>();
builder.RegisterType<IDatabase<Database2>>().As<Database2>();

虽然实现之间共享IDatabase<T>,但.NET类型系统(以及Autofac)将IDatabase<Database1>视为与IDatabase<Database2>完全不同的类型。这意味着类型会自动放到正确的位置,但这也意味着您不能只在应用程序中交换一个类型。因此,这是否适合您取决于您​​的具体用例。