Autofac 枚举不适用于类型或模块的多个注册
Autofac Enumeration not working with multiple registrations of types or modules
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>
。 注册 可以绑定到类型或委托或其他任何具有配置行为的东西。
通过注册两次 Type,您将拥有两个不同的 IComponentRegistration
,在您的情况下,registration 将是相似的。如果您查看以下差异,您会发现您将有两个不同的 registration,它们使用相同的 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
可能在多个模块中注册,那么在 main 模块中注册此 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. Please have a look at the source code of the CollectionRegistrationSource.cs为更完整的注册版。
when Autofac is injecting a constructor parameter of type
IEnumerable<ITask>
it will not look for a component that suppliesIEnumerable<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>
。 注册 可以绑定到类型或委托或其他任何具有配置行为的东西。
通过注册两次 Type,您将拥有两个不同的 IComponentRegistration
,在您的情况下,registration 将是相似的。如果您查看以下差异,您会发现您将有两个不同的 registration,它们使用相同的 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
可能在多个模块中注册,那么在 main 模块中注册此 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. Please have a look at the source code of the CollectionRegistrationSource.cs为更完整的注册版。