2013-06-12 45 views
1
  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) { /*...*/ } 
        /*...*/ 
    } 
    

现在,我要注册从工厂处理程序是当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

“FooCommand”对象的实现是什么?它有父母吗?如果确实如此,则可以尝试通过修改“公共类FactoryCommandHandler :ICommandHandler 其中T:[parentclassname]” –

+0

@AzharKhorasany:它没有基类。而且我不明白你建议的通用类型约束是如何改变的。 –

+0

应用一个约束告诉编译器T的对象只能是一个具体的类型,并且它应该能够在没有约束的情况下注册到编译器不知道什么是T. –

回答

2

不幸的是,在这种情况下,您不能使用通用注册是您需要这样做的方式(您需要明确注册每个具体命令处理程序与其实现的接口,包括该接口的通用类型)。

您可以使用以下内容作为您的注册,而不是:

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()); 
+0

非常感谢。我发现自己在编写自己的'IRegistrationSource'后发现RegisterAssemblyTypes是问题......我还没有想出如何做到这一点,非常感谢! –

+0

不客气! :) –

+0

测试并接受。再次感谢!我改进了你略述的代码,请参阅[我的答案](http://stackoverflow.com/a/17068231/572644)。 –

-1

我用马特·戴维斯回答代码和改进这一点:现在

  • 它正确处理实现其他接口的命令处理程序。
  • 它现在正确处理多次执行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<>)); 
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>的单个实例。
  • A Func<ICommandHandler<T>>注入那个FactoryCommandHandler<T>允许创建decoratee(包装的实例)。这有效地延迟了该实例的创建。
  • 注入的工厂将保留装饰者的生活方式。

FactoryCommandHandler<T>只依赖于一个单身人士的Func<T>。因此,FactoryCommandHandler<T>可以被注册为单身(上述注册中发生的情况)。如果取决于其他生活方式的依赖性,最好将其注册为瞬态

+0

感谢您的评论。 Autofac还支持基于自动委托的工厂,而无需明确注册它们。然而,'RegisterSingleDecorator'方法真的很好,因为它缺少的事实是Autofac中这个复杂解决方案的来源。几年前,当我决定使用Autofac时,它似乎是最干净和最简单的注册语法。但那真的很好看。我想我会仔细看看简单注射器。 –

+0

我确实同意Autofac拥有非常好的API。然而,简单的注射器是专为您正在处理的体系结构类型而设计的。这就是为什么在这种情况下注册非常容易。对于其他场景,设计或体系结构,Autofac可能是更好的选择。 – Steven