Last active
December 16, 2019 09:18
-
-
Save takahirohonda/17f37e53a38d2869102398ea82e9ba90 to your computer and use it in GitHub Desktop.
ServiceCollectionExtensions - ASP.NET Core DI
This file contains hidden or 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
/* | |
* Based on Habitat | |
* https://github.com/Sitecore/Habitat/tree/master/src/Foundation/DependencyInjection/code | |
*/ | |
using System; | |
using System.Collections.Generic; | |
using System.Globalization; | |
using System.Linq; | |
using System.Reflection; | |
using System.Text.RegularExpressions; | |
using System.IO; | |
using Microsoft.Extensions.DependencyInjection; | |
namespace ApplicationCore.Extensions | |
{ | |
public static class ServiceCollectionExtensions | |
{ | |
public static void AddClassesWithServiceAttribute(this IServiceCollection serviceCollection, | |
params string[] assemblyFilters) | |
{ | |
var assemblies = GetAssemblies(assemblyFilters); | |
serviceCollection.AddClassesWithServiceAttribute(assemblies); | |
} | |
private static void AddClassesWithServiceAttribute(this IServiceCollection serviceCollection, | |
params Assembly[] assemblies) | |
{ | |
var typesWithAttributes = assemblies | |
.Where(assembly => !assembly.IsDynamic) | |
.SelectMany(GetExportedTypes) | |
.Where(type => !type.IsAbstract) | |
.Select(type => new | |
{ | |
type.GetCustomAttribute<ServiceAttribute>()?.Lifetime, | |
ServiceType = type, | |
ImplementationType = type.GetCustomAttribute<ServiceAttribute>()?.ServiceType | |
}) | |
.Where(t => t.Lifetime != null); | |
foreach (var type in typesWithAttributes) | |
{ | |
if (type.ImplementationType == null) | |
{ | |
serviceCollection.Add(type.ServiceType, type.Lifetime.Value); | |
} | |
else | |
{ | |
serviceCollection.Add(type.ImplementationType, type.ServiceType, type.Lifetime.Value); | |
} | |
} | |
} | |
private static void Add(this IServiceCollection serviceCollection, Type type, Lifetime lifetime) | |
{ | |
switch (lifetime) | |
{ | |
case Lifetime.Scoped: | |
serviceCollection.AddScoped(type); | |
break; | |
case Lifetime.Singleton: | |
serviceCollection.AddSingleton(type); | |
break; | |
case Lifetime.Transient: | |
serviceCollection.AddTransient(type); | |
break; | |
default: | |
throw new ArgumentOutOfRangeException(nameof(lifetime), lifetime, null); | |
} | |
} | |
private static void Add(this IServiceCollection serviceCollection, | |
Type serviceType, | |
Type implementationType, | |
Lifetime lifetime) | |
{ | |
switch (lifetime) | |
{ | |
case Lifetime.Scoped: | |
serviceCollection.AddScoped(serviceType, implementationType); | |
break; | |
case Lifetime.Singleton: | |
serviceCollection.AddSingleton(serviceType, implementationType); | |
break; | |
case Lifetime.Transient: | |
serviceCollection.AddTransient(serviceType, implementationType); | |
break; | |
default: | |
throw new ArgumentOutOfRangeException(nameof(lifetime), lifetime, null); | |
} | |
} | |
private static Assembly[] GetAssemblies(IEnumerable<string> assemblyFilters) | |
{ | |
var assemblies = new List<Assembly>(); | |
foreach (var assemblyFilter in assemblyFilters) | |
{ | |
assemblies.AddRange(AppDomain.CurrentDomain.GetAssemblies() | |
.Where(assembly => IsWildcardMatch(assembly.GetName().Name, assemblyFilter)) | |
.ToArray()); | |
} | |
return assemblies.ToArray(); | |
} | |
private static IEnumerable<Type> GetTypesImplementing(Type implementsType, | |
IEnumerable<Assembly> assemblies, | |
params string[] classFilter) | |
{ | |
var types = GetTypesImplementing(implementsType, assemblies.ToArray()); | |
if (classFilter != null && classFilter.Any()) | |
{ | |
types = types.Where(type => classFilter.Any(filter => IsWildcardMatch(type.FullName, filter))); | |
} | |
return types; | |
} | |
private static IEnumerable<Type> GetTypesImplementing(Type implementsType, params Assembly[] assemblies) | |
{ | |
if (assemblies == null || assemblies.Length == 0) | |
{ | |
return new Type[0]; | |
} | |
var targetType = implementsType; | |
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 (FileLoadException) | |
{ | |
// The assembly points to a not found assembly - ignore and continue | |
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); | |
} | |
} | |
private static bool IsWildcardMatch(string assemblyName, string wildcardStringToGetAssembly) | |
{ | |
return assemblyName == wildcardStringToGetAssembly | |
|| Regex.IsMatch(assemblyName,"^" + Regex.Escape(wildcardStringToGetAssembly) | |
.Replace("\\*", ".*") + "$", RegexOptions.IgnoreCase); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment