在使用软件时候,某个功能有没有可能使用时才加载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的基础上开发的。希望本文对您有所收获,欢迎留言或提出异议。
