.NET Core插件式开发小实践

在使用软件时候,某个功能有没有可能使用时才加载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();

输出结果:

.NET Core插件式开发小实践

调用成功。需要注意的是重复调用可能会出现内存溢出的问题,大家可以在读取的时候判断一下是否加载这个程序集。

5、结语

    本文讲述了.NET Core中插件式开发的实现步骤,这种插件开发主要用于桌面版的程序,它的优点是可以热插拔,方便了程序升级维护和提高了用户体验度。本实例是在.NET6的基础上开发的。希望本文对您有所收获,欢迎留言或提出异议。

源码链接:
https://pan.baidu.com/s/1fIMro987msjWje_gIbC5uw?pwd=ux6g
 
参考:
https://learn.microsoft.com/zh-cn/dotnet/standard/assembly/unloadability