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