2013-08-06 60 views
4

我試圖設置一個似乎有複雜需求的Autofac模塊。AutoFac - 註冊一些開放的裝飾器通用

這裏有雲:

我有一個通用的接口:

public interface IMyInterface<TFoo, TBar> 

我有一大堆的實現此接口的

例如類

class MyImpl1 : IMyInterface<string, bool> { } 
class MyImpl2 : IMyInterface<bool, string> { } 
class MyImpl3 : IMyInterface<bool, string> { } 

最後,我有一個裝飾:

class MyDecorator<TFoo, TBar> : IMyInterface<TFoo, TBar> 

我只是想 「裝飾」 具有特定屬性(的MyInterface)的實現。因此,MyInterface的所有具有屬性[MyAttribute] 的實現均使用MyDecorator進行裝飾。

我很接近,但沒有雪茄尚未:

var builder = new ContainerBuilder();   

builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly()) 
    .Where(type => type.GetCustomAttributes(true) 
     .Any(attr => attr.GetType() == typeof(MyAttribute))) 
    .AsClosedTypesOf(typeof (IMyInterface<,>)) 
    .Keyed("CachableQueries", typeof(IMyInterface<,>)); 

builder.RegisterGenericDecorator(typeof(MyDecorator<,>), 
    typeof(IMyInterface<,>), "CachableQueries"); 

var container = builder.Build(); 

Console.WriteLine(container.Resolve<IMyInterface<string,bool>>()); 
Console.WriteLine(container.Resolve<IMyInterface<bool,bool>>()); 

據我所知,拼圖的最後一塊是關鍵,它實際上需要的類型傳遞到Keyed("CachableQueries", THE_TYPE);但它不是打球。

更新

nemesv送我在正確的方向。

作爲我的問題的一部分,我忘了提及我還需要註冊IMyInterface <的所有實現,>沒有[MyAttribute]。

我在兩個階段做了這個。首先用Decorator註冊類型,然後註冊其餘的。

我的解決方案: 我知道它需要重構,但作爲概念證明。有用。

class Program 
{ 
    static void Main(string[] args) 
    { 
     var builder = new ContainerBuilder(); 

     //Get all the types we're interested in (that inherit IMyInterface) 
     List<Type> typesToQuery = Assembly.GetExecutingAssembly().GetTypes() 
      .Where(type => type.GetInterfaces() 
        .Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof (IMyInterface<,>))).ToList(); 

     //Even tho the decorator inherits IMyInterface (we don't want to process it) 
     typesToQuery.Remove(typeof (MyDecorator<,>)); 


     //build a dictionary of all the types, so we don't process them again. 
     Dictionary<Type, bool> typesToProcess = typesToQuery.ToDictionary(queryType => queryType, queryType => false); 


     //Register all types that have [MyAttribute] 
     foreach (var type in typesToQuery 
        .Where(type => type.GetCustomAttributes(true) 
        .Any(attr => attr.GetType() == (typeof(MyAttribute))))) 
     { 
      builder.RegisterType(type).Keyed("CachableQueries", 
       type.GetInterfaces() 
        .First(i => 
          i.IsGenericType && 
          i.GetGenericTypeDefinition() == typeof(IMyInterface<,>))); 

      typesToProcess[type] = true; //update, so this type isn't processed again 
     } 

     //Decorate the correct ones 
     builder.RegisterGenericDecorator(typeof(MyDecorator<,>), typeof(IMyInterface<,>), fromKey: "CachableQueries"); 

     //Register the rest of the types we're interested 
     foreach (Type type in typesToProcess.Where(kvp => kvp.Value == false).Select(pair => pair.Key)) 
     { 
      builder.RegisterType(type).As(
       type.GetInterfaces() 
        .First(i => 
          i.IsGenericType && 
          i.GetGenericTypeDefinition() == typeof(IMyInterface<,>))); 

     } 

     var container = builder.Build(); 

     Console.WriteLine(container.Resolve<IMyInterface<string, bool>>()); 
     Console.WriteLine(container.Resolve<IMyInterface<bool, bool>>()); 

     //Result: 
     //AutoFacPlay.MyDecorator`2[System.String,System.Boolean] - this one was decorated (as it has MyAttribute) 
     //AutoFacPlay.MyImplementation2 - this one wasn't decorated 

     Console.ReadLine(); 

    } 
} 
+1

相關:http://stackoverflow.com/questions/18000522/register-autofac-decorator-for-only-one-generic-command-handler – Steven

+0

關閉:)我正在使用開放的通用接口。在那個例子中,裝飾器是由特定類型決定的。我的場景由一個屬性決定。我不會移動到SimpleInjector(不是我的決定):P – Mike

+0

(我永遠不能讓代碼指示器在WYSIWYG編輯器中工作!) – Mike

回答

2

問題是,當您使用Keyed註冊時,您需要指定封閉的服務類型,例如IMyInterface<string, bool>所以你不能使用開放式泛型有喜歡typeof(IMyInterface<,>)

