翼度科技»论坛 编程开发 .net 查看内容

Natasha 插件化之dll

10

主题

10

帖子

30

积分

新手上路

Rank: 1

积分
30
调用外部dll来实现组件化

场景


  • 有一个设备管理控制系统,主要作用是控制设备及收集相关设备的信息,目前只集成了门禁和监控,后期期望添加更多设备时,一般都是在公司编写完后现场实施并调试,代码一般也是每个设备创建独立的项目,供总项目调用;慢慢的可能会演变出所有设备都继承一个公共的接口类,接口类中实现获取能力集和发送命令,以此来减少对于总控模块的修改,而此时只需要维护好能力集即可;可是这样每次也需要运行一整个解决方案,如果其他地方也需要这个系统,只能通过卸载项目来减少引用进行实时,这时就可以通过将项目分拆出去,通过Natasha进行组件化的管理。这样子的好处是通过约定的接口和能力集进行通信,主程序和设备耦合度低,如果遇到其他项目需要该系统,只需要将所需的dll放入特定文件夹即可。
  • 任何可以分拆成模块的系统都可以按照组件化的逻辑进行开发,例如有业务流程的项目,每个步骤可有多个组件选项,可执行一个或者多个;或者说通过获得的插件来动态配置那一步应执行那个插件,通过插件的反馈判断是否应执行下一步操作。
好处


  • 低耦合,业务分拆,插件只需要关心插件接口及反馈即可,不需要关心核心系统的业务逻辑
  • 分工明确,每个人只需关注插件代码即可,项目整合后出现问题也好排查,未调用插件,则主系统有问题,调用插件结果与实际不符则插件有问题。
  • 维护方便,学习成本低,对于某一个特定插件,代码量会远远低于整个项目的代码量,而且每个业务都可以进行分拆。
实现


  • 获得Assembly
主要使用NatashaDomain类实例化的方法(源码位置:src\Natasha.Domain\Extension\NatashaDomainExtension.cs)
相关方法:
LoadPluginUseDefaultDependency        如果加载的dll已经被加载过了,则跳过
LoadPluginWithAllDependency                        不会判断高低版本,源码中的解释是默认的,感觉和Default类似
LoadPluginWithHighDependency                使用高版本的dll
LoadPluginWithLowDependency                 使用低版本的dll
参数说明:
string path                                                                                                                                                                                        必填,dll所在路径
Func? excludeAssembliesFunc = null        选填,需要排除的dll,返回true为排除引用,例如共同引用某个公用的dll(例如Utils.dll),此时可以选择使用哪个版本的dll

  • 找到clas类并且实例化
