178 lines
7.5 KiB
C#
178 lines
7.5 KiB
C#
using System;
|
|
using System.Collections.Concurrent;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Threading.Tasks;
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
using Prise.Activation;
|
|
using Prise.AssemblyLoading;
|
|
using Prise.AssemblyScanning;
|
|
using Prise.Proxy;
|
|
using Prise.Utils;
|
|
|
|
namespace Prise
|
|
{
|
|
public class DefaultPluginLoader : IPluginLoader
|
|
{
|
|
private readonly IAssemblyScanner assemblyScanner;
|
|
private readonly IPluginTypeSelector pluginTypeSelector;
|
|
private readonly IAssemblyLoader assemblyLoader;
|
|
private readonly IParameterConverter parameterConverter;
|
|
private readonly IResultConverter resultConverter;
|
|
private readonly IPluginActivator pluginActivator;
|
|
protected readonly ConcurrentBag<IPluginLoadContext> pluginContexts;
|
|
public DefaultPluginLoader(
|
|
IAssemblyScanner assemblyScanner,
|
|
IPluginTypeSelector pluginTypeSelector,
|
|
IAssemblyLoader assemblyLoader,
|
|
IParameterConverter parameterConverter,
|
|
IResultConverter resultConverter,
|
|
IPluginActivator pluginActivator)
|
|
{
|
|
this.assemblyScanner = assemblyScanner;
|
|
this.pluginTypeSelector = pluginTypeSelector;
|
|
this.assemblyLoader = assemblyLoader;
|
|
this.parameterConverter = parameterConverter;
|
|
this.resultConverter = resultConverter;
|
|
this.pluginActivator = pluginActivator;
|
|
this.pluginContexts = new ConcurrentBag<IPluginLoadContext>();
|
|
}
|
|
|
|
public async Task<AssemblyScanResult> FindPlugin<T>(string pathToPlugin)
|
|
{
|
|
return
|
|
(await this.FindPlugins<T>(pathToPlugin))
|
|
.FirstOrDefault();
|
|
}
|
|
|
|
public async Task<AssemblyScanResult> FindPlugin<T>(string pathToPlugins, string plugin)
|
|
{
|
|
return
|
|
(await this.FindPlugins<T>(pathToPlugins))
|
|
.FirstOrDefault(p => p.AssemblyPath.Split(Path.DirectorySeparatorChar).Last().Equals(plugin));
|
|
}
|
|
|
|
public Task<IEnumerable<AssemblyScanResult>> FindPlugins<T>(string pathToPlugins)
|
|
{
|
|
return this.assemblyScanner.Scan(new AssemblyScannerOptions
|
|
{
|
|
StartingPath = pathToPlugins,
|
|
PluginType = typeof(T)
|
|
});
|
|
}
|
|
|
|
public async Task<T> LoadPlugin<T>(AssemblyScanResult scanResult, string hostFramework = null, Action<PluginLoadContext> configureLoadContext = null)
|
|
{
|
|
hostFramework = hostFramework.ValueOrDefault(HostFrameworkUtils.GetHostframeworkFromHost());
|
|
|
|
var pathToAssembly = Path.Combine(scanResult.AssemblyPath, scanResult.AssemblyName);
|
|
var pluginLoadContext = PluginLoadContext.DefaultPluginLoadContext(pathToAssembly, typeof(T), hostFramework);
|
|
// This allows the loading of netstandard plugins
|
|
pluginLoadContext.IgnorePlatformInconsistencies = true;
|
|
|
|
configureLoadContext?.Invoke(pluginLoadContext);
|
|
|
|
this.pluginContexts.Add(pluginLoadContext);
|
|
|
|
var pluginAssembly = await this.assemblyLoader.Load(pluginLoadContext);
|
|
var pluginTypes = this.pluginTypeSelector.SelectPluginTypes<T>(pluginAssembly);
|
|
var pluginType = pluginTypes.FirstOrDefault(p => p.Name.Equals(scanResult.PluginType.Name));
|
|
if (pluginType == null)
|
|
throw new PluginLoadException($"Did not found any plugins to load from {nameof(AssemblyScanResult)} {scanResult.AssemblyPath} {scanResult.AssemblyName}");
|
|
|
|
return await this.pluginActivator.ActivatePlugin<T>(new DefaultPluginActivationOptions
|
|
{
|
|
PluginType = pluginType,
|
|
PluginAssembly = pluginAssembly,
|
|
ParameterConverter = this.parameterConverter,
|
|
ResultConverter = this.resultConverter,
|
|
HostServices = pluginLoadContext.HostServices
|
|
});
|
|
}
|
|
|
|
public async Task<IEnumerable<T>> LoadPlugins<T>(AssemblyScanResult scanResult, string hostFramework = null, Action<PluginLoadContext> configureLoadContext = null)
|
|
{
|
|
var plugins = new List<T>();
|
|
|
|
#if SUPPORTS_ASYNC_STREAMS
|
|
await foreach (var plugin in this.LoadPluginsAsAsyncEnumerable<T>(scanResult, hostFramework, configureLoadContext))
|
|
#else
|
|
foreach(var plugin in await this.LoadPluginsAsAsyncEnumerable<T>(scanResult, hostFramework, configureLoadContext))
|
|
#endif
|
|
plugins.Add(plugin);
|
|
|
|
return plugins;
|
|
}
|
|
|
|
#if SUPPORTS_ASYNC_STREAMS
|
|
public async IAsyncEnumerable<T> LoadPluginsAsAsyncEnumerable<T>(AssemblyScanResult scanResult, string hostFramework = null, Action<PluginLoadContext> configureLoadContext = null)
|
|
{
|
|
#else
|
|
public async Task<IEnumerable<T>> LoadPluginsAsAsyncEnumerable<T>(AssemblyScanResult scanResult, string hostFramework = null, Action<PluginLoadContext> configureLoadContext = null)
|
|
{
|
|
#endif
|
|
hostFramework = hostFramework.ValueOrDefault(HostFrameworkUtils.GetHostframeworkFromHost());
|
|
|
|
var pathToAssembly = Path.Combine(scanResult.AssemblyPath, scanResult.AssemblyName);
|
|
var pluginLoadContext = PluginLoadContext.DefaultPluginLoadContext(pathToAssembly, typeof(T), hostFramework);
|
|
// This allows the loading of netstandard plugins
|
|
pluginLoadContext.IgnorePlatformInconsistencies = true;
|
|
|
|
configureLoadContext?.Invoke(pluginLoadContext);
|
|
|
|
var pluginAssembly = await this.assemblyLoader.Load(pluginLoadContext);
|
|
|
|
this.pluginContexts.Add(pluginLoadContext);
|
|
|
|
var pluginTypes = this.pluginTypeSelector.SelectPluginTypes<T>(pluginAssembly);
|
|
|
|
#if SUPPORTS_ASYNC_STREAMS
|
|
foreach (var pluginType in pluginTypes)
|
|
yield return await this.pluginActivator.ActivatePlugin<T>(new DefaultPluginActivationOptions
|
|
{
|
|
PluginType = pluginType,
|
|
PluginAssembly = pluginAssembly,
|
|
ParameterConverter = this.parameterConverter,
|
|
ResultConverter = this.resultConverter,
|
|
HostServices = pluginLoadContext.HostServices
|
|
});
|
|
#else
|
|
var plugins = new List<T>();
|
|
foreach (var pluginType in pluginTypes)
|
|
plugins.Add( await this.pluginActivator.ActivatePlugin<T>(new DefaultPluginActivationOptions
|
|
{
|
|
PluginType = pluginType,
|
|
PluginAssembly = pluginAssembly,
|
|
ParameterConverter = this.parameterConverter,
|
|
ResultConverter = this.resultConverter,
|
|
HostServices = pluginLoadContext.HostServices
|
|
}));
|
|
return plugins;
|
|
#endif
|
|
}
|
|
|
|
public void UnloadAll()
|
|
{
|
|
foreach (var context in this.pluginContexts)
|
|
this.assemblyLoader.Unload(context);
|
|
|
|
this.pluginContexts.Clear();
|
|
}
|
|
|
|
protected bool disposed = false;
|
|
protected virtual void Dispose(bool disposing)
|
|
{
|
|
if (!this.disposed && disposing)
|
|
UnloadAll();
|
|
|
|
this.disposed = true;
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
Dispose(true);
|
|
GC.SuppressFinalize(this);
|
|
}
|
|
}
|
|
} |