2013-08-06 35 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