1
  1. 假设一个简单的界面:

    public interface ICommandHandler<T>
    {
        void Handle(T command);
    }
    
  2. T假设不同的具体s 有几种实现,例如:

    public class FooCommandHandler : ICommandHandler<FooCommand> { /*...*/ }
    
  3. 假设该接口的通用工厂实现:

    public class FactoryCommandHandler<T> : ICommandHandler<T>
    {
        public FactoryCommandHandler(Func<ICommandHandler<T>> factory) { /*...*/ }
        /*...*/
    }
    

现在,我想将工厂处理程序从3注册为解析 an 时解析的实例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 似乎无法自动创建工厂,它通常支持.

如何修复注册并实现我的目标?

4

3 回答 3

2

不幸的是,您不能RegisterAssemblyTypes在这种情况下使用 - 它不能按照您需要的方式完全处理通用注册(您需要针对其实现的接口显式注册每个具体的命令处理程序,包括该接口的通用类型)。

您可以使用以下内容作为您的注册:

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());
于 2013-06-12T13:51:11.310 回答
1

很抱歉在此处插入Simple Injector,但我不禁注意到您正在为 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>都将返回。
  • AFunc<ICommandHandler<T>>被注入到FactoryCommandHandler<T>允许创建被装饰者(被包装的实例)的地方。这有效地延迟了该实例的创建。
  • 注入的工厂将保留被装饰者的生活方式。

FactoryCommandHandler<T>唯一取决于 a Func<T>which 是单例。因此,FactoryCommandHandler<T>可以将其自身注册为单例(在上面的注册中会发生什么)。如果它依赖于其他生活方式的依赖,最好将其注册为transient

于 2013-06-12T19:43:54.543 回答
-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<>));
于 2013-06-12T14:34:13.163 回答