Last active
January 12, 2020 14:24
-
-
Save kamsar/460528edc3043b16bdccfdbd0ea0c552 to your computer and use it in GitHub Desktop.
Automatically register controllers with Sitecore 8.2 IoC container
This file contains 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.Globalization; | |
using System.Linq; | |
using System.Reflection; | |
using System.Runtime.CompilerServices; | |
using System.Text.RegularExpressions; | |
using System.Web.Mvc; | |
using Microsoft.Extensions.DependencyInjection; | |
namespace Sitecore.Foundation.DependencyInjection | |
{ | |
public static class ServiceCollectionExtensions | |
{ | |
[MethodImplAttribute(MethodImplOptions.NoInlining)] | |
public static void AddMvcControllersInCurrentAssembly(this IServiceCollection serviceCollection) | |
{ | |
AddMvcControllers(serviceCollection, Assembly.GetCallingAssembly()); | |
} | |
public static void AddMvcControllers(this IServiceCollection serviceCollection, params string[] assemblyFilters) | |
{ | |
var assemblyNames = new HashSet<string>(assemblyFilters.Where(filter => !filter.Contains('*'))); | |
var wildcardNames = assemblyFilters.Where(filter => filter.Contains('*')).ToArray(); | |
var assemblies = AppDomain.CurrentDomain.GetAssemblies().Where(assembly => | |
{ | |
var nameToMatch = assembly.GetName().Name; | |
if (assemblyNames.Contains(nameToMatch)) return true; | |
return wildcardNames.Any(wildcard => IsWildcardMatch(nameToMatch, wildcard)); | |
}) | |
.ToArray(); | |
AddMvcControllers(serviceCollection, assemblies); | |
} | |
public static void AddMvcControllers(this IServiceCollection serviceCollection, params Assembly[] assemblies) | |
{ | |
var controllers = GetTypesImplementing<IController>(assemblies) | |
.Where(controller => controller.Name.EndsWith("Controller", StringComparison.Ordinal)); | |
foreach (var controller in controllers) | |
{ | |
serviceCollection.AddTransient(controller); | |
} | |
// h/t Sean Holmesby and Akshay Sura: this adds Web API controller support | |
var apiControllers = GetTypesImplementing<ApiController>(assemblies) | |
.Where(controller => controller.Name.EndsWith("Controller", StringComparison.Ordinal)); | |
foreach (var apiController in apiControllers) | |
{ | |
serviceCollection.AddTransient(apiController); | |
} | |
} | |
public static Type[] GetTypesImplementing<T>(params Assembly[] assemblies) | |
{ | |
if (assemblies == null || assemblies.Length == 0) | |
{ | |
return new Type[0]; | |
} | |
var targetType = typeof(T); | |
return assemblies | |
.Where(assembly => !assembly.IsDynamic) | |
.SelectMany(GetExportedTypes) | |
.Where(type => !type.IsAbstract && !type.IsGenericTypeDefinition && targetType.IsAssignableFrom(type)) | |
.ToArray(); | |
} | |
private static IEnumerable<Type> GetExportedTypes(Assembly assembly) | |
{ | |
try | |
{ | |
return assembly.GetExportedTypes(); | |
} | |
catch (NotSupportedException) | |
{ | |
// A type load exception would typically happen on an Anonymously Hosted DynamicMethods | |
// Assembly and it would be safe to skip this exception. | |
return Type.EmptyTypes; | |
} | |
catch (ReflectionTypeLoadException ex) | |
{ | |
// Return the types that could be loaded. Types can contain null values. | |
return ex.Types.Where(type => type != null); | |
} | |
catch (Exception ex) | |
{ | |
// Throw a more descriptive message containing the name of the assembly. | |
throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, | |
"Unable to load types from assembly {0}. {1}", assembly.FullName, ex.Message), ex); | |
} | |
} | |
/// <summary> | |
/// Checks if a string matches a wildcard argument (using regex) | |
/// </summary> | |
private static bool IsWildcardMatch(string input, string wildcards) | |
{ | |
return Regex.IsMatch(input, "^" + Regex.Escape(wildcards).Replace("\\*", ".*").Replace("\\?", ".") + "$", RegexOptions.IgnoreCase); | |
} | |
} | |
} |
I added the change to the gist with credit; thanks!
To make it work for attribute routed ApiControllers, I changed it to add types implementing IHttpController instead of ApiController (without this change, the ApiControllers returned 404):
public static void AddMvcControllers(this IServiceCollection serviceCollection, params Assembly[] assemblies)
{
var controllers = GetTypesImplementing<IController>(assemblies)
.Where(controller => controller.Name.EndsWith("Controller", StringComparison.Ordinal));
foreach (var controller in controllers)
{
serviceCollection.AddTransient(controller);
}
var apiControllers = GetTypesImplementing<IHttpController>(assemblies)
.Where(controller => controller.Name.EndsWith("Controller", StringComparison.Ordinal));
foreach (var apiController in apiControllers)
{
serviceCollection.AddTransient(apiController);
}
}
Great! I owe you a beer.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Thanks to Sean Holmesby I found why my ApiController weren't working with Microsoft DI and here is it: