在Autofac中使用反射注册多个命名类型

时间:2019-05-02 17:22:38

标签: c# autofac

我大约有100个存储库类,它们都实现相同的接口。它们都有共同的依赖性。

我已经用不同的名称注册了这个常见依赖的两个版本。

我想使用命名的依赖项第二次注册我所有的存储库类。

这基本上是我的原始注册内容:

builder.RegisterAssemblyTypes(typeof(Repository<>).Assembly)
    .WithParameter(
        new ResolvedParameter(
            (pi, ctx) => pi.ParameterType == typeof(IClientContext),
            (pi, ctx) => ctx.ResolveNamed<IClientContext>(RegistrationKeys.Published)))
    .AsImplementedInterfaces();

因此,如果我想使用不同的密钥第二次注册所有相同的类型,则需要执行以下操作:

builder.RegisterAssemblyTypes(typeof(Repository<>).Assembly)
    .WithParameter(
        new ResolvedParameter(
            (pi, ctx) => pi.ParameterType == typeof(IClientContext),
            (pi, ctx) => ctx.ResolveNamed<IClientContext>(RegistrationKeys.Unpublished)))
    .Named(RegistrationKeys.Unpublished)
    .AsImplementedInterfaces();

但是,这是行不通的,因为Named方法需要指定注册的类型,但是它应该基于RegisterAssemblyTypes调用中的一系列解析类型而为动态。

如何在不向应用程序中添加数百行代码的情况下做到这一点?

1 个答案:

答案 0 :(得分:1)

您也许可以编写自己的扩展方法来完成此操作。

具有功能的.Named中的

You might have noticed that there is an overload

builder.RegisterAssemblyTypes(typeof(AComponent).GetTypeInfo().Assembly)
       .Named(t => t.Name, typeof(object));

该函数接受Type在程序集中的注册,并使用该类型在服务上生成名称。不幸的是,该重载也仅将特定的固定类型用作服务类型,因此这会将所有内容注册为Named<object>(...),这不是您想要的。

但是!

If you look at how that's implemented ...

public static IRegistrationBuilder<TLimit, TScanningActivatorData, TRegistrationStyle>
  Named<TLimit, TScanningActivatorData, TRegistrationStyle>(
    this IRegistrationBuilder<TLimit, TScanningActivatorData, TRegistrationStyle> registration,
    Func<Type, string> serviceNameMapping,
    Type serviceType)
  where TScanningActivatorData : ScanningActivatorData
{
  return registration.As(t => new KeyedService(serviceNameMapping(t), serviceType));
}

...您可以看到基本上是将类型注册,然后将其传递给您提供的函数以生成该名称。您可以将其更改为 not 而不接受特定的服务类型,然后将其注册为自己。

public static IRegistrationBuilder<TLimit, TScanningActivatorData, TRegistrationStyle>
  NamedSelf<TLimit, TScanningActivatorData, TRegistrationStyle>(
    this IRegistrationBuilder<TLimit, TScanningActivatorData, TRegistrationStyle> registration,
    Func<Type, string> serviceNameMapping)
  where TScanningActivatorData : ScanningActivatorData
{
  return registration.As(t => new KeyedService(serviceNameMapping(t), t));
}

现在使用您自定义的NamedSelf扩展名。

builder.RegisterAssemblyTypes(typeof(Repository<>).Assembly)
    .WithParameter(
        new ResolvedParameter(
            (pi, ctx) => pi.ParameterType == typeof(IClientContext),
            (pi, ctx) => ctx.ResolveNamed<IClientContext>(RegistrationKeys.Unpublished)))
    .NamedSelf(t => RegistrationKeys.Unpublished)
    .AsImplementedInterfaces();

您当然可以根据需要进行更新。也许您不希望它成为一个函数,而只是接受一个字符串-很简单。也许您希望将其注册为命名接口-您可以让函数生成整个KeyedService而不是将其嵌入扩展名中。

要点是,您可以使用现有扩展名作为灵感来编写自己的自定义扩展名,这些扩展名可以满足您的需求,而不必手动注册。

[免责声明:除了我的主意之外,我没有通过编译器来运行所有这些操作,因此可能存在拼写错误,导致无法复制/粘贴。您还需要检查null参数和所有其他内容。单击链接以查看实际的原始资源。希望这至少可以解除您的封锁。]