找到class类(默认所有插件的实现类为*Controller),也可以按照下面例子来
var type = assembly.GetTypes().Where(item => item.Name.IndexOf("Controller")!=-1).First();
实例化
var plugin = (IPluginClass)(Activator.CreateInstance(type)!);
例子


  • 创建两个底层项目

    • IPluginBase项目:用于创建插件使用的接口
      1. using PluginUtil;
      2. namespace IPluginBase
      3. {
      4.     public interface IPluginClass
      5.     {
      6.         /// <summary>
      7.         /// 初始化方法
      8.         /// </summary>
      9.         public void initialize();
      10.         /// <summary>
      11.         /// 获得插件特有的方法
      12.         /// </summary>
      13.         /// <returns></returns>
      14.         public List<FruitFunction> getFunction();
      15.         /// <summary>
      16.         /// 获得需要定时方法
      17.         /// </summary>
      18.         /// <returns></returns>
      19.         public List<FruitFunction> getTimeFunc();
      20.         /// <summary>
      21.         /// 通过方法执行插件代码
      22.         /// </summary>
      23.         /// <param name="function">FruitFunction</param>
      24.         /// <param name="param">可能输入的数值</param>
      25.         /// <returns></returns>
      26.         public String execute(FruitFunction function, String param);
      27.     }
      28. }
      复制代码
    • PluginUtil项目,用于声明FruitFunction
      1. using System.ComponentModel;
      2. namespace PluginUtil
      3. {
      4.     public enum FruitFunction
      5.     {
      6.         [Description("硬度")]
      7.         hardness = 01,
      8.         [Description("苹果特有功能")]
      9.         appleAttr = 02,
      10.         [Description("切")]
      11.         cut = 03
      12.     }
      13. }
      复制代码

  • 创建两个插件项目

    • PluginApple项目
      1. using IPluginBase;
      2. using PluginUtil;
      3. namespace PluginApple
      4. {
      5.     public class PluginAppleClass : IPluginClass
      6.     {
      7.         public string execute(FruitFunction function, string param)
      8.         {
      9.             switch (function) {
      10.                 case FruitFunction.cut:
      11.                     Console.WriteLine("切苹果");
      12.                     break;
      13.                 case FruitFunction.hardness:
      14.                     Console.WriteLine("苹果很脆");
      15.                     break;
      16.                 case FruitFunction.appleAttr:
      17.                     Console.WriteLine("苹果特有属性");
      18.                     break;
      19.             }
      20.             return "结束";
      21.         }
      22.         public List<FruitFunction> getFunction()
      23.         {
      24.             Console.WriteLine("返回苹果的现有功能");
      25.             return new List<FruitFunction>() { FruitFunction.appleAttr, FruitFunction.cut, FruitFunction.hardness };
      26.         }
      27.         public List<FruitFunction> getTimeFunc()
      28.         {
      29.             Console.WriteLine("返回苹果的定时功能");
      30.             return new List<FruitFunction>() { FruitFunction.cut};
      31.         }
      32.         public void initialize()
      33.         {
      34.             Console.WriteLine("苹果初始化完成");
      35.         }
      36.     }
      37. }
      复制代码
    • PluginBanana项目
      1. using IPluginBase;
      2. using PluginUtil;
      3. namespace PluginBanana
      4. {
      5.     public class PluginBananaClass : IPluginClass
      6.     {
      7.         public string execute(FruitFunction function, string param)
      8.         {
      9.             switch (function)
      10.             {
      11.                 case FruitFunction.cut:
      12.                     Console.WriteLine("切香蕉");
      13.                     break;
      14.                 case FruitFunction.hardness:
      15.                     Console.WriteLine("香蕉很软");
      16.                     break;
      17.             }
      18.             return "结束";
      19.         }
      20.         public List<FruitFunction> getFunction()
      21.         {
      22.             Console.WriteLine("返回香蕉的现有功能");
      23.             return new List<FruitFunction>() { FruitFunction.cut, FruitFunction.hardness };
      24.         }
      25.         public List<FruitFunction> getTimeFunc()
      26.         {
      27.             Console.WriteLine("返回香蕉的定时功能");
      28.             return new List<FruitFunction>() { FruitFunction.cut };
      29.         }
      30.         public void initialize()
      31.         {
      32.             Console.WriteLine("香蕉初始化完成");
      33.         }
      34.     }
      35. }
      复制代码

  • 使用Natasha实现两个插件项目的方法
    前置条件:将PluginApple.dll和PluginBanana.dll放到NatashaStudyConsole.exe同级下的plugins文件夹中
    1. using IPluginBase;
    2. using PluginUtil;
    3. using System;
    4. using System.Collections.Generic;
    5. using System.Linq;
    6. using System.Reflection;
    7. using System.Text;
    8. using System.Threading.Tasks;
    9. namespace NatashaStudyConsole
    10. {
    11.     internal class PluginDemo
    12.     {
    13.         public void PluginMethod() {
    14.             //获得dll存放路径
    15.             string path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory+ "plugins");
    16.             //实例化NatashaDomain
    17.             NatashaDomain domain = new(Guid.NewGuid().ToString());
    18.             List<IPluginClass> assemblies = new List<IPluginClass>();
    19.             //通过获得所有的dll来进行实例化
    20.             Directory.GetFiles(path, "*.dll").ToList().ForEach(dll =>
    21.             {
    22.                 //加载dll
    23.                 var assembly = domain.LoadPluginWithAllDependency(dll);
    24.                 // 本例子中项目名称为"A",需要实例化的类为"AClass",因此使用IndexOf方法
    25.                 // 获得项目名称
    26.                 var asmName = assembly.GetName().Name!;
    27.                 // 根据项目名称判断应该实例那个class
    28.                 var type = assembly.GetTypes().Where(item => item.Name.IndexOf(asmName)!=-1).First();
    29.                 // 实例化
    30.                 var plugin = (IPluginClass)(Activator.CreateInstance(type)!);
    31.                 if (plugin != null) {
    32.                     // 保存
    33.                     assemblies.Add(plugin);
    34.                 }
    35.             });
    36.             assemblies.ForEach(assembly => {
    37.                 // 实现相关方法
    38.                 List<FruitFunction> functions = assembly.getFunction();
    39.                 if(functions != null && functions.Count > 0)
    40.                 {
    41.                     assembly.execute(functions[0], "");
    42.                 }
    43.             });
    44.         }
    45.     }
    46. }
    复制代码
    代码结构示意图:

    执行结果:


来源:https://www.cnblogs.com/wanghun315/archive/2023/05/20/17417520.html
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x

举报 回复 使用道具