假设一个简单的界面:
public interface ICommandHandler<T>
{
void Handle(T command);
}
假设不同具体T
的几种实现方式如下:
public class FooCommandHandler : ICommandHandler<FooCommand> { /*...*/ }
假设该接口的通用工厂实现:
public class FactoryCommandHandler<T> : ICommandHandler<T>
{
public FactoryCommandHandler(Func<ICommandHandler<T>> factory) { /*...*/ }
/*...*/
}
现在,我想将 3 中的工厂处理程序注册为解析ICommandHandler<T>
时解析的实例。
我的问题是我无法正确注册其他实现,因此可以解决它们的工厂问题。
以下是我的尝试:
builder.RegisterAssemblyTypes(assembly)
.Where(type => type.Name.EndsWith("CommandHandler"))
.Named("concreteCommandHandler", typeof(ICommandHandler<>));
builder.RegisterGeneric(typeof(FactoryCommandHandler<>)
.WithParameter(
(p, c) => true,
(p, c) => c.ResolveNamed("concreteCommandHandler", p.ParameterType))
.As(typeof(ICommandHandler<>));
然而,这失败了,因为没有注册名为Func<ICommandHandler<SomeConcreteCommand>>
的人。在这种情况下,Autofac似乎无法自动创建工厂which it normally supports。
如何修复注册并实现目标?
答案 0 :(得分:2)
不幸的是你不能在这个实例中使用RegisterAssemblyTypes
- 它doesn't fully handle通用注册你需要的方式(你需要显式注册每个具体的命令处理程序对它的实现的接口,包括该接口的泛型类型。)
您可以使用以下内容作为注册:
assembly.GetTypes()
.Where(type => type.Name.EndsWith("CommandHandler"))
.ToList()
.ForEach(t => builder.RegisterType(t)
.Named("concreteCommandHandler", typeof (ICommandHandler<>)
.MakeGenericType(t.GetInterfaces()[0].GenericTypeArguments[0])
));
builder.RegisterGeneric(typeof(FactoryCommandHandler<>)
.WithParameter(
(p, c) => true,
(p, c) => c.ResolveNamed("concreteCommandHandler", p.ParameterType))
.As(typeof(ICommandHandler<>));
这将成功让你做这样的事情,返回带有命名命令的通用工厂作为构造函数参数:
container.Resolve<ICommandHandler<FooCommand>>().Handle(new FooCommand());
答案 1 :(得分:1)
我很抱歉在这里插入Simple Injector,但我不禁注意到你正在努力解决简单注射器中儿童玩耍的问题。在Simple Injector中,您可以在两行代码中执行您想要的操作:
// using SimpleInjector;
// using SimpleInjector.Extensions;
var container = new Container();
container.RegisterManyForOpenGeneric(
typeof(ICommandHandler<>),
assembly);
container.RegisterSingleDecorator(
typeof(ICommandHandler<>),
typeof(FactoryCommandHandler<>));
这两条简单的行确保了以下内容:
ICommandHandler<T>
。ICommandHandler<T>
接口,则会为该接口的每个封闭通用版本注册。FactoryCommandHandler<T>
已注册为ICommandHandler<T>
个实施。对于ICommandHandler<T>
的每个封闭通用版本,都会返回该通用FactoryCommandHandler<T>
的单个实例。Func<ICommandHandler<T>>
注入FactoryCommandHandler<T>
,允许创建decoratee(包装的实例)。这有效地延迟了该实例的创建。 FactoryCommandHandler<T>
仅取决于单身Func<T>
。因此FactoryCommandHandler<T>
可以自己注册为单身人士(上面的注册会发生什么)。如果它取决于其他生活方式的依赖性,最好将其注册为 transient 。
答案 2 :(得分:-1)
我在Matt Davies的回答中使用了代码并对其进行了一些改进:
ICommandHandler<T>
的命令处理程序。WithParameter
来改进原始版本。像这样,它现在支持FactoryCommandHandler<T>
上的多个构造函数参数。结果如下:
public static class AutofacExtensions
{
public static void RegisterGenericTypesWithFactoryDecorator(
this ContainerBuilder builder,
IEnumerable<Type> relevantTypes,
Type factoryDecorator,
Type implementedInterfaceGenericTypeDefinition)
{
var serviceName = implementedInterfaceGenericTypeDefinition.ToString();
foreach (var implementationType in relevantTypes)
{
var implementedInterfaces =
implementationType.GetGenericInterfaces(
implementedInterfaceGenericTypeDefinition);
foreach (var implementedInterface in implementedInterfaces)
builder.RegisterType(implementationType)
.Named(serviceName, implementedInterface);
}
builder.RegisterGeneric(factoryDecorator)
.WithParameter(
(p, c) => IsSpecificFactoryParameter(p, implementedInterfaceGenericTypeDefinition),
(p, c) => c.ResolveNamed(serviceName, p.ParameterType))
.As(implementedInterfaceGenericTypeDefinition)
.SingleInstance();
}
private static bool IsSpecificFactoryParameter(ParameterInfo p,
Type expectedFactoryResult)
{
var parameterType = p.ParameterType;
if (!parameterType.IsGenericType ||
parameterType.GetGenericTypeDefinition() != typeof(Func<>))
return false;
var actualFactoryResult = p.ParameterType.GetGenericArguments()
.First();
if (actualFactoryResult == expectedFactoryResult)
return true;
if (expectedFactoryResult.IsGenericTypeDefinition &&
actualFactoryResult.IsGenericType)
return expectedFactoryResult ==
actualFactoryResult.GetGenericTypeDefinition();
return false;
}
}
public static class TypeExtensions
{
public static IEnumerable<Type> GetGenericInterfaces(
this Type type, Type openGenericInterface)
{
return
type.GetInterfaces()
.Where(x => x.IsGenericType &&
x.GetGenericTypeDefinition() == openGenericInterface);
}
}
用法:
var relevantTypes = assembly.GetTypes();
builder.RegisterGenericTypesWithFactoryDecorator(
relevantTypes.Where(type => type.Name.EndsWith("CommandHandler")),
typeof(FactoryCommandHandlerDecorator<>),
typeof(ICommandHandler<>));