Last active
March 4, 2021 10:36
-
-
Save lggomez/45fb796655ef8da4c261e7537981116e to your computer and use it in GitHub Desktop.
Extensions for Unity containers that add basic support for aspect and interceptor registrations, among other helpers
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| using System; | |
| using System.Collections.Generic; | |
| using System.Linq; | |
| using System.Reflection; | |
| using System.Runtime.CompilerServices; | |
| using System.Runtime.Remoting; | |
| using Bootstrapper.IoC.Behaviors; | |
| using Microsoft.Practices.Unity; | |
| using Microsoft.Practices.Unity.InterceptionExtension; | |
| namespace Bootstrapper.IoC.Common | |
| { | |
| /// <summary> | |
| /// Extensions for Unity containers that add basic support for aspect and interceptor registrations, among other helpers | |
| /// Author: Luis Gómez | |
| /// </summary> | |
| public static class UnityContainerExtensions | |
| { | |
| #region Public extensions | |
| /// <summary> | |
| /// Registers an exception handling interceptor for derived instances of a particular implementation class | |
| /// </summary> | |
| /// <param name="container">The unity container</param> | |
| /// <param name="name">The registration name</param> | |
| /// <param name="T1">The interface of the target class to intercept</param> | |
| /// <param name="T2">The derived type to intercept</param> | |
| /// <returns></returns> | |
| public static IUnityContainer RegisterTypeWithDefaultExceptionHandler<T1, T2>(this IUnityContainer container, string name = "") | |
| where T2 : class, T1 | |
| where T1 : class | |
| { | |
| ValidateInterfaceTypeParameters<T1, T2>(); | |
| return container.RegisterTypeWithInterceptors<T1, T2>(name, typeof(LogExceptionBehavior)); | |
| } | |
| /// <summary> | |
| /// Registers an exception handling interceptor for derived instances of a particular implementation class | |
| /// </summary> | |
| /// <param name="container">The unity container</param> | |
| /// <param name="name">The registration name</param> | |
| /// <param name="T1">The interface type to intercept</param> | |
| /// <returns></returns> | |
| public static IUnityContainer RegisterTypeWithDefaultExceptionHandler<T1>(this IUnityContainer container, string name = "") | |
| where T1 : class | |
| { | |
| ValidateInterfaceTypeParameters<T1>(); | |
| return container.RegisterTypeWithInterceptors<T1>(name, typeof(LogExceptionBehavior)); | |
| } | |
| /// <summary> | |
| /// Registers multiple interceptors for derived instances of a particular implementation class | |
| /// </summary> | |
| /// <param name="container">The unity container</param> | |
| /// <param name="name">The registration name</param> | |
| /// <param name="types">The interceptor types array</param> | |
| /// <param name="T1">The interface type to intercept</param> | |
| /// <returns></returns> | |
| public static IUnityContainer RegisterTypeWithInterceptors<T1>(this IUnityContainer container, string name = "", params Type[] types) | |
| where T1 : class | |
| { | |
| ValidateInterfaceTypeParameters<T1>(); | |
| var injectionMembers = GenerateIncerceptionInjectionMembers<T1>(types); | |
| return string.IsNullOrWhiteSpace(name) ? | |
| container.RegisterType<T1>(injectionMembers) : | |
| container.RegisterType<T1>(name, injectionMembers); | |
| } | |
| /// <summary> | |
| /// Registers multiple interceptors for derived instances of a particular implementation class, along with the initial class/interface registration | |
| /// </summary> | |
| /// <param name="container">The unity container</param> | |
| /// <param name="name">The registration name</param> | |
| /// <param name="types">The interceptor types array</param> | |
| /// <param name="T1">The interface type to intercept</param> | |
| /// <returns></returns> | |
| public static IUnityContainer RegisterTypeWithInterceptors<T1, T2>(this IUnityContainer container, string name = "", params Type[] types) | |
| where T1 : class | |
| where T2 : class, T1 | |
| { | |
| ValidateInterfaceTypeParameters<T1, T2>(); | |
| var injectionMembers = GenerateIncerceptionInjectionMembers<T1, T2>(types); | |
| return string.IsNullOrWhiteSpace(name) ? | |
| container.RegisterType<T1, T2>(injectionMembers) : | |
| container.RegisterType<T1, T2>(name, injectionMembers); | |
| } | |
| /// <summary> | |
| /// Registers an attribute interceptor for a given type | |
| /// </summary> | |
| /// <param name="container">The unity container</param> | |
| /// <param name="name">The registration name</param> | |
| /// <param name="attributeInterface">The attribute interface type</param> | |
| /// <param name="attributeType">The attribute implementation type</param> | |
| /// <param name="T1">The interface type to intercept</param> | |
| /// <returns></returns> | |
| public static IUnityContainer RegisterTypeWithAttributeInterceptors<T1>(this IUnityContainer container, string name, Type attributeInterface, Type attributeType) | |
| where T1 : class | |
| { | |
| ValidateInterfaceTypeParameters<T1>(); | |
| var injectionMembers = GenerateAttributeIncerceptionInjectionMembers(); | |
| container | |
| .RegisterType(attributeInterface, attributeType, new ContainerControlledLifetimeManager()) | |
| .RegisterType<T1>(name, injectionMembers); | |
| return container; | |
| } | |
| /// <summary> | |
| /// Registers an attribute interceptor for a given type | |
| /// </summary> | |
| /// <param name="container">The unity container</param> | |
| /// <param name="name">The registration name</param> | |
| /// <param name="attributeInterface">The attribute interface type</param> | |
| /// <param name="attributeType">The attribute implementation type</param> | |
| /// <param name="T1">The interface type to intercept</param> | |
| /// <returns></returns> | |
| public static IUnityContainer RegisterTypeWithAttributeInterceptors<T1, T2>(this IUnityContainer container, string name, Type attributeInterface, Type attributeType) | |
| where T1 : class | |
| where T2 : class, T1 | |
| { | |
| ValidateAttributeTypes(attributeInterface, attributeType); | |
| var injectionMembers = GenerateAttributeIncerceptionInjectionMembers(); | |
| container | |
| .RegisterType(attributeInterface, attributeType, new ContainerControlledLifetimeManager()) | |
| .RegisterType<T1, T2>(name, injectionMembers); | |
| return container; | |
| } | |
| /// <summary> | |
| /// Registers a service factory injection for the given type | |
| /// </summary> | |
| /// <param name="container">The unity container</param> | |
| /// <param name="name">The registration name</param> | |
| /// <param name="T1">The interface type to inject the factory into</param> | |
| /// <returns></returns> | |
| public static IUnityContainer RegisterTypeWithInjectionFactory<T1>(this IUnityContainer container, string name = "") | |
| where T1 : class | |
| { | |
| ValidateInterfaceTypeParameters<T1>(); | |
| container.RegisterType<Func<string, T1>>(name, new InjectionFactory(c => new Func<string, T1>(c.ResolveAndUnwrap<T1>))); | |
| return container; | |
| } | |
| /// <summary> | |
| /// Resolves all instances of a given type and unwraps all dynamic modules | |
| /// </summary> | |
| /// <param name="container">The unity container</param> | |
| /// <param name="name">The registration name</param> | |
| /// <param name="T1">The type to resolve</param> | |
| /// <returns></returns> | |
| public static T1 ResolveAndUnwrap<T1>(this IUnityContainer container, string name = "") | |
| where T1 : class | |
| { | |
| ValidateInterfaceTypeParameters<T1>(); | |
| var resolvedObject = !string.IsNullOrWhiteSpace(name) ? container.Resolve<T1>(name) : container.Resolve<T1>(); | |
| var targetFieldInfo = GetObjectProxyTarget(ref resolvedObject); | |
| if (targetFieldInfo != null) | |
| { | |
| return targetFieldInfo.GetValue(resolvedObject) as T1; | |
| } | |
| return resolvedObject; | |
| } | |
| /// <summary> | |
| /// Resolves all instances of a given type and unwraps all dynamic modules and proxies | |
| /// </summary> | |
| /// <param name="container">The unity container</param> | |
| /// <param name="T1">The type to resolve</param> | |
| /// <returns></returns> | |
| public static T1[] ResolveAndUnwrapAll<T1>(this IUnityContainer container) | |
| where T1 : class | |
| { | |
| ValidateInterfaceTypeParameters<T1>(); | |
| var resolvedObjects = container.ResolveAll<T1>(); | |
| var objects = new T1[resolvedObjects.Count()]; | |
| if (resolvedObjects.Any()) | |
| { | |
| for (int i = 0; i < resolvedObjects.Count(); i++) | |
| { | |
| var resolvedObject = resolvedObjects.ElementAt(i); | |
| var targetFieldInfo = GetObjectProxyTarget(ref resolvedObject); | |
| if (targetFieldInfo != null) | |
| { | |
| objects[i] = targetFieldInfo.GetValue(resolvedObject) as T1; | |
| } | |
| else | |
| { | |
| objects[i] = resolvedObject; | |
| } | |
| } | |
| var filteredTypes = | |
| objects.Where( | |
| o => o.GetType() | |
| .GetInterfaces() | |
| .Any(i => | |
| string.Equals(typeof(T1).Name, i.Name, | |
| StringComparison.InvariantCultureIgnoreCase))) | |
| .ToArray(); | |
| return filteredTypes; | |
| } | |
| return new T1[0]; | |
| } | |
| #endregion | |
| #region Private helpers | |
| /// <summary> | |
| /// Gets the fieldInfo of a corresponding target instance in a possibly wrapped object | |
| /// and updates the reference if necessary | |
| /// </summary> | |
| /// <param name="resolvedObject">The resolved object to unwrap</param> | |
| /// <param name="T1">The type to resolve</param> | |
| /// <returns></returns> | |
| private static FieldInfo GetObjectProxyTarget<T1>(ref T1 resolvedObject) | |
| where T1 : class | |
| { | |
| if (resolvedObject == null) return null; | |
| var resolvedObjectTypeName = resolvedObject | |
| .GetType() | |
| .FullName.ToLowerInvariant(); | |
| Func<object, FieldInfo[]> fieldExtractor = (o) => | |
| o.GetType() | |
| .GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance); | |
| //Resolve Unity module proxies | |
| if (resolvedObjectTypeName.Contains("DynamicModule.ns.Wrapped".ToLowerInvariant())) | |
| { | |
| return fieldExtractor(resolvedObject).FirstOrDefault(info => info.Name.ToLowerInvariant().Equals("target")); | |
| } | |
| //Unwrap remoting proxies | |
| if (RemotingServices.IsTransparentProxy(resolvedObject)) | |
| { | |
| resolvedObject = new ObjectHandle(resolvedObject).Unwrap() as T1; | |
| } | |
| return null; | |
| } | |
| /// <summary> | |
| /// Generates the injection members to add interceptors for a given registration | |
| /// </summary> | |
| /// <param name="types">The interceptor types array</param> | |
| /// <param name="T1">The interface to inject the members into</param> | |
| /// <returns></returns> | |
| private static InjectionMember[] GenerateIncerceptionInjectionMembers<T1>(Type[] types) | |
| where T1 : class | |
| { | |
| var injectionMembers = new List<InjectionMember> { new Interceptor<InterfaceInterceptor>() }; | |
| CreateInjectionMembers<T1>(types, injectionMembers); | |
| return injectionMembers.ToArray(); | |
| } | |
| /// <summary> | |
| /// Generates the injection members to add interceptors for a given registration | |
| /// </summary> | |
| /// <param name="types">The interceptor types array</param> | |
| /// <param name="T1">The interface to inject the members into</param> | |
| /// <param name="T2">The derived type mapped to inject the members into</param> | |
| /// <returns></returns> | |
| private static InjectionMember[] GenerateIncerceptionInjectionMembers<T1, T2>(Type[] types) | |
| where T1 : class | |
| where T2 : class, T1 | |
| { | |
| var injectionMembers = new List<InjectionMember> { new Interceptor<TransparentProxyInterceptor>() }; | |
| CreateInjectionMembers<T2>(types, injectionMembers); | |
| return injectionMembers.ToArray(); | |
| } | |
| /// <summary> | |
| /// Generates the injection members to add attribute interceptors for a given registration | |
| /// </summary> | |
| /// <param name="T1">The interface to inject the members into</param> | |
| /// <param name="T2">The derived type mapped to inject the members into</param> | |
| /// <returns></returns> | |
| private static InjectionMember[] GenerateAttributeIncerceptionInjectionMembers() | |
| { | |
| var injectionMembers = new List<InjectionMember> | |
| { | |
| new InterceptionBehavior<PolicyInjectionBehavior>(), | |
| new Interceptor<VirtualMethodInterceptor>() | |
| }; | |
| return injectionMembers.ToArray(); | |
| } | |
| /// <summary> | |
| /// Generates the injection members, adding interceptors given a list of types | |
| /// </summary> | |
| /// <param name="types">The interceptor types array</param> | |
| /// <param name="injectionMembers">The injection member list to add</param> | |
| /// <param name="T1">The type to inject the members into</param> | |
| /// <returns></returns> | |
| private static void CreateInjectionMembers<T>(Type[] types, ICollection<InjectionMember> injectionMembers) | |
| where T : class | |
| { | |
| foreach (Type type in types) | |
| { | |
| var interceptionName = typeof(T).Name + "_" + type.Name + "_" + "Interception"; | |
| injectionMembers.Add(new InterceptionBehavior(type, interceptionName)); | |
| } | |
| } | |
| #endregion | |
| #region Type validators | |
| [MethodImpl(MethodImplOptions.AggressiveInlining)] | |
| private static void ValidateInterfaceTypeParameters<T1>() | |
| where T1 : class | |
| { | |
| if (!typeof(T1).IsInterface) throw new ArgumentException("Type parameter T1 must be an interface"); | |
| } | |
| [MethodImpl(MethodImplOptions.AggressiveInlining)] | |
| private static void ValidateInterfaceTypeParameters<T1, T2>() | |
| where T2 : T1 | |
| where T1 : class | |
| { | |
| if (!typeof(T1).IsInterface) throw new ArgumentException("Type parameter T1 must be an interface"); | |
| } | |
| [MethodImpl(MethodImplOptions.AggressiveInlining)] | |
| private static void ValidateAttributeTypes(Type attributeInterface, Type attributeType) | |
| { | |
| if (!attributeInterface.IsInterface) throw new ArgumentException("Attribute interface type parameter must be an interface"); | |
| if (attributeType.IsInterface) throw new ArgumentException("Attribute implementation type parameter must not be an interface"); | |
| } | |
| #endregion | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment