• aspnetcore插件开发dll热加载


    该项目比较简单,只是单纯的把业务的dll模块和controller的dll做了一个动态的添加删除处理,目的就是插件开发。由于该项目过于简单,请勿吐槽。复杂的后续可以通过泛型的实体、dto等做业务和接口的动态区分。

    项目结构如下:

     

    上面的两个模块是独立通过dll加载道项目中的

     

    repository动态的核心思想在此项目中是反射

    复制代码
    public interface IRepositoryProvider
    {
        IRepository GetRepository(string serviceeName);
    }
    public class RepositoryProvider : IRepositoryProvider
    {
        public IRepository GetRepository(string x)
        {
            var path = $"{Directory.GetCurrentDirectory()}\\lib\\{x}.Repository.dll";
            var _AssemblyLoadContext = new AssemblyLoadContext(Guid.NewGuid().ToString("N"), true);
            Assembly assembly = null;
            using (var fs = new FileStream(path, FileMode.Open, FileAccess.Read))
            {
                 assembly = _AssemblyLoadContext.LoadFromStream(fs);
            }
               
            //var assembly = Assembly.LoadFrom(path);
            var types = assembly.GetTypes()
                .Where(t => typeof(IRepository).IsAssignableFrom(t) && !t.IsInterface);
            return (IRepository)Activator.CreateInstance(types.First());
        }
    }
    复制代码

    通过一个provider注入来获取示例,这个repository的示例既然是动态热拔插,能想到暂时只能是反射来做这一块了。

    复制代码
    using Autofac;
    using IOrder.Repository;
    using Order.Repository;
    
    namespace AutofacRegister
    {
        public class RepositoryModule:Module
        {
            protected override void Load(ContainerBuilder builder)
            {
                //builder.RegisterType().As().SingleInstance();
                builder.RegisterType().As().InstancePerLifetimeScope();
            }
        }
    }
    复制代码

     

    controller插件这一块大同小异,这个控制器是通过程序集注入来实现的

    复制代码
      public class MyControllerFilter : IStartupFilter
      {
         
          private readonly PluginManager pluginManager;
          List<string> controllers = new List<string> { "First","Second" };
          public MyControllerFilter(PluginManager pluginManager)
          {
              this.pluginManager = pluginManager;
              controllers.ForEach(x => pluginManager.LoadPlugins($"{Directory.GetCurrentDirectory()}\\lib\\", $"{x}.Impl.dll"));
          }
          Action IStartupFilter.Configure(Action next)
          {
              BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly;
              return app =>
              {
    
                  app.UseRouting();
                  app.UseEndpoints(endpoints =>
                  {
                      foreach (IPlugin item in pluginManager.GetPlugins())
                      {
                          foreach (MethodInfo mi in item.GetType().GetMethods(bindingFlags))
                          {
                              endpoints.MapPost($"/{item.GetType().Name.Replace("Service", "")}/{mi.Name}", async (string parameters, HttpContext cotext) =>
                              {
    
                                  var task = (Task)mi.Invoke(item, new object[] { parameters });
                                  if (task is Task apiTask)
                                  {
                                      await apiTask;
    
                                      // 如果任务有返回结果
                                      if (apiTask is Task<object> resultTask)
                                      {
                                          var res = await resultTask;
                                          return Results.Ok(JsonConvert.SerializeObject(res));
                                      }
                                  }
    
                                  // 如果方法没有返回 Task,返回 NotFound
                                  return Results.NotFound("Method execution did not return a result.");
                              });
                          }
                      }
                  });
                  next(app);
              };
          }
      }
    复制代码

     

    但是有一个问题,它的变化势必需要重新渲染整个controller,我只能重启他的服务了。

    复制代码
    using Microsoft.Extensions.Hosting;
    using System;
    using System.Diagnostics;
    using System.IO;
    using System.Threading;
    using System.Threading.Tasks;
    
    namespace StartupDiagnostics
    {
        public class FileWatcherService : IHostedService
        {
            private readonly string _watchedFolder;
            private FileSystemWatcher _fileSystemWatcher;
            private readonly IHostApplicationLifetime _appLifetime;
            public FileWatcherService(IHostApplicationLifetime appLifetime)
            {
                _watchedFolder =Path.Combine(Directory.GetCurrentDirectory(),"lib"); //细化指定类型的dll
                _appLifetime = appLifetime;
            }
    
            public Task StartAsync(CancellationToken cancellationToken)
            {
                _fileSystemWatcher = new FileSystemWatcher(_watchedFolder);
                _fileSystemWatcher.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.DirectoryName;
                _fileSystemWatcher.Changed += OnFileChanged;
                _fileSystemWatcher.Created += OnFileChanged;
                _fileSystemWatcher.Deleted += OnFileChanged;
                _fileSystemWatcher.Renamed += OnFileChanged;
                _fileSystemWatcher.EnableRaisingEvents = true;
    
                return Task.CompletedTask;
            }
    
            public Task StopAsync(CancellationToken cancellationToken)
            {
                _fileSystemWatcher.Dispose();
                return Task.CompletedTask;
            }
    
            private void OnFileChanged(object sender, FileSystemEventArgs e)
            {
                // 文件夹内容发生变化时重新启动应用程序
                var processStartInfo = new ProcessStartInfo
                {
                    FileName = "dotnet",
                    Arguments = $"exec \"{System.Reflection.Assembly.GetEntryAssembly().Location}\"",
                    UseShellExecute = false
                };
    
              
                _appLifetime.ApplicationStopped.Register(() =>
                {
                    Process.Start(processStartInfo);
                });
                _appLifetime.StopApplication();
    
            }
        }
    }
    复制代码

     

    repository这一块页面效果没法展示,

    controllerr可以通过swagger来看看,first和second这可以通过删除dll和添加dll来增加和删除controller给第三方。

    这是控制台展示的重启效果

     

    源代码如下:

    liuzhixin405/AspNetCoreSimpleAop (github.com)

  • 相关阅读:
    R数据分析:孟德尔随机化实操
    Redis 实现 用户输入密码错误5次锁定
    远程教育:低代码重塑教育技术
    计算机前沿(从三次数学危机到量子计算)
    关键词抽取
    SQL语法
    第21章 Spring事务管理之扩展篇(三)
    【Python从入门到进阶】58、Pandas库中Series对象的操作(一)
    MASA Stack 第五期社区例会
    【机器学习】训练集/验证集/测试集释疑
  • 原文地址:https://www.cnblogs.com/morec/p/18161023