From 4810c7c98660a5aeb3c375a242231f2e0cf78577 Mon Sep 17 00:00:00 2001 From: Shad Storhaug Date: Sun, 9 Feb 2014 04:08:02 +0700 Subject: [PATCH] Updates to CommonConventions static helper class: 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. --- .../Shared/DI/CommonConventions.cs | 114 +++++++++++++++--- 1 file changed, 97 insertions(+), 17 deletions(-) diff --git a/src/MvcSiteMapProvider/CodeAsConfiguration/Shared/DI/CommonConventions.cs b/src/MvcSiteMapProvider/CodeAsConfiguration/Shared/DI/CommonConventions.cs index 7958b863..83e68e66 100644 --- a/src/MvcSiteMapProvider/CodeAsConfiguration/Shared/DI/CommonConventions.cs +++ b/src/MvcSiteMapProvider/CodeAsConfiguration/Shared/DI/CommonConventions.cs @@ -8,7 +8,27 @@ namespace DI { internal class CommonConventions { - // Single implementations of interface with matching name (minus the "I"). + /// + /// 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] + /// + /// 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. + /// 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 throughout your project. + /// 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. + /// An array of 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. + /// 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. public static void RegisterDefaultConventions( Action registerMethod, Assembly[] interfaceAssemblies, @@ -23,27 +43,52 @@ public static void RegisterDefaultConventions( foreach (var interfaceType in interfaces) { - if (!IsMatch(interfaceType, excludeTypes, excludeRegEx)) + if (!IsExcludedType(interfaceType, excludeTypes, excludeRegEx)) { List implementations = new List(); 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 + + /// + /// Registers all of the types that implement the passed in interfaceTypes with the DI container so they can be + /// resolved as an 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. + /// + /// 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. + /// The interfaces to limit the registration to. If empty, no types will be registered. + /// 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. + /// An array of 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. + /// 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. public static void RegisterAllImplementationsOfInterface( Action registerMethod, Type[] interfaceTypes, @@ -60,7 +105,7 @@ public static void RegisterAllImplementationsOfInterface( 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); @@ -69,7 +114,26 @@ public static void RegisterAllImplementationsOfInterface( } } - // For DI containers that require the use of a single registration method call for all implementations of a given interface + /// + /// Registers all of the types that implement the passed in interfaceTypes with the DI container so they can be + /// resolved as an 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. + /// + /// 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 . + /// The interfaces to limit the registration to. If empty, no types will be registered. + /// 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. + /// An array of 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. + /// 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. public static void RegisterAllImplementationsOfInterfaceSingle( Action> registerMethod, Type[] interfaceTypes, @@ -87,7 +151,7 @@ public static void RegisterAllImplementationsOfInterfaceSingle( foreach (var implementationType in implementations) { - if (!IsMatch(implementationType, excludeTypes, excludeRegEx)) + if (!IsExcludedType(implementationType, excludeTypes, excludeRegEx)) { matchingImplementations.Add(implementationType); } @@ -99,22 +163,38 @@ public static void RegisterAllImplementationsOfInterfaceSingle( } - 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 GetInterfaces(Assembly assembly) { return assembly.GetTypes().Where(t => t.IsInterface);