注册不带通用接口和扫描组件的适配器

本文关键字:扫描 组件 适配器 接口 带通 注册 | 更新日期: 2023-09-27 17:59:14

我正在尝试实现以下目标:

  1. 扫描特定部件(RegisterAssemblyTypes(assembly)(。
  2. 过滤到一个特定的命名空间(.IsInNamespace("...")(,它将具有没有公共基本接口的独立接口。
  3. 将通用适配器注册为通用接口。( .RegisterGeneric(typeof(Adapter<>)).As(typeof(IAdapter<>))
  4. 我需要其中的几个注册,每个注册集都将IAdapter<>与不同的Adapter<>实例一起使用。

换句话说,IFooIBar 可能在命名空间 1 中,IAbc在命名空间 2 中,我想注册:

  • Adapter<IFoo>IAdapter<IFoo>
  • Adapter<IBar>IAdapter<IBar>
  • Adapter2<IAbc>Adapter<IAbc>(customParams) IAdapter<IAbc>

稍微重复一遍,如果我自己扫描程序集以获取接口,那么我相信我可以做类似的事情(未经测试(:

Type interfaces[] = ...; // Scan assembly and filter to namespace1
foreach (Type interfaceType in interfaces)
{
    builder
        .Register(context => typeof(Adapter<>).MakeGenericType(interfaceType))
        .As(type => typeof(IAdapter<>).MakeGenericType(interfaceType));
}
interfaces = ...; // Interfaces in namespace2
foreach (Type interfaceType in interfaces)
{
    builder
        .Register(context => typeof(Adapter2<>).MakeGenericType(interfaceType))
        .As(type => typeof(IAdapter<>).MakeGenericType(interfaceType));
}

有没有办法使用 Autofac 开箱即用而无需进行自己的扫描/循环? 我已经查看了 Autofac 适配器支持,但这似乎仅适用于通用接口。 RegisterGeneric 也有帮助,但只对一种类型的实例有帮助。

注册不带通用接口和扫描组件的适配器

> Autofac 没有内置支持根据泛型参数的类型解析泛型服务。顺便说一下,您可以使用IRegistrationSource进行动态注册来执行所需的操作。

假设您有这些类型:

namespace A
{
    interface IFoo { }
}
namespace B
{
    interface IBar { }
}
interface IAdapter<T> { }
class Adapter1<T> : IAdapter<T> { }
class Adapter2<T> : IAdapter<T> { }
class Foo : A.IFoo { }
class Bar : B.IBar { }

你想要这样的东西:

ContainerBuilder builder = new ContainerBuilder();
builder.RegisterType<Foo>().As<A.IFoo>();
builder.RegisterType<Bar>().As<B.IBar>();
builder.RegisterGeneric(typeof(Adapter1<>)).As(typeof(IAdapter<>));
builder.RegisterGeneric(typeof(Adapter2<>)).As(typeof(IAdapter<>));
IContainer container = builder.Build();
var fooAdapter = container.Resolve<IAdapter<A.IFoo>>(); // should return Adapter1<Foo>()
var barAdapter = container.Resolve<IAdapter<B.IBar>>(); // should return Adapter2<Bar>()

Autofac解析IAdapter<T>时,它无法找到基于T命名空间的具体实现。

IRegistrationSource允许您在 Autofac 需要时动态注册类型。

class TestRegistrationSource : IRegistrationSource
{
    public Boolean IsAdapterForIndividualComponents
    {
        get { return false; }
    }
    public IEnumerable<IComponentRegistration> RegistrationsFor(
        Service service, Func<Service, IEnumerable<IComponentRegistration>> registrationAccessor)
    {
        IServiceWithType typedService = service as IServiceWithType;
        if (typedService == null)
        {
            yield break;
        }
        if (!(typedService.ServiceType.IsGenericType 
              && typedService.ServiceType.GetGenericTypeDefinition() == typeof(IAdapter<>)))
        {
            yield break;
        }
        Type t = typedService.ServiceType.GetGenericArguments()[0];
        IComponentRegistration registration = 
            RegistrationBuilder.ForDelegate((c, p) => c.ResolveNamed(t.Namespace, typedService.ServiceType, p))
                               .As(service)
                               .CreateRegistration();
        yield return registration;
    }
}

Autofac 需要时,此IRegistrationSource将注册一个新的具体实现。当 Autofac 需要IAdapter<A.Foo>时,它将进入TestRegistrationSource,该将新IAdapter<A.Foo>注册为委托,这将解析命名的通用注册IAdapter<T>

为了使TestRegistrationSource正常工作,您必须更改Adapter1<T>的注册,Adapter2<T>使用通用命名注册。

ContainerBuilder builder = new ContainerBuilder();
builder.RegisterType<Foo>().As<A.IFoo>();
builder.RegisterType<Bar>().As<B.IBar>();
builder.RegisterSource(new TestRegistrationSource());
builder.RegisterGeneric(typeof(Adapter1<>)).Named("Namespace.A", typeof(IAdapter<>));
builder.RegisterGeneric(typeof(Adapter2<>)).Named("Namespace.B", typeof(IAdapter<>));
IContainer container = builder.Build();
var fooAdapter = container.Resolve<IAdapter<A.IFoo>>(); // will return Adapter1<Foo>()
var barAdapter = container.Resolve<IAdapter<B.IBar>>(); // will return Adapter2<Bar>()