AutoFac - Register Decorator for some open shared

I am trying to configure an Autofac module which seems to have a complex requirement.

here goes:

I have a common interface:

public interface IMyInterface<TFoo, TBar> 

I have a bunch of classes implementing this interface

eg.

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

Finally, I have a decorator:

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

I only want to "decorate" implementations (from MyInterface ) that have a specific attribute. Thus, all implementations of MyInterface that have the [MyAttribute] attribute are decorated with MyDecorator.

I'm near, but not cigars yet:

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

I understand that the last piece of the puzzle is the key, it really needs to pass the type to Keyed("CachableQueries", THE_TYPE); but it does not play the ball.

Update

Nemesv sent me in the right direction.

As part of my question, I forgot to mention that I also needed to register all IMyInterface <,> implementations that did not have [MyAttribute].

I did this in two steps. First register the types with Decorator, and then register the rest.

My solution: I know that it needs refactoring, but as a proof of concept. it works.

 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(); } } 
+4
source share
2 answers

The problem is that when you use Keyed registration, you need to specify a private type of service, for example. IMyInterface<string, bool> , so you cannot use an open generic type like typeof(IMyInterface<,>)

However, since there is no API when using RegisterAssemblyTypes , you can get a registered private type to register it as Keyed . Therefore, you need to manually perform the โ€œbuildโ€:

You need to replace your RegisterAssemblyTypes call with something like this (you will probably need additional error handling during production):

 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<,>))); } 

This is equivalent to the following manual registration, which is required for RegisterGenericDecorator to work (provided that MyImpl1 and MyImpl3 marked with MyAttribute :

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

Please note: you cannot use RegisterGeneric because you have this special MyAttribute filter.

+2
source

Well, I did not understand that the question was from 3 years ago, since it was updated just a week ago.

We can use chained methods during registration to separate types that are decorated with an attribute from those that are not.

 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(); } } } 

Exit the console:

 BooleanToStringTwo is being decorated! True from BooleanToStringTwo BooleanToStringOne is being decorated! True from BooleanToStringOne True from BooleanToStringThree 
+2
source

Source: https://habr.com/ru/post/1495599/


All Articles