从通用类型的命名注册中解析自动工厂

时间:2013-06-12 12:50:28

标签: c# .net autofac

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

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

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

    public class FactoryCommandHandler<T> : ICommandHandler<T>
    {
        public FactoryCommandHandler(Func<ICommandHandler<T>> factory) { /*...*/ }
        /*...*/
    }
    
  4. 现在,我想将 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

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

3 个答案:

答案 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<>));
相关问题