Skip to content

Commit

Permalink
Updates to CommonConventions static helper class:
Browse files Browse the repository at this point in the history
1) Adapter suffix is now registered by default, default convention takes precedence.
2) Single implementations of interface no longer required to auto-register.
3) ExcludeFromAutoRegistrationAttribute can be applied to any class to explicitly exclude it from auto registration.
4) Renamed IsMatch private functions to IsExcludedType for clarity.
5) Added documentation headers for the public members of CommonConventions.cs.
  • Loading branch information
NightOwl888 committed Feb 8, 2014
1 parent b68d787 commit 4810c7c
Showing 1 changed file with 97 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,27 @@ namespace DI
{
internal class CommonConventions
{
// Single implementations of interface with matching name (minus the "I").
/// <summary>
/// Registers all single types in a set of assemblies that meet the following criteria:
/// 1) The name of the type matches the interface name (I[TypeName] = [TypeName]) or
/// 2) The name of the type matches the interface name + the suffix "Adapter" (I[TypeName] = [TypeName]Adapter)
/// 3) The class does not have a string parameter in the constructor
/// 4) The class is not decorated with the [ExcludeFromAutoRegistrationAttribute]
/// </summary>
/// <param name="registerMethod">The method on the DI container used to register a type to an abstraction.
/// Should be in the format (interfaceType, implementationType) => SomeMethod(interfaceType, implementationType)
/// or similar.</param>
/// <param name="interfaceAssemblies">An array of assemblies to scan for the interface types. These assemblies
/// must be referenced within this project. Typically, you will just want to use the MvcSiteMapProvider
/// assembly unless you decide to use <see cref="CommonConventions"/> throughout your project.</param>
/// <param name="implementationAssemblies">An array of assemblies to scan for the implementation types.
/// This array should contain all of the assemblies where you have defined types that will be injected
/// into MvcSiteMapProvider.</param>
/// <param name="excludeTypes">An array of <see cref="System.Type"/> used to manually exclude types if
/// you intend to register them manually. Use this parameter if you want to inject your own implementation
/// and therefore don't want the default type automatically registered.</param>
/// <param name="excludeRegEx">A regular expression that can be used to exclude types based on the type
/// name (excluding the namespace name). All types that match the regular expression will be excluded.</param>
public static void RegisterDefaultConventions(
Action<Type, Type> registerMethod,
Assembly[] interfaceAssemblies,
Expand All @@ -23,27 +43,52 @@ internal class CommonConventions

foreach (var interfaceType in interfaces)
{
if (!IsMatch(interfaceType, excludeTypes, excludeRegEx))
if (!IsExcludedType(interfaceType, excludeTypes, excludeRegEx))
{
List<Type> implementations = new List<Type>();

foreach (var assembly in implementationAssemblies)
implementations.AddRange(GetImplementationsOfInterface(assembly, interfaceType));
implementations.AddRange(GetImplementationsOfInterface(assembly, interfaceType).Where(implementation => !IsExcludedType(implementation, excludeTypes, excludeRegEx)).ToArray());

if (implementations.Count == 1)
// Prefer the default name ITypeName = TypeName
Type implementationType = implementations.Where(implementation => IsDefaultType(interfaceType, implementation)).FirstOrDefault();

if (implementationType == null)
{
// Fall back on ITypeName = ITypeNameAdapter
implementationType = implementations.Where(implementation => IsAdapterType(interfaceType, implementation)).FirstOrDefault();
}

if (implementationType != null)
{
var implementationType = implementations[0];
if (!IsMatch(implementationType, excludeTypes, excludeRegEx) && interfaceType.Name.Equals("I" + implementationType.Name))
{
System.Diagnostics.Debug.WriteLine("Auto registration of {1} : {0}", interfaceType.Name, implementationType.Name);
registerMethod(interfaceType, implementationType);
}
System.Diagnostics.Debug.WriteLine("Auto registration of {1} : {0}", interfaceType.Name, implementationType.Name);
registerMethod(interfaceType, implementationType);
}
}
}
}

// For DI containers that allow the use of a multiple registration method calls for individual implementations of a given interface

/// <summary>
/// Registers all of the types that implement the passed in interfaceTypes with the DI container so they can be
/// resolved as an <see cref="IEnumerable{T}"/> of values (where T is the interface type).
///
/// This overload is for DI containers that allow the use of multiple registration method calls, one for
/// each implementation of the interface.
/// </summary>
/// <param name="registerMethod">The method of the DI container used to register a single implementation, which will be
/// called one time per implementation found to register each implementation of the type. Should be in the format
/// (interfaceType, implementationType) => SomeMethod(interfaceType, implementationType) or similar.</param>
/// <param name="interfaceTypes">The interfaces to limit the registration to. If empty, no types will be registered.</param>
/// <param name="implementationAssemblies">An array of assemblies to scan for the implementation types.
/// This array should contain all of the assemblies where you have defined types that will be injected
/// into MvcSiteMapProvider.</param>
/// <param name="excludeTypes">An array of <see cref="System.Type"/> used to manually exclude types if
/// you intend to register them manually. Use this parameter if you want to inject your own implementation
/// and therefore don't want the default type automatically registered.</param>
/// <param name="excludeRegEx">A regular expression that can be used to exclude types based on the type
/// name (excluding the namespace name). All types that match the regular expression will be excluded.</param>
public static void RegisterAllImplementationsOfInterface(
Action<Type, Type> registerMethod,
Type[] interfaceTypes,
Expand All @@ -60,7 +105,7 @@ internal class CommonConventions

foreach (var implementationType in implementations)
{
if (!IsMatch(implementationType, excludeTypes, excludeRegEx))
if (!IsExcludedType(implementationType, excludeTypes, excludeRegEx))
{
System.Diagnostics.Debug.WriteLine("Auto registration of {1} : {0}", interfaceType.Name, implementationType.Name);
registerMethod(interfaceType, implementationType);
Expand All @@ -69,7 +114,26 @@ internal class CommonConventions
}
}

// For DI containers that require the use of a single registration method call for all implementations of a given interface
/// <summary>
/// Registers all of the types that implement the passed in interfaceTypes with the DI container so they can be
/// resolved as an <see cref="IEnumerable{T}"/> of values (where T is the interface type).
///
/// This overload is for DI containers that require the use of a multiple registration method call for
/// all implementations a given interface.
/// </summary>
/// <param name="registerMethod">The method of the DI container used to register an array of implementations, which will be
/// called only one time to register all of the implementations of the type. Should be in the format
/// (interfaceType, implementationTypes) => SomeMethod(interfaceType, implementationTypes) or similar, where
/// implementationTypes is a <see cref="System.Collections.Generic.IEnumerable{System.Type}"/>.</param>
/// <param name="interfaceTypes">The interfaces to limit the registration to. If empty, no types will be registered.</param>
/// <param name="implementationAssemblies">An array of assemblies to scan for the implementation types.
/// This array should contain all of the assemblies where you have defined types that will be injected
/// into MvcSiteMapProvider.</param>
/// <param name="excludeTypes">An array of <see cref="System.Type"/> used to manually exclude types if
/// you intend to register them manually. Use this parameter if you want to inject your own implementation
/// and therefore don't want the default type automatically registered.</param>
/// <param name="excludeRegEx">A regular expression that can be used to exclude types based on the type
/// name (excluding the namespace name). All types that match the regular expression will be excluded.</param>
public static void RegisterAllImplementationsOfInterfaceSingle(
Action<Type, IEnumerable<Type>> registerMethod,
Type[] interfaceTypes,
Expand All @@ -87,7 +151,7 @@ internal class CommonConventions

foreach (var implementationType in implementations)
{
if (!IsMatch(implementationType, excludeTypes, excludeRegEx))
if (!IsExcludedType(implementationType, excludeTypes, excludeRegEx))
{
matchingImplementations.Add(implementationType);
}
Expand All @@ -99,22 +163,38 @@ internal class CommonConventions
}


private static bool IsMatch(Type type, Type[] excludeTypes, string excludeRegEx)
private static bool IsExcludedType(Type type, Type[] excludeTypes, string excludeRegEx)
{
return IsMatch(type, excludeTypes) || IsMatch(type, excludeRegEx);
return IsExcludedType(type, excludeTypes) || IsExcludedType(type, excludeRegEx) || IsExcludedType(type);
}

private static bool IsMatch(Type type, Type[] excludeTypes)
private static bool IsExcludedType(Type type, Type[] excludeTypes)
{
return excludeTypes.Contains(type);
}

private static bool IsMatch(Type type, string excludeRegEx)
private static bool IsExcludedType(Type type, string excludeRegEx)
{
if (string.IsNullOrEmpty(excludeRegEx)) return false;
return Regex.Match(type.Name, excludeRegEx, RegexOptions.Compiled).Success;
}

private static bool IsExcludedType(Type type)
{
return type.GetCustomAttributes(typeof(MvcSiteMapProvider.DI.ExcludeFromAutoRegistrationAttribute), false).Length > 0;
}

private static bool IsDefaultType(Type interfaceType, Type implementationType)
{
return interfaceType.Name.Equals("I" + implementationType.Name);
}

private static bool IsAdapterType(Type interfaceType, Type implementationType)
{
return implementationType.Name.EndsWith("Adapter") &&
interfaceType.Name.Equals("I" + implementationType.Name.Substring(0, implementationType.Name.Length - 7));
}

private static IEnumerable<Type> GetInterfaces(Assembly assembly)
{
return assembly.GetTypes().Where(t => t.IsInterface);
Expand Down

0 comments on commit 4810c7c

Please sign in to comment.