Skip to content

Instantly share code, notes, and snippets.

@davidfowl
Created August 23, 2017 03:48
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save davidfowl/a4674f854492d3d374f522f74aa5acb0 to your computer and use it in GitHub Desktop.
Save davidfowl/a4674f854492d3d374f522f74aa5acb0 to your computer and use it in GitHub Desktop.
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