在使用软件时候,某个功能有没有可能使用时才加载DLL,而且不影响正常软件的使用呢?比如像插座一样,使用的时候插上电,不用的时候可以拔出插座,强调的.NET当然可以。这种主要用于需要在运行时增加额外的操作,或者替换目前使用的功能,可以考虑使用插件式的方式开发,以便实现灵活的可拆卸动态增加功能。在.NET framework中可以使用AppDomain来实现可拆卸动态增加功能,而在.NET Core也提供了可热插拔的上下文AssemblyLoadContext。本文就讲讲.NET Core热插拔的使用。
.NET Core实现dll文件热插拔使用步骤如下:.
1、新建一个公用的属性接口层
这个接口是主要记录插件类的名称,介绍和一个要实现的接口方法,也就是我们在插件中要调用的方法,这里的接口名称为ICommand,项目名称为IPlugIn。
public interface ICommand
{
string Name { get; }
string Description { get; }
string Execute();
}
2、创建插件的项目
创建需要实现插件的项目,并实现第一步的接口属性和方法。项目名称为PlugIn。
public class DoPlugin : ICommand
{
public string Name => "PlugIn";
public string Description => "Description PlugIn";
public string Execute()
{
var thisv = JsonSerializer.Serialize(this);
Console.WriteLine(thisv);
Console.WriteLine("插件方法中逻辑展示");
return "调用插件方法执行成功";
}
}
在项目属性中设置ProjectReference 下的Private节点为false,表示引用的类为公共程序集,默认为true,这里必须设置为false
<ItemGroup>
<ProjectReference Include="..\IPlugIn\IPlugIn.csproj">
<Private>false</Private>
</ProjectReference>
</ItemGroup>
3、在要使用插件的项目创建PluginLoadContext上下文
public class PluginLoadContext: AssemblyLoadContext
{
private AssemblyDependencyResolver _resolver;
public PluginLoadContext(string pluginPath, bool isCollectible) : base(isCollectible)
{
_resolver = new AssemblyDependencyResolver(pluginPath);
}
//加载依赖项
protected override Assembly Load(AssemblyName assemblyName)
{
string assemblyPath = _resolver.ResolveAssemblyToPath(assemblyName);
if (assemblyPath != null)
{
return LoadFromAssemblyPath(assemblyPath);
}
return null;
}
protected override IntPtr LoadUnmanagedDll(string unmanagedDllName)
{
string libraryPath = _resolver.ResolveUnmanagedDllToPath(unmanagedDllName);
if (libraryPath != null)
{
return LoadUnmanagedDllFromPath(libraryPath);
}
return IntPtr.Zero;
}
}
该类主要负责将给定路径的dll加载到当前应用程序域,静态方法用户获取实现了插件接口的实例。AssemblyLoadContext从概念上讲,加载上下文会创建一个用于加载、解析和可能卸载一组程序集的范围。
4、调用
//复制到任意位置,最好是项目下面,最好是绝对路径
string str = @"E:\net\PlugIn\bin\Release\net6.0\Plugin.dll";
string resultString = string.Empty;
// 创建 PluginLoadContext对象
var alc = new PluginLoadContext(str,true);
// 加载程序到上下文
Assembly assembly = alc.LoadFromAssemblyPath(str);
if(assembly)
//创建插件对象并调用
foreach (Type type in assembly.GetTypes())
{
if (typeof(ICommand).IsAssignableFrom(type))
{
ICommand result = Activator.CreateInstance(type) as ICommand;
if (result != null)
{
resultString = result.Execute();
break;
}
}
}
//卸载程序集上下文
alc.Unload();
Console.WriteLine(resultString);
Console.ReadKey();
输出结果:
调用成功。需要注意的是重复调用可能会出现内存溢出的问题,大家可以在读取的时候判断一下是否加载这个程序集。
5、结语
本文讲述了.NET Core中插件式开发的实现步骤,这种插件开发主要用于桌面版的程序,它的优点是可以热插拔,方便了程序升级维护和提高了用户体验度。本实例是在.NET6的基础上开发的。希望本文对您有所收获,欢迎留言或提出异议。