但是因爲使用RegisterAssemblyTypes時,有沒有API,你可以得到,以便將其註冊爲Keyed當前註冊的封閉式。所以,你需要實現「集結掃描」手工:

你需要像這樣的東西(你可能需要一些更多的錯誤在生產處理),以取代RegisterAssemblyTypes電話:

foreach (var type in Assembly.GetExecutingAssembly().GetTypes() 
    .Where(type => type.GetCustomAttributes(true) 
         .Any(attr => attr.GetType() == (typeof(MyAttribute))))) 
{ 
    builder.RegisterType(type).Keyed("CachableQueries", 
     type.GetInterfaces() 
      .First(i => 
        i.IsGenericType && 
        i.GetGenericTypeDefinition() == typeof(IMyInterface<,>))); 
} 

這相當於以這爲RegisterGenericDecorator工作需要以下手工註冊(假定MyImpl1MyImpl3被標有MyAttribute

builder.RegisterType<MyImpl1>() 
     .Keyed<IMyInterface<string, bool>>("CachableQueries"); 
builder.RegisterType<MyImpl3>() 
     .Keyed<IMyInterface<bool, bool>>("CachableQueries"); 

注ÿ你不能在這裏使用RegisterGeneric,因爲你有這個特殊的MyAttribute過濾器。

+0

感謝您的回答Nemesv,它使我走上了正確的軌道。我需要打開原始反射代碼才能使其工作。 :) – Mike

2

好吧,我沒有意識到問題出自3年前,因爲它是在一週前更新的。

我們可以在註冊過程中利用鏈式方法來分隔裝飾有屬性和非屬性的類型。

using System; 
using System.Collections.Generic; 
using System.Linq; 
using Autofac; 

namespace ConsoleApplication1 
{ 
    public interface IOpenGeneric<T, U> 
    { 
     U Get(T value); 
    } 

    [AttributeUsage(AttributeTargets.Class)] 
    public class DecorateAttribute : Attribute 
    { 
    } 

    [Decorate] 
    public class BooleanToStringOne : IOpenGeneric<bool, string> 
    { 
     public string Get(bool value) 
     { 
      return $"{value.ToString()} from BooleanToStringOne"; 
     } 
    } 

    [Decorate] 
    public class BooleanToStringTwo : IOpenGeneric<bool, string> 
    { 
     public string Get(bool value) 
     { 
      return $"{value.ToString()} from BooleanToStringTwo"; 
     } 
    } 

    public class BooleanToStringThree : IOpenGeneric<bool, string> 
    { 
     public string Get(bool value) 
     { 
      return $"{value.ToString()} from BooleanToStringThree"; 
     } 
    } 

    public class OpenGenericDecorator<T, U> : IOpenGeneric<T, U> 
    { 
     private readonly IOpenGeneric<T, U> _inner; 

     public OpenGenericDecorator(IOpenGeneric<T, U> inner) 
     { 
      _inner = inner; 
     } 

     public U Get(T value) 
     { 
      Console.WriteLine($"{_inner.GetType().Name} is being decorated!"); 
      return _inner.Get(value); 
     } 
    } 

    public static class ReflectionExtensions 
    { 
     public static bool HasAttribute<TAttribute>(this Type type) 
      where TAttribute : Attribute 
     { 
      return type 
       .GetCustomAttributes(typeof(TAttribute), false) 
       .Cast<Attribute>() 
       .Any(); 
     } 
    } 

    class Program 
    { 
     static void Main(string[] args) 
     { 
      var assembly = typeof(Program).Assembly; 
      var builder = new ContainerBuilder(); 

      // associate types that have the [Decorate] attribute with a specific key 
      builder 
       .RegisterAssemblyTypes(assembly) 
       .Where(x => x.HasAttribute<DecorateAttribute>()) 
       .AsClosedTypesOf(typeof(IOpenGeneric<,>), "decoratable-service"); 

      // get the keyed types and register the decorator 
      builder.RegisterGenericDecorator(
       typeof(OpenGenericDecorator<,>), 
       typeof(IOpenGeneric<,>), 
       "decoratable-service"); 

      // no key for the ones with no [Decorate] attribute so they'll 
      // get resolved "as is" 
      builder 
       .RegisterAssemblyTypes(assembly) 
       .Where(x => !x.HasAttribute<DecorateAttribute>()) 
       .AsClosedTypesOf(typeof(IOpenGeneric<,>)); 

      var container = builder.Build(); 

      var booleanToStrings = container.Resolve<IEnumerable<IOpenGeneric<bool,string>>>(); 
      foreach (var item in booleanToStrings) 
      { 
       Console.WriteLine(item.Get(true)); 
       Console.WriteLine(); 
      } 

      Console.ReadLine(); 
     } 
    } 
} 

從控制檯的輸出是

BooleanToStringTwo is being decorated! 
True from BooleanToStringTwo 

BooleanToStringOne is being decorated! 
True from BooleanToStringOne 

True from BooleanToStringThree