繁体   English   中英

Autofac枚举不适用于多个类型或模块的注册

[英]Autofac Enumeration not working with multiple registrations of types or modules

autofac文档说明:

当Autofac注入IEnumerable<ITask>类型的构造函数参数时,它不会查找提供IEnumerable<ITask>的组件。 相反,容器将找到ITask的所有实现并注入所有这些实现。

但实际上,它会按照注册的次数添加每个注册类型。 因此,如果您按以下方式注册两次课程:

builder.RegisterType<A>(); 
builder.RegisterType<A>(); 

然后你在枚举中得到两个项目 在单个模块中,这不是问题,因为您显然只需要注册一次类型。 但是如果你有一个由多个模块注册的共享模块(典型的菱形模块依赖关系图),那么你可以在枚举中获得尽可能多的项目,因为共享模块已经被其他人注册了......

这是一个错误吗? 有没有办法强制枚举为每个实现提供单个项目,如文档中所述,不再?

您必须将Autofac视为一种Dictionary<IComponentRegistration> 注册可以绑定到类型或委托或其他具有配置行为的内容。

通过注册两次类型,您将有两个不同的IComponentRegistration ,在您的情况下, 注册将是类似的。 如果您查看以下差异,您会发现您将使用相同Type但具有不同配置的两个不同注册

builder.RegisterType<A>().WithConstrusctorArgument("param1", "x"); 
builder.RegisterType<A>().WithConstrusctorArgument("param1", "y"); 

在这种情况下,解析IEnumerable<A>将为您提供两个不同的A实例,它确实有意义。

有没有办法强制枚举为每个实现提供单个项目

我不建议这样做。 重构您的应用程序不允许这种情况可能更好,如果您需要,您将删除Autofac中的许多好东西。 如果您的Type可以在多个模块中注册,则可以在模块中注册此Type


顺便说一下,如果你真的想这样做,你必须找到一种方法来区分容器的所有注册。 然后,您可以通过实现自己的IRegistrationSource覆盖IEnumerable<>的默认实现,以下示例将为您提供每种类型只有一个注册。

class CustomEnumerableRegistrationSource : 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)
        {
            return Enumerable.Empty<IComponentRegistration>();
        }
        if (!(typedService.ServiceType.IsGenericType && typedService.ServiceType.GetGenericTypeDefinition() == typeof(IEnumerable<>)))
        {
            return Enumerable.Empty<IComponentRegistration>();
        }

        Type elementType = typedService.ServiceType.GetGenericArguments()[0];

        Service elementService = typedService.ChangeType(elementType);

        Type collectionType = typeof(List<>).MakeGenericType(elementType);

        IComponentRegistration registration = RegistrationBuilder.ForDelegate(collectionType, (c, p) =>
        {
            IEnumerable<IComponentRegistration> registrations = c.ComponentRegistry.RegistrationsFor(elementService);

            IEnumerable<Object> elements = registrations.Select(cr => c.ResolveComponent(cr, p));

            // get distinct elements by type
            Array array = elements.GroupBy(o => o.GetType()).Select(o => o.First()).ToArray();

            Array array2 = Array.CreateInstance(elementType, array.Length);
            array.CopyTo(array2, 0);

            Object collection = Activator.CreateInstance(collectionType, new Object[] { array2 });

            return collection;
        }).As(service)
          .CreateRegistration();

        return new IComponentRegistration[] { registration };
    }
}

你可以像这样使用它:

ContainerBuilder builder = new ContainerBuilder();

builder.RegisterType<A1>().As<IA>();
builder.RegisterType<A1>().As<IA>();
builder.RegisterType<A2>().As<IA>();

builder.RegisterSource(new CustomEnumerableRegistrationSource());

IContainer container = builder.Build();

IEnumerable<IA> services = container.Resolve<IEnumerable<IA>>();
Console.WriteLine(services.Count());

此注册是原始CollectionRegistrationSource的简化版本。 请查看CollectionRegistrationSource.cs的源代码,以获得更完整的注册版本。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM