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

标签 c# autofac autofac-module

autofac documentation状态:

when Autofac is injecting a constructor parameter of type IEnumerable<ITask> it will not look for a component that supplies IEnumerable<ITask>. Instead, the container will find all implementations of ITask and inject all of them.

但实际上,它添加每个已注册类型的次数与已注册的次数一样多。因此,如果您按以下方式注册一个类(class)两次:

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

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

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

最佳答案

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

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

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

在这种情况下解析 IEnumerable<A>会给你两个不同的 A 实例这确实有道理。

Is there any way to force the enumeration to provide a single item for each implementation

我不建议这样做。最好重构您的应用程序以不允许出现这种情况,如果您需要,您将删除 Autofac 中的许多重要内容。 如果你有 Type可能在多个模块中注册 注册此 Type 可能有意义在 main 模块中。


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

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 的源代码以获得更完整的注册版本。

关于c# - Autofac 枚举不适用于类型或模块的多个注册,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32415690/

相关文章:

c# - 为什么在关闭表单之前调用 FormDeactivate?

c# - 参数化实例化如何与单实例生命范围一起工作

asp.net-mvc - Autofac 生命周期范围与 ASP.NET MVC

c# - Autofac RegisterAssemblyTypes 来自测试

c# - 我需要哪些软件才能精通 Microsoft 认可的语言?

c# - 如何检查元素是否存在?

c# - GroupBy 选择的 LINQ 错误 : System. NotSupportedException

c# - NopCommerce 4.20 依赖注入(inject)插件开发错误

c# - N 层架构中的 Autofac 模块