pschelpdesk/Vendor/Prise/DefaultPluginLoader.cs
2024-11-04 20:45:34 +01:00

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);
}
}
}