AutoFac – 为一些开放的Generic注册装饰器

我正在尝试设置一个似乎有复杂要求的Autofac模块。

开始:

我有一个通用的界面:

public interface IMyInterface 

我有一堆实现此接口的类

例如

 class MyImpl1 : IMyInterface { } class MyImpl2 : IMyInterface { } class MyImpl3 : IMyInterface { } 

最后,我有一个装饰者:

 class MyDecorator : IMyInterface 

我只想“装饰”具有特定属性的( MyInterface )实现。 所以具有[MyAttribute]属性的MyInterface的所有实现都使用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>()); Console.WriteLine(container.Resolve<IMyInterface>()); 

我知道拼图的最后一块是Key,它实际上需要将类型传递给Keyed("CachableQueries", THE_TYPE); 但它不打球。

更新

nemesv让我朝着正确的方向前进。

作为我的问题的一部分,我忘了提到我还需要注册没有[MyAttribute]的IMyInterface 的所有实现。

我分两个阶段做到了这一点。 首先使用Decorator注册类型,然后注册其余的类型。

我的解决方案:我知道它需要重构,但作为概念的certificate。 有用。

 class Program { static void Main(string[] args) { var builder = new ContainerBuilder(); //Get all the types we're interested in (that inherit IMyInterface) List 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 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>()); Console.WriteLine(container.Resolve<IMyInterface>()); //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(); } } 

问题是,当您使用Keyed注册时,您需要指定关闭的服务类型,例如IMyInterface因此您不能使用类似的开放通用typeof(IMyInterface<,>)

但是,因为在使用RegisterAssemblyTypes ,没有API可以获取当前注册的闭合类型,以便将其注册为Keyed 。 因此,您需要手动实施“assembly扫描”:

你需要用这样的东西替换你的RegisterAssemblyTypes调用(在生产中你可能需要更多的error handling):

 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工作所需的以下手动注册(假设MyImpl1MyImpl3MyAttribute标记:

 builder.RegisterType() .Keyed>("CachableQueries"); builder.RegisterType() .Keyed>("CachableQueries"); 

请注意,您不能在此使用RegisterGeneric ,因为您有这个特殊的MyAttributefilter。

好吧,我没有意识到这个问题来自3年前,因为它是在一周前更新的。

我们可以在注册期间利用链式方法来隔离用属性修饰的类型和不属性的类型。

 using System; using System.Collections.Generic; using System.Linq; using Autofac; namespace ConsoleApplication1 { public interface IOpenGeneric { U Get(T value); } [AttributeUsage(AttributeTargets.Class)] public class DecorateAttribute : Attribute { } [Decorate] public class BooleanToStringOne : IOpenGeneric { public string Get(bool value) { return $"{value.ToString()} from BooleanToStringOne"; } } [Decorate] public class BooleanToStringTwo : IOpenGeneric { public string Get(bool value) { return $"{value.ToString()} from BooleanToStringTwo"; } } public class BooleanToStringThree : IOpenGeneric { public string Get(bool value) { return $"{value.ToString()} from BooleanToStringThree"; } } public class OpenGenericDecorator : IOpenGeneric { private readonly IOpenGeneric _inner; public OpenGenericDecorator(IOpenGeneric 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(this Type type) where TAttribute : Attribute { return type .GetCustomAttributes(typeof(TAttribute), false) .Cast() .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()) .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()) .AsClosedTypesOf(typeof(IOpenGeneric<,>)); var container = builder.Build(); var booleanToStrings = container.Resolve>>(); 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