<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      aspnetcore插件開發dll熱加載 二

      這一篇文章應該是個總結。

      投簡歷的時候是不是有人問我有沒有abp的開發經歷,汗顏!

      在各位大神的嘗試及自己的總結下,還是實現了業務和主機服務分離,通過dll動態的加載卸載,控制器動態的刪除添加。

      項目如下:

       

      演示效果:

       

      下面就是代碼部分:

      重點

      1.IActionDescriptorChangeProvider接口,(關于添加刪除可以通過后臺任務檢測刷新,移除控制器操作)

      2.builder.Services.AddControllers().ConfigureApplicationPartManager和AssemblyLoadContext搭配加載業務的dll(動態鏈接庫)。

      我的業務代碼很簡單,可能有人要說了,那復雜的業務,有很多業務類,注入這塊怎么辦,怎么實現整個的調用鏈。

      關于業務和主服務之間的關聯代碼就在這了

      namespace ModuleLib
      {
          //可以給個抽象類,默認實現。否則各個服務每次實現接口會多做一步刪除為實現接口的動作
          public interface IModule
          {
              void ConfigureService(IServiceCollection services, IConfiguration configuration=null);
              void Configure(IApplicationBuilder app, IWebHostEnvironment env = null);
          } 
      }

       

      看下面的項目,有沒有一點模塊化開發的感覺,但是這次分離的很徹底,只需要dll就行,不需要程序集引用。

      {
        "Modules": [
          {
            "id": "FirstWeb",
            "version": "1.0.0",
            "path": "C:\\Users\\victor.liu\\Documents\\GitHub\\AspNetCoreSimpleAop\\LastModule\\FirstWeb\\bin\\Debug\\net8.0"
          },
          {
            "id": "SecondService",
            "version": "1.0.0",
            "path": "C:\\Users\\victor.liu\\Documents\\GitHub\\AspNetCoreSimpleAop\\LastModule\\SecondService\\bin\\Debug\\net8.0"   //????csproj?????????????з???????????????????????????
          }
        ]
      }

      以Assembly為單位做存儲

      using System;
      using System.Collections.Generic;
      using System.Linq;
      using System.Reflection;
      using System.Text;
      using System.Threading.Tasks;
      
      namespace Common
      {
          public class ModuleInfo
          {
              public string Id { get; set; }
              public string Name { get; set; }
              public Version Version { get; set; }
              public string Path { get; set; } = "lib";
              public Assembly Assembly { get; set; }
          }
      }

      在初次加載的時候注入Imodule,并且緩存起來,這樣避免了反射的操作,之前的做法是通過反射來拿IModule

      using Common;
      using ModuleLib;
      using System.Reflection;
      
      namespace MainHost.ServiceExtensions
      {
          public static class InitModuleExt
          {
              public static void InitModule(this IServiceCollection services,IConfiguration configuration)
              {
                  var modules = configuration.GetSection("Modules").Get<List<ModuleInfo>>();
                  foreach (var module in modules)
                  {
                      GolbalConfiguration.Modules.Add(module);
                      module.Assembly = Assembly.LoadFrom($"{module.Path}\\{module.Id}.dll"); //測試才這么寫
      
                      var moduleType = module.Assembly.GetTypes().FirstOrDefault(t => typeof(IModule).IsAssignableFrom(t));
                      if ((moduleType != null) && (moduleType != typeof(IModule)))
                      {
                          services.AddSingleton(typeof(IModule), moduleType);
                      }
                  }
              }
          }
      }

       

      再看看Program是怎么寫的,等等,為什么注釋掉了重要的代碼呢

      using BigHost;
      using BigHost.AssemblyExtensions;
      using Common;
      using Microsoft.AspNetCore.Mvc;
      using Microsoft.AspNetCore.Mvc.ApplicationParts;
      using Microsoft.AspNetCore.Mvc.Infrastructure;
      using Microsoft.Extensions.Configuration;
      using ModuleLib;
      using System.Xml.Linq;
      using DependencyInjectionAttribute;
      
      var builder = WebApplication.CreateBuilder(args);
      builder.Configuration.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);
      builder.Configuration.AddJsonFile($"appsettings.{builder.Environment.EnvironmentName}.json", optional: true, reloadOnChange: true);
      builder.Configuration.AddJsonFile("appsettings.Modules.json", optional: false, reloadOnChange: true);
      //builder.Services.InitModule(builder.Configuration);
      //var sp = builder.Services.BuildServiceProvider();
      //var modules = sp.GetServices<IModule>();
      // Add services to the container.
      // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
      builder.Services.AddEndpointsApiExplorer();
      builder.Services.AddSwaggerGen();
      
      //最新dotnet沒有這些
      builder.Services.AddControllers().ConfigureApplicationPartManager(apm =>
      {
          var context = new CollectibleAssemblyLoadContext();
          DirectoryInfo DirInfo = new DirectoryInfo(Path.Combine(Directory.GetCurrentDirectory(), "lib"));
          foreach (var file in DirInfo.GetFiles("*.dll"))
          {
              //if(!(file.Name.Contains("Test001Controller") || file.Name.Contains("Test002Controller")))
              //{
              //    continue;
              //}//不能屏蔽掉依賴引用
              var assembly = context.LoadFromAssemblyPath(file.FullName);
              var controllerAssemblyPart = new AssemblyPart(assembly);
              apm.ApplicationParts.Add(controllerAssemblyPart);
              ExternalContexts.Add(file.Name, context);
          }
      });
          //builder.Services.AddTransient<IProductBusiness, ProductBusiness>();
          //foreach (var module in modules)
          //{
          //    module.ConfigureService(builder.Services, builder.Configuration);
          //}
          //GolbalConfiguration.Modules.Select(x => x.Assembly).ToList().ForEach(x =>
          //{
          //    builder.Services.ReisterServiceFromAssembly(x);
          //    var controllerAssemblyPart = new AssemblyPart(x);
          //    apm.ApplicationParts.Add(controllerAssemblyPart);
          //    ExternalContexts.Add(x.GetName().Name, context);
          //});
      //});
      //GolbalConfiguration.Modules.Select(x => x.Assembly).ToList().ForEach(x => builder.Services.ReisterServiceFromAssembly(x));
      builder.Services.AddSingleton<IActionDescriptorChangeProvider>(ActionDescriptorChangeProvider.Instance);
      builder.Services.AddSingleton(ActionDescriptorChangeProvider.Instance);
      
      
      
      var app = builder.Build();
      ServiceLocator.Instance = app.Services;
      //foreach (var module in modules)
      //{
      //    module.Configure(app, app.Environment);
      //}
      
      // Configure the HTTP request pipeline.
      if (app.Environment.IsDevelopment())
      {
          app.UseSwagger();
          app.UseSwaggerUI();
      }
      app.UseHttpsRedirection();
      
      
      app.MapGet("/Add", ([FromServices] ApplicationPartManager _partManager, string name) =>
      {
      
          FileInfo FileInfo = new FileInfo(Path.Combine(Directory.GetCurrentDirectory(), "lib/" + name + ".dll"));
          using (FileStream fs = new FileStream(FileInfo.FullName, FileMode.Open))
          {
              var context = new CollectibleAssemblyLoadContext();
              var assembly = context.LoadFromStream(fs);
              var controllerAssemblyPart = new AssemblyPart(assembly);
      
              _partManager.ApplicationParts.Add(controllerAssemblyPart);
      
              //ExternalContexts.Add(name + ".dll", context);
              ExternalContexts.Add(name, context);
      
              //更新Controllers
              ActionDescriptorChangeProvider.Instance.HasChanged = true;
              ActionDescriptorChangeProvider.Instance.TokenSource!.Cancel();
          }
          return "添加{name}controller成功";
      })
      .WithTags("Main")
      .WithOpenApi();
      
      app.MapGet("/Remove", ([FromServices] ApplicationPartManager _partManager, string name) =>
      {
          //if (ExternalContexts.Any(
          //    $"{name}.dll"))
          if (ExternalContexts.Any(
         $"{name}"))
          {
              var matcheditem = _partManager.ApplicationParts.FirstOrDefault(x => x.Name == name);
              if (matcheditem != null)
              {
                  _partManager.ApplicationParts.Remove(matcheditem);
                  matcheditem = null;
              }
              ActionDescriptorChangeProvider.Instance.HasChanged = true;
              ActionDescriptorChangeProvider.Instance.TokenSource!.Cancel();
              //ExternalContexts.Remove(name + ".dll");
              ExternalContexts.Remove(name);
              return $"成功移除{name}controller";
          }
          else
          {
              return "$沒有{name}controller";
          }
      });
      app.UseRouting(); //最新dotnet沒有這些
      app.MapControllers();  //最新dotnet沒有這些
      app.Run();

       

      這里先對上面的嘗試做個總結:

      模塊化開發通過IModule分離各個模塊解耦,通過dll把接口加入到主程序,很nice,但是,我還想更深入一層,把這個接口也一并做成可拔可插,這樣就不得不考慮如何動態的重載controller,這也沒問題。重中之重來了,上面的都做到了,但是我要的不僅僅是增加刪除一個controller,關聯的業務代碼發生了改變如何重載刷新,依賴注入這一塊繞不過去。并沒有好的解決辦法,就這樣項目戛然而止。

      目前有兩種解決辦法:

      1.加個中間層,通過反射去動態獲取業務實現

      2.業務實現通過new對象來拿。

      下面是代碼:

      using IOrder.Repository;
      using System;
      using System.Collections.Generic;
      using System.Linq;
      using System.Text;
      using System.Threading.Tasks;
      using Microsoft.Extensions.DependencyInjection;
      using System.Reflection;
      using System.Runtime.Loader;
      
      namespace AutofacRegister
      {
          public interface IRepositoryProvider
          {
              IRepository GetRepository(string serviceeName);
          }
          public class RepositoryProvider : IRepositoryProvider
          {
              private readonly Dictionary<string, (Assembly assembly, DateTime lastModified)> _assemblyCache = new Dictionary<string, (Assembly assembly, DateTime lastModified)>();
              private readonly Dictionary<string, IRepository> _typeCache = new Dictionary<string, IRepository>();
      
              public IRepository GetRepository(string serviceName)
              {
                  var path = $"{Directory.GetCurrentDirectory()}\\lib\\{serviceName}.Repository.dll";
                  var lastModified = File.GetLastWriteTimeUtc(path);
                  if (_assemblyCache.TryGetValue(path, out var cachedEntry) && cachedEntry.lastModified == lastModified)
                  {
                      // 使用緩存中的 Assembly 對象
                      return CreateInstanceFromAssembly(cachedEntry.assembly,serviceName);
                  }
                  else
                  {
                      // 加載并緩存新的 Assembly 對象
                      var assembly = LoadAssemblyFromFile(path);
                      _assemblyCache[path] = (assembly, lastModified);
                      return CreateInstanceFromAssembly(assembly,serviceName);
                  }
              }
      
              private Assembly LoadAssemblyFromFile(string path)
              {
                  var _AssemblyLoadContext = new AssemblyLoadContext(Guid.NewGuid().ToString("N"), true);
                  using (var fs = new FileStream(path, FileMode.Open, FileAccess.Read))
                  {
                      return _AssemblyLoadContext.LoadFromStream(fs);
                  }
              }
              private IRepository CreateInstanceFromAssembly(Assembly assembly,string serviceName)
              {
                  var  type_key = $"{assembly.FullName}_{serviceName}";
                  if(_typeCache.TryGetValue(type_key, out var cachedType))
                  {
                      return _typeCache[type_key];
                  }
                  var type = assembly.GetTypes()
                      .Where(t => typeof(IRepository).IsAssignableFrom(t) && !t.IsInterface)
                      .FirstOrDefault();
      
                  if (type != null)
                  {
                      var instance= (IRepository)Activator.CreateInstance(type);
                      _typeCache[type_key] = instance;
                      return instance;
                  }
                  else
                  {
                      throw new InvalidOperationException("No suitable type found in the assembly.");
                  }
              }
          }
      }

       

      所有的注入業務放到單獨的注入文件中,

      using Autofac;
      using IOrder.Repository;
      using Order.Repository;
      
      namespace AutofacRegister
      {
          public class RepositoryModule:Module
          {
              protected override void Load(ContainerBuilder builder)
              {
                  //builder.RegisterType<Repository>().As<IRepository>().SingleInstance();
                  builder.RegisterType<RepositoryProvider>().As<IRepositoryProvider>().InstancePerLifetimeScope();
              }
          }
      }

       

      上面的代碼可以再加一層代理,類似這樣

      using CustomAttribute;
      using System.Reflection;
      using ZURU_ERP.Base.Common.UnitOfWork;
      using ZURU_ERP.Base.Common;
      using ZURU_ERP.Base.Model;
      using System.Collections.Concurrent;
      
      namespace ZURU_ERP.Base.Reflect
      {
          public class MethodInfoCache
          {
              public string Name { get; set; }
              public Type ClassType { get; set; }
              public CusTransAttribute TransAttribute { get; set; }
      
              public List<CusActionAttribute> ActionAttributes { get; set; }
              public bool UseTrans => (TransAttribute == null);
              public bool UseAop => ActionAttributes.Any();
          }
          public class CusProxyGenerator<T> : DispatchProxy where T:class
          {
      
              private readonly ConcurrentDictionary<string, MethodInfoCache> _cache = new ConcurrentDictionary<string, MethodInfoCache>();
              private IBusiness<T> business;
              private  List<ICusAop> cusAop;
      
              protected override object? Invoke(MethodInfo? targetMethod, object?[]? args)
              {
                  #region 緩存優化 未經過測試
                  string methodKey = targetMethod.Name;
                  if (!_cache.ContainsKey(methodKey))
                  {
                      var classType = business.GetType();
                      var transAttribute = classType.GetMethod(targetMethod.Name).GetCustomAttributes<CusTransAttribute>().FirstOrDefault();
                      var actionAttributes = classType.GetMethod(targetMethod.Name).GetCustomAttributes<CusActionAttribute>().ToList();
                      _cache[methodKey] = new MethodInfoCache()
                      {
                          Name = methodKey,
                          ClassType = classType,
                          TransAttribute = transAttribute,
                          ActionAttributes = actionAttributes
                      };
                  }
                  var methodInfoCache = _cache[methodKey];
                  object result;
                  if (methodInfoCache.UseAop)
                  {
                      var actionnames = methodInfoCache.ActionAttributes.Select(x => x.Name).ToList();
                      var waitInvokes = cusAop.Where(x => actionnames.Contains(x.GetType().Name)).OrderBy(x => actionnames.IndexOf(x.GetType().Name)).ToList(); //排序
                      foreach (var item in waitInvokes)
                      {
                          item.Before(args);
                      }
      
                      result = methodInfoCache.UseTrans ? Trans(targetMethod, args, out result) : targetMethod.Invoke(business, args);
                      foreach (var item in waitInvokes)
                      {
                          item.After(new object[] { result });
                      }
                      return result;
                  }
                  else
                  {
                      return methodInfoCache.UseTrans ? Trans(targetMethod, args, out result) : targetMethod.Invoke(business, args);
                  } 
                  #endregion
      
                  #region 沒緩存原代碼 經過測試
      
                  //bool useTran = false;
                  //var classType = business.GetType();
                  //var useClassTrans = classType.GetCustomAttributes<CusTransAttribute>();
                  //if (useClassTrans.Any())
                  //{
                  //    useTran = true;
                  //}
                  //else
                  //{
                  //    useTran = classType.GetMethod(targetMethod.Name).GetCustomAttributes<CusTransAttribute>().Any();  //是否使用事務
                  //}
      
                  //var actionnames = classType.GetCustomAttributes<CusActionAttribute>().Select(x => x.Name).ToList();
      
                  //var waitInvokes = cusAop.Where(x => actionnames.Contains(x.GetType().Name)).OrderBy(x => actionnames.IndexOf(x.GetType().Name)).ToList(); //排序
      
                  //foreach (var item in waitInvokes)
                  //{
                  //    item.Before(args);
                  //}
      
                  //object result;
                  //if (useTran)
                  //{
                  //    return Trans(targetMethod, args, out result);
                  //}
                  //else
                  //{
                  //    result = targetMethod.Invoke(business, args);
                  //}
      
                  //foreach (var item in waitInvokes)
                  //{
                  //    item.After(new object[] { result });
                  //}
      
                  //return result;
                  #endregion
              }
      
              private object? Trans(MethodInfo? targetMethod, object?[]? args, out object result)
              {
                  var _unitOfWorkManage = App.GetService<IUnitOfWorkManage>();
      
                  Console.WriteLine($"{targetMethod.Name} transaction started.");
      
                  try
                  {
                      if (_unitOfWorkManage.TranCount <= 0)
                      {
                          Console.WriteLine($"Begin Transaction");
                          _unitOfWorkManage.BeginTran();
                      }
                      result = targetMethod.Invoke(business, args);
                      if (result is ApiResult apiResult && !apiResult.success)
                      {
                          Console.WriteLine("apiResult return false Transaction rollback.");
                          _unitOfWorkManage.RollbackTran();
                          return apiResult;
                      }
                      if (_unitOfWorkManage.TranCount > 0)
                          _unitOfWorkManage.CommitTran();
                      Console.WriteLine("Transaction Commit.");
                      Console.WriteLine($"{targetMethod.Name}  transaction succeeded.");
      
                      return result;
                  }
                  catch (Exception e)
                  {
                      _unitOfWorkManage.RollbackTran();
                      Console.WriteLine("Transaction Rollback.");
                      Console.WriteLine($"{targetMethod.Name}  transaction failed: " + e.Message);
                      throw;
                  }
              }
      
              public static IBusiness<T> Create(IBusiness<T> business, List<ICusAop> cusAop)
              {
                  object proxy = Create<IBusiness<T>, CusProxyGenerator<T>>();
                  ((CusProxyGenerator<T>)proxy).SetParameters(business, cusAop);
                  return (IBusiness<T>)proxy;
              }
      
              private void SetParameters(IBusiness<T> business, List<ICusAop> cusAop)
              {
                  this.business = business;
                  this.cusAop = cusAop;
              }
          }
      }

       

      由于這層代碼沒有走依賴注入,想用各種aop組件,靈活性稍微低了一點點。

      下面第二種直接在業務代碼中new對象也不是不可,這一層的前后需要的都可以注入到容器里面去。只不過這一層就想到包裝類一層不要在使用這個類的時候做過多的職責承擔

      using IBusiness;
      
      namespace Business
      {
          public class ProductBusiness : IDisposable// : IProductBusiness
          {
              public static readonly ProductBusiness Instance;
              private bool _disposed = false; 
              static ProductBusiness()
              {
                  Instance = new ProductBusiness();
              }
            
              private ProductBusiness()
              {
                  // 初始化資源
              }
              public async Task<int> AddProduct(string name, decimal price)
              {
                  await Task.CompletedTask;
                  return 1;
              }
      
              // 實現IDisposable接口
              public void Dispose()
              {
                  Dispose(true);
                  GC.SuppressFinalize(this);
              }
      
              protected virtual void Dispose(bool disposing)
              {
                  if (_disposed)
                      return;
      
                  if (disposing)
                  {
                      // 釋放托管資源
                  }
      
                  // 釋放非托管資源
                  _disposed = true;
              }
      
              // 析構函數
              ~ProductBusiness()
              {
                  Dispose(false);
              }
          }
      }

      使用的時候就直接拿實例:

        [HttpPost]
        public async Task<int> Add()
        {
            //using var scope = ServiceLocator.Instance.CreateScope();
            //var business = scope.ServiceProvider.GetRequiredService<IProductBusiness>();
            using var business = ProductBusiness.Instance;
            return await business.AddProduct("product1",12.1m);
        }

       

      demo源代碼:

      liuzhixin405/AspNetCoreSimpleAop (github.com)

       

      posted @ 2024-05-25 16:54  星仔007  閱讀(960)  評論(4)    收藏  舉報
      主站蜘蛛池模板: 人妻日韩人妻中文字幕| 三台县| 动漫AV纯肉无码AV电影网| 久久99精品久久久大学生| 精品国产AⅤ无码一区二区| 国产一区二区高清不卡| 欧美高清精品一区二区| 97久久久亚洲综合久久| 国产一区二区三区高清视频| 成人国产精品中文字幕| 无码福利写真片视频在线播放| 高潮潮喷奶水飞溅视频无码| 真实国产精品视频400部| 四虎成人精品永久免费av| 亚洲国模精品一区二区| 一本一道av无码中文字幕﹣百度| 亚洲中文字幕在线无码一区二区| 99久久精品视香蕉蕉| 亚洲精品成人区在线观看| 久久精品国产一区二区三| aa性欧美老妇人牲交免费| 免费观看激色视频网站| 男女啪啪18禁无遮挡激烈| 久久精品免视看国产成人| 久热这里只有精品视频六| 69精品丰满人妻无码视频a片| 亚洲乱妇老熟女爽到高潮的片| 国产精品二区中文字幕 | 潮喷无码正在播放| 天天综合色天天综合色h| 久视频久免费视频久免费| 精品国精品无码自拍自在线| 亚洲天堂精品一区二区| 一本色道久久88精品综合| 日本深夜福利在线观看| 国产精品一区二区蜜臀av| japanese无码中文字幕| 久99久热这里只有精品| 国产精品日韩精品日韩| 欧美老熟妇乱子伦牲交视频| 男女性高爱潮免费网站|