2013-06-12 40 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