public PluginInfo(Type type, PluginSetConstructionInfo constructionInfo) { Type = type; Ancestors = ImmutableArray.Create( type.GetAllBaseTypes().Select(t => (TypeRef)t).ToArray()); Interfaces = ImmutableArray.Create( type.GetInterfaces().Select(t => (TypeRef)t).ToArray()); CastableTo = ImmutableHashSet.Create( Ancestors.AddRange(Interfaces).Add(type).ToArray()); Capabilities = ImmutableDictionary <string, object> .Empty; Dependencies = ImmutableHashSet <TypeRef> .Empty; object?tmpPlugin = null; try { tmpPlugin = constructionInfo.PluginFactory.Create(type); } catch (Exception) { // There might be plugins we can't construct -- mainly, // b/c their constructors aren't written properly to support // temporary plugin factory. // All we can do here is to ignore this exception & assume // these plugins can't provide capabilities and dependencies. } if (tmpPlugin is IHasCapabilities hc) { Capabilities = hc.Capabilities; } if (tmpPlugin is IHasDependencies hd) { Dependencies = hd.Dependencies.Select(t => (TypeRef)t).ToImmutableHashSet(); } var allAssemblyRefs = constructionInfo.AllAssemblyRefs[type.Assembly]; AllDependencies = constructionInfo.Plugins .Where(p => p != type && ( allAssemblyRefs.Contains(p.Assembly) || CastableTo.Contains(p))) .Select(t => (TypeRef)t) .Concat(Dependencies) .ToImmutableHashSet(); }
public PluginSetInfo(IEnumerable <Type> plugins) { var ci = new PluginSetConstructionInfo() { Plugins = plugins.ToArray(), }; if (ci.Plugins.Length == 0) { // Super important to have this case handled explicitly. // Otherwise the initializer for PluginContainerConfiguration.Empty // will fail due to recursion here & attempt to register null as a // singleton inside BuildContainer call below. InfoByType = ImmutableDictionary <TypeRef, PluginInfo> .Empty; TypesByBaseType = ImmutableDictionary <TypeRef, ImmutableHashSet <TypeRef> > .Empty; TypesByBaseTypeOrderedByDependency = ImmutableDictionary <TypeRef, ImmutableArray <TypeRef> > .Empty; return; } HashSet <Assembly> GetAllDependencies(Assembly assembly, HashSet <Assembly>?result = null) { result ??= new HashSet <Assembly>(); foreach (var referenceName in assembly.GetReferencedAssemblies()) { var reference = Assembly.Load(referenceName); if (result.Add(reference)) { GetAllDependencies(reference, result); } } return(result); } var hAssemblies = ci.Plugins.Select(t => t.Assembly).ToHashSet(); ci.AllAssemblyRefs = hAssemblies .Select(a => ( Assembly: a, Refs: GetAllDependencies(a) .Where(a => hAssemblies.Contains(a)) .ToHashSet())) .ToDictionary(p => p.Assembly, p => p.Refs); ci.Assemblies = hAssemblies .OrderByDependency(a => ci.AllAssemblyRefs.GetValueOrDefault(a) ?? Enumerable.Empty <Assembly>()) .ToArray(); ci.PluginFactory = new PluginHostBuilder() .ConfigureServices((builder, services) => { services.AddSingleton <IPluginInfoQuery>(new PluginInfoQuery()); services.AddSingleton <IPluginFactory>(s => new QueryingPluginFactory(s)); }) .SetAutoStart(false) .Build() .GetRequiredService <IPluginFactory>(); var dPlugins = new Dictionary <TypeRef, PluginInfo>(); var dTypesByBaseType = new Dictionary <TypeRef, ImmutableHashSet <TypeRef> >(); foreach (var plugin in ci.Plugins) { var pluginInfo = new PluginInfo(plugin, ci); dPlugins.Add(plugin, pluginInfo); foreach (var baseType in pluginInfo.CastableTo) { var existingImpls = dTypesByBaseType.GetValueOrDefault(baseType) ?? ImmutableHashSet <TypeRef> .Empty; dTypesByBaseType[baseType] = existingImpls.Add(pluginInfo.Type); } } var orderedPlugins = dPlugins.Values .OrderByDependency(p => p.AllDependencies.Select(t => dPlugins[t])) .ToArray(); for (var i = 0; i < orderedPlugins.Length; i++) { orderedPlugins[i].OrderByDependencyIndex = i; } InfoByType = dPlugins.ToImmutableDictionary(); TypesByBaseType = dTypesByBaseType.ToImmutableDictionary(); TypesByBaseTypeOrderedByDependency = dTypesByBaseType .Select(p => new KeyValuePair <TypeRef, ImmutableArray <TypeRef> >( p.Key, p.Value .OrderBy(t => dPlugins[t].OrderByDependencyIndex) .ToImmutableArray())) .ToImmutableDictionary(); }