Skip to content

Instantly share code, notes, and snippets.

@lggomez
Last active March 4, 2021 10:36
Show Gist options
  • Select an option

  • Save lggomez/45fb796655ef8da4c261e7537981116e to your computer and use it in GitHub Desktop.

Select an option

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
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