Created
August 23, 2017 03:48
-
-
Save davidfowl/a4674f854492d3d374f522f74aa5acb0 to your computer and use it in GitHub Desktop.
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.Linq; | |
using System.Reflection; | |
using System.Threading.Tasks; | |
using Microsoft.AspNetCore.Hosting; | |
using Microsoft.Extensions.DependencyInjection; | |
using Microsoft.Extensions.DependencyModel; | |
using WebApplication40; | |
[assembly: HostingStartup(typeof(AutoLoad))] | |
namespace WebApplication40 | |
{ | |
public class AutoLoad : IHostingStartup | |
{ | |
public void Configure(IWebHostBuilder builder) | |
{ | |
var appName = builder.GetSetting(WebHostDefaults.ApplicationKey); | |
var assemblies = DefaultAssemblyPartDiscoveryProvider.DiscoverAssemblyParts(appName).ToList(); | |
var servicesFuncs = new List<MethodInfo>(); | |
foreach (var a in assemblies) | |
{ | |
foreach (var type in a.GetExportedTypes()) | |
{ | |
if (type.Name.EndsWith("ServiceCollectionExtensions")) | |
{ | |
foreach (var method in type.GetMethods(BindingFlags.Public | BindingFlags.Static)) | |
{ | |
var args = method.GetParameters(); | |
if (method.Name.StartsWith("Add") && args.Length == 1 && args[0].ParameterType == typeof(IServiceCollection)) | |
{ | |
servicesFuncs.Add(method); | |
break; | |
} | |
} | |
} | |
} | |
} | |
builder.ConfigureServices(services => | |
{ | |
foreach (var f in servicesFuncs) | |
{ | |
try | |
{ | |
f.Invoke(null, new object[] { services }); | |
} | |
catch | |
{ | |
} | |
} | |
}); | |
} | |
} | |
// Discovers assemblies that are part of the MVC application using the DependencyContext. | |
public static class DefaultAssemblyPartDiscoveryProvider | |
{ | |
internal static HashSet<string> ReferenceAssemblies { get; } = new HashSet<string>(StringComparer.OrdinalIgnoreCase) | |
{ | |
"Microsoft.Extensions.DependencyInjection.Abstractions" | |
}; | |
public static IEnumerable<Assembly> DiscoverAssemblyParts(string entryPointAssemblyName) | |
{ | |
var entryAssembly = Assembly.Load(new AssemblyName(entryPointAssemblyName)); | |
var context = DependencyContext.Load(entryAssembly); | |
return GetCandidateAssemblies(entryAssembly, context); | |
} | |
internal static IEnumerable<Assembly> GetCandidateAssemblies(Assembly entryAssembly, DependencyContext dependencyContext) | |
{ | |
if (dependencyContext == null) | |
{ | |
// Use the entry assembly as the sole candidate. | |
return new[] { entryAssembly }; | |
} | |
return GetCandidateLibraries(dependencyContext) | |
.SelectMany(library => library.GetDefaultAssemblyNames(dependencyContext)) | |
.Select(Assembly.Load); | |
} | |
// Returns a list of libraries that references the assemblies in <see cref="ReferenceAssemblies"/>. | |
// By default it returns all assemblies that reference any of the primary MVC assemblies | |
// while ignoring MVC assemblies. | |
// Internal for unit testing | |
internal static IEnumerable<RuntimeLibrary> GetCandidateLibraries(DependencyContext dependencyContext) | |
{ | |
if (ReferenceAssemblies == null) | |
{ | |
return Enumerable.Empty<RuntimeLibrary>(); | |
} | |
var candidatesResolver = new CandidateResolver(dependencyContext.RuntimeLibraries, ReferenceAssemblies); | |
return candidatesResolver.GetCandidates(); | |
} | |
private class CandidateResolver | |
{ | |
private readonly IDictionary<string, Dependency> _runtimeDependencies; | |
public CandidateResolver(IReadOnlyList<RuntimeLibrary> runtimeDependencies, ISet<string> referenceAssemblies) | |
{ | |
var dependenciesWithNoDuplicates = new Dictionary<string, Dependency>(StringComparer.OrdinalIgnoreCase); | |
foreach (var dependency in runtimeDependencies) | |
{ | |
if (dependenciesWithNoDuplicates.ContainsKey(dependency.Name)) | |
{ | |
throw new InvalidOperationException("Resources.FormatCandidateResolver_DifferentCasedReference(dependency.Name)"); | |
} | |
dependenciesWithNoDuplicates.Add(dependency.Name, CreateDependency(dependency, referenceAssemblies)); | |
} | |
_runtimeDependencies = dependenciesWithNoDuplicates; | |
} | |
private Dependency CreateDependency(RuntimeLibrary library, ISet<string> referenceAssemblies) | |
{ | |
var classification = DependencyClassification.Unknown; | |
if (referenceAssemblies.Contains(library.Name)) | |
{ | |
classification = DependencyClassification.DependencyInjectionReference; | |
} | |
return new Dependency(library, classification); | |
} | |
private DependencyClassification ComputeClassification(string dependency) | |
{ | |
if (!_runtimeDependencies.ContainsKey(dependency)) | |
{ | |
// Library does not have runtime dependency. Since we can't infer | |
// anything about it's references, we'll assume it does not have a reference to Mvc. | |
return DependencyClassification.DoesNotReferenceDependencyInjection ; | |
} | |
var candidateEntry = _runtimeDependencies[dependency]; | |
if (candidateEntry.Classification != DependencyClassification.Unknown) | |
{ | |
return candidateEntry.Classification; | |
} | |
else | |
{ | |
var classification = DependencyClassification.DoesNotReferenceDependencyInjection ; | |
foreach (var candidateDependency in candidateEntry.Library.Dependencies) | |
{ | |
var dependencyClassification = ComputeClassification(candidateDependency.Name); | |
if (dependencyClassification == DependencyClassification.ReferencesDependencyInjection || | |
dependencyClassification == DependencyClassification.DependencyInjectionReference) | |
{ | |
classification = DependencyClassification.ReferencesDependencyInjection; | |
break; | |
} | |
} | |
candidateEntry.Classification = classification; | |
return classification; | |
} | |
} | |
public IEnumerable<RuntimeLibrary> GetCandidates() | |
{ | |
foreach (var dependency in _runtimeDependencies) | |
{ | |
if (ComputeClassification(dependency.Key) == DependencyClassification.ReferencesDependencyInjection) | |
{ | |
yield return dependency.Value.Library; | |
} | |
} | |
} | |
private class Dependency | |
{ | |
public Dependency(RuntimeLibrary library, DependencyClassification classification) | |
{ | |
Library = library; | |
Classification = classification; | |
} | |
public RuntimeLibrary Library { get; } | |
public DependencyClassification Classification { get; set; } | |
public override string ToString() | |
{ | |
return $"Library: {Library.Name}, Classification: {Classification}"; | |
} | |
} | |
private enum DependencyClassification | |
{ | |
Unknown = 0, | |
/// <summary> | |
/// References (directly or transitively) one of the Mvc packages listed in | |
/// <see cref="ReferenceAssemblies"/>. | |
/// </summary> | |
ReferencesDependencyInjection = 1, | |
/// <summary> | |
/// Does not reference (directly or transitively) one of the Mvc packages listed by | |
/// <see cref="ReferenceAssemblies"/>. | |
/// </summary> | |
DoesNotReferenceDependencyInjection = 2, | |
/// <summary> | |
/// One of the references listed in <see cref="ReferenceAssemblies"/>. | |
/// </summary> | |
DependencyInjectionReference = 3, | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment