<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          知識全聚集 .Net Core 技術(shù)突破 | 如何實現(xiàn)一個模塊化方案二

          共 15076字,需瀏覽 31分鐘

           ·

          2020-09-23 16:16

          https://www.cnblogs.com/MrChuJiu/p/13708035.html

          作者:初久的私房菜


          教程

          往期推薦

          知識全聚集 .Net Core 技術(shù)突破 | 如何實現(xiàn)一個模塊化方案一


          02 | 模塊化方案二

          其他教程預(yù)覽

          分庫分表項目實戰(zhàn)教程

          Git地址:https://github.com/MrChuJiu/EasyLogger

          01 | 前言

          02 | 簡單的分庫分表設(shè)計

          03 | 控制反轉(zhuǎn)搭配簡單業(yè)務(wù)

          04 | 強化設(shè)計方案

          05 | 完善業(yè)務(wù)自動創(chuàng)建數(shù)據(jù)庫

          06 | 最終篇-通過AOP自動連接數(shù)據(jù)庫-完成日志業(yè)務(wù)

          簡介

          開講第二篇,本篇代碼并非Copy的ABP,只是參考ABP的功能,進行的實現(xiàn)方案,讓代碼更加通俗易懂。代碼的講解思路和上一篇一樣,但是不引用上篇的寫法。

          開始

          第一步 基本操作

          還是老樣子,我們新建一個模塊化接口類
          新建接口 IAppModule (ps:項目中起的類名和方法名盡量對標(biāo)ABP)

             /// 
          /// 應(yīng)用模塊接口定義
          ///

          public interface IAppModule
          {
          ///
          /// 配置服務(wù)前
          ///

          ///
          void OnPreConfigureServices();
          ///
          /// 配置服務(wù)
          ///

          /// 配置上下文
          void OnConfigureServices();
          ///
          /// 配置服務(wù)后
          ///

          ///
          void OnPostConfigureServices();
          ///
          /// 應(yīng)用啟動前
          ///

          ///
          void OnPreApplicationInitialization();
          ///
          /// 應(yīng)用啟動
          ///

          ///
          void OnApplicationInitialization();
          ///
          /// 應(yīng)用啟動后
          ///

          ///
          void OnPostApplicationInitialization();
          ///
          /// 應(yīng)用停止
          ///

          ///
          void OnApplicationShutdown();
          }

          新建類 AppModule 繼承 IAppModule

             public abstract class AppModule : IAppModule
          {
          public virtual void OnPreConfigureServices()
          {

          }

          public virtual void OnConfigureServices()
          {

          }

          public virtual void OnPostConfigureServices()
          {

          }

          public virtual void OnPreApplicationInitialization()
          {

          }

          public virtual void OnApplicationInitialization()
          {

          }

          public virtual void OnPostApplicationInitialization()
          {

          }
          public virtual void OnApplicationShutdown()
          {

          }
          }

          第二步 預(yù)準(zhǔn)備

          這一步來完成ABP的DependsOnAttribute,通過特性進行引入模塊,
          這里參數(shù) params Type[] 因為一個模塊會依賴多個模塊
          新建類 DependsOnAttribute 繼承 Attribute

          /// 
          /// 模塊依賴的模塊
          ///

          [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
          public class DependsOnAttribute : Attribute
          {
          ///
          /// 依賴的模塊類型
          ///

          public Type[] DependModuleTypes { get; private set; }

          public DependsOnAttribute(params Type[] dependModuleTypes)
          {
          DependModuleTypes = dependModuleTypes ?? new Type[0];
          }
          }

          既然一個模塊會包含多個模塊的引用,那么就應(yīng)該有一個存儲的方式
          新建類 ModuleDescriptor 該類來存儲 自身和引用的其他模塊

              /// 
          /// 模塊描述
          ///

          public class ModuleDescriptor
          {
          private object _instance;

          ///
          /// 模塊類型
          ///

          public Type ModuleType { get; private set; }

          ///
          /// 依賴項
          ///

          public ModuleDescriptor[] Dependencies { get; private set; }

          ///
          /// 實例
          ///

          public object Instance
          {
          get
          {
          if (this._instance == null)
          {
          this._instance = Activator.CreateInstance(this.ModuleType);
          }
          return this._instance;
          }
          }

          public ModuleDescriptor(Type moduleType, params ModuleDescriptor[] dependencies)
          {
          this.ModuleType = moduleType;
          // 如果模塊依賴 為空給一個空數(shù)組
          this.Dependencies = dependencies ?? new ModuleDescriptor[0];
          }
          }

          第三步 模塊管理器

          來到核心步驟,這里我們寫模塊管理器,白話就是存儲模塊和模塊操作方法的一個類(同上一篇的StartupModulesOptions)
          第一步肯定是模塊的啟動
          我們新建 IModuleManager接口

           public interface IModuleManager : IDisposable
          {
          ///
          /// 啟動模塊
          ///

          ///
          void StartModule(IServiceCollection services)
          where TModule : IAppModule;
          }

          緊跟新建類 ModuleManager 繼承 IModuleManager, StartModule 先放在一邊
          這里的思路是:模塊是從一個入口的根模塊開始的慢慢的形成一個樹狀的引用關(guān)系,我們首先需要拿到所有的模塊引用,并把他們從樹葉為起點排列起來,依次注入。
          (理解為A=>B=>C 那么注入的順序應(yīng)該是 C=>B=>A)

          1.先來實現(xiàn)根絕入口遞歸獲取所有的引用關(guān)系 我已經(jīng)在方法中將每一步的注釋都寫上了

          /// 
          /// 獲取模塊依賴樹
          ///

          ///
          ///
          protected virtual List VisitModule(Type moduleType) {

          var moduleDescriptors = new List();
          // 是否必須被重寫|是否是接口|是否為泛型類型|是否是一個類或委托
          if (moduleType.IsAbstract || moduleType.IsInterface || moduleType.IsGenericType || !moduleType.IsClass) {
          return moduleDescriptors;
          }

          // 過濾沒有實現(xiàn)IRModule接口的類
          var baseInterfaceType = moduleType.GetInterface(_moduleInterfaceTypeFullName, false);
          if (baseInterfaceType == null)
          {
          return moduleDescriptors;
          }

          // 得到當(dāng)前模塊依賴了那些模塊
          var dependModulesAttribute = moduleType.GetCustomAttribute();
          // 依賴屬性為空
          if (dependModulesAttribute == null)
          {
          moduleDescriptors.Add(new ModuleDescriptor(moduleType));
          }
          else {
          // 依賴屬性不為空,遞歸獲取依賴
          var dependModuleDescriptors = new List();
          foreach (var dependModuleType in dependModulesAttribute.DependModuleTypes)
          {
          dependModuleDescriptors.AddRange(
          VisitModule(dependModuleType)
          );
          }
          // 創(chuàng)建模塊描述信息,內(nèi)容為模塊類型和依賴類型
          moduleDescriptors.Add(new ModuleDescriptor(moduleType, dependModuleDescriptors.ToArray()));
          }

          return moduleDescriptors;
          }
          補:_moduleInterfaceTypeFullName 定義
                  /// 
          /// 模塊接口類型全名稱
          ///

          public static string _moduleInterfaceTypeFullName = typeof(IAppModule).FullName;
          2.拿到依賴關(guān)系通過拓?fù)渑判蜻M行順序處理 (ps:拓?fù)渑判虻刂?https://www.cnblogs.com/myzony/p/9201768.html)

          新建類 Topological 這塊沒啥特別要講的根據(jù)鏈接去看下就好了

              /// 
          /// 拓?fù)渑判蚬ぞ哳?/span>
          ///

          public static class Topological
          {
          public static List Sort(IEnumerable source, Func> getDependencies) {

          var sorted = new List();
          var visited = new Dictionarybool>();

          foreach (var item in source)
          {
          Visit(item, getDependencies, sorted, visited);
          }

          return sorted;
          }

          static void Visit(T item, Func> getDependencies, List sorted, Dictionarybool> visited)
          {
          bool inProcess;
          var alreadyVisited = visited.TryGetValue(item, out inProcess);

          // 如果已經(jīng)訪問該頂點,則直接返回
          if (alreadyVisited)
          {
          // 如果處理的為當(dāng)前節(jié)點,則說明存在循環(huán)引用
          if (inProcess)
          {
          throw new ArgumentException("模塊出現(xiàn)循環(huán)依賴.");
          }
          }
          else
          {
          // 正在處理當(dāng)前頂點
          visited[item] = true;

          // 獲得所有依賴項
          var dependencies = getDependencies(item);
          // 如果依賴項集合不為空,遍歷訪問其依賴節(jié)點
          if (dependencies != null)
          {
          foreach (var dependency in dependencies)
          {
          // 遞歸遍歷訪問
          Visit(dependency, getDependencies, sorted, visited);
          }
          }

          // 處理完成置為 false
          visited[item] = false;
          sorted.Add(item);
          }
          }

          }

          回到 ModuleManager 新建方法 ModuleSort

           /// 
          /// 模塊排序
          ///

          ///
          ///
          public virtual List ModuleSort() where TModule : IAppModule
          {
          // 得到模塊樹依賴
          var moduleDescriptors = VisitModule(typeof(TModule));
          // 因為現(xiàn)在得到的數(shù)據(jù)是從樹根開始到樹葉 - 實際的注入順序應(yīng)該是從樹葉開始 所以這里需要對模塊進行排序
          return Topological.Sort(moduleDescriptors, o => o.Dependencies);
          }
          補:ModuleSort本來是個私有方法 后為了讓模塊使用者可以實現(xiàn)重寫,請在 IModuleManager 加入
                  /// 
          /// 模塊排序
          ///

          /// 啟動模塊類型
          /// 排序結(jié)果
          List ModuleSort()
          where TModule : IAppModule;

          3.模塊已經(jīng)可以通過方法拿到了就來實現(xiàn) StartModule 方法 篩選去重 依次進行注入, 并最終保存到全局對象中

                  /// 
          /// 模塊明細(xì)和實例
          ///

          public virtual IReadOnlyList ModuleDescriptors { get; protected set; }

          ///
          /// 入口 StartModule
          /// 我們通過傳遞泛型進來的 TModule 為起點
          /// 查找他的依賴樹
          ///

          ///
          ///
          public void StartModule(IServiceCollection services) where TModule : IAppModule
          {

          var moduleDescriptors = new List();

          var moduleDescriptorList = this.ModuleSort();
          // 去除重復(fù)的引用 進行注入
          foreach (var item in moduleDescriptorList)
          {
          if (moduleDescriptors.Any(o => o.ModuleType.FullName == item.ModuleType.FullName))
          {
          continue;
          }
          moduleDescriptors.Add(item);
          services.AddSingleton(item.ModuleType, item.Instance);
          }
          ModuleDescriptors = moduleDescriptors.AsReadOnly();
          }
          4.ModuleDescriptors既然存儲著我們的所有模塊,那么我們怎么執(zhí)行模塊的方法呢

          入口通過調(diào)用下面的方法進行模塊的方法調(diào)用

                  /// 
          /// 進行模塊的 ConfigurationService 方法調(diào)用
          ///

          ///
          ///
          ///
          public IServiceCollection ConfigurationService(IServiceCollection services, IConfiguration configuration) {

          foreach (var module in ModuleDescriptors)
          {
          (module.Instance as IAppModule)?.OnPreConfigureServices();
          }

          foreach (var module in ModuleDescriptors)
          {
          (module.Instance as IAppModule)?.OnConfigureServices();
          }

          foreach (var module in ModuleDescriptors)
          {
          (module.Instance as IAppModule)?.OnPostConfigureServices();
          }

          return services;
          }
          ///
          /// 進行模塊的 Configure 方法調(diào)用
          ///

          ///
          ///
          public IServiceProvider ApplicationInitialization(IServiceProvider serviceProvider)
          {
          foreach (var module in ModuleDescriptors)
          {
          (module.Instance as IAppModule)?.OnPreApplicationInitialization();
          }

          foreach (var module in ModuleDescriptors)
          {
          (module.Instance as IAppModule)?.OnApplicationInitialization();
          }

          foreach (var module in ModuleDescriptors)
          {
          (module.Instance as IAppModule)?.OnPostApplicationInitialization();
          }

          return serviceProvider;
          }
          ///
          /// 模塊銷毀
          ///

          public void ApplicationShutdown()
          {
          // todo我覺得這里有點問題問 易大師
          //var modules = ModuleDescriptors.Reverse().ToList();

          foreach (var module in ModuleDescriptors)
          {
          (module.Instance as IAppModule)?.OnApplicationShutdown();
          }
          }

          當(dāng)然還漏了一個模塊銷毀,該方法在主模塊被銷毀的時候調(diào)用(ps: 我個人思路應(yīng)該是從樹葉開始進行,但是ABP對模塊順序進行了反轉(zhuǎn)從根開始進行銷毀,所以這里同上)

                  /// 
          /// 主模塊銷毀的時候 銷毀子模塊
          ///

          public void Dispose()
          {
          this.Dispose(true);
          }

          protected virtual void Dispose(bool state)
          {
          this.ApplicationShutdown();

          }

          第四步 Extensions

          模塊管理器寫完了,那么這個方法如何調(diào)用呢來寫我們的 Extensions
          新建 RivenModuleServiceCollectionExtensions 類,讓其完成ConfigurationService方法的模塊調(diào)用

           /// 
          /// 模塊服務(wù)擴展
          ///

          public static class RivenModuleServiceCollectionExtensions
          {
          ///
          /// 添加Riven模塊服務(wù)
          ///

          ///
          ///
          ///
          ///
          public static IServiceCollection AddRivenModule(this IServiceCollection services, IConfiguration configuration)
          where TModule : IAppModule
          {
          var moduleManager = new ModuleManager();
          // 將模塊都查詢排序好
          moduleManager.StartModule(services);
          // 調(diào)用模塊 和 子模塊的ConfigurationService方法
          moduleManager.ConfigurationService(services, configuration);
          // 注入全局的 IModuleManager
          services.TryAddSingleton(moduleManager);
          return services;
          }
          }

          新建 RivenModuleIApplicationBuilderExtensions 類 ,讓其完成Configuration方法的模塊調(diào)用

           public static class RivenModuleIApplicationBuilderExtensions
          {
          ///
          /// 使用RivenModule
          ///

          ///
          ///
          public static IServiceProvider UseRivenModule(this IServiceProvider serviceProvider)
          {
          var moduleManager = serviceProvider.GetService();

          return moduleManager.ApplicationInitialization(serviceProvider);
          }
          }

          第五步 測試

          新建一個測試項目,引入寫好的模塊化類庫,在 ConfigureServices 中調(diào)用

          services.AddRivenModule(Configuration);

          Configure 中調(diào)用

           app.ApplicationServices.UseRivenModule();

          模塊銷毀演示(ps:這個是演示效果、實際是在項目停止的時候進行。)

           app.Map("/ApplicationShutdown", _ =>
          {
          _.Run((context) =>
          {
          var moduleManager = app.ApplicationServices.GetService();
          moduleManager.ApplicationShutdown();
          return Task.FromResult(0);
          });
          });

          補:

          新建 MyAppStartupModule、TestModuleA、TestModuleB 繼承AppModule。
          MyAppStartupModule作為入口模塊 引用 A => B 然后在模塊方法中打印 Console.WriteLine 看效果

          補充 給模塊傳遞參數(shù)

          新建 ApplicationInitializationContext 類

          public class ApplicationInitializationContext
          {
          public IServiceProvider ServiceProvider { get; }

          public IConfiguration Configuration { get; }

          public ApplicationInitializationContext([NotNull] IServiceProvider serviceProvider, [NotNull] IConfiguration configuration)
          {
          ServiceProvider = serviceProvider;
          Configuration = configuration;
          }
          }

          新建 ApplicationShutdownContext 類

           public class ApplicationShutdownContext
          {
          public IServiceProvider ServiceProvider { get; }

          public ApplicationShutdownContext([NotNull] IServiceProvider serviceProvider)
          {
          ServiceProvider = serviceProvider;
          }
          }

          新建 ServiceConfigurationContext 類

           public class ServiceConfigurationContext
          {
          public IServiceCollection Services { get; protected set; }

          public IConfiguration Configuration { get; protected set; }


          public ServiceConfigurationContext(IServiceCollection services, IConfiguration configuration)
          {
          Services = services;
          Configuration = configuration;
          }

          }

          修改 IAppModule 接口, 模塊和實現(xiàn)都自己手動都同步一下

              /// 
          /// 應(yīng)用模塊接口定義
          ///

          public interface IAppModule
          {
          ///
          /// 配置服務(wù)前
          ///

          ///
          void OnPreConfigureServices(ServiceConfigurationContext context);

          ///
          /// 配置服務(wù)
          ///

          /// 配置上下文
          void OnConfigureServices(ServiceConfigurationContext context);

          ///
          /// 配置服務(wù)后
          ///

          ///
          void OnPostConfigureServices(ServiceConfigurationContext context);

          ///
          /// 應(yīng)用啟動前
          ///

          ///
          void OnPreApplicationInitialization(ApplicationInitializationContext context);

          ///
          /// 應(yīng)用啟動
          ///

          ///
          void OnApplicationInitialization(ApplicationInitializationContext context);

          ///
          /// 應(yīng)用啟動后
          ///

          ///
          void OnPostApplicationInitialization(ApplicationInitializationContext context);

          ///
          /// 應(yīng)用停止
          ///

          ///
          void OnApplicationShutdown(ApplicationShutdownContext context);
          }

          修改 ModuleManager的 ConfigurationService、ApplicationInitialization、ApplicationShutdown 方法給調(diào)用傳遞對應(yīng)參數(shù)
          這部分代碼我就不貼了,會的大佬都能自己寫,想看的去我的github直接下載源碼看吧,麻煩老板們給點個星星?。?!

          項目地址

          知識全聚集,逐個擊破:https://github.com/MrChuJiu/Easy.Core.Flow

          鳴謝

          玩雙截棍的熊貓

          源地址:https://github.com/rivenfx/Modular


          瀏覽 51
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  粉嫩视频在线播放 | 国产日韩高清在线观看 | www.亚洲黄 | 日日夜夜AV | 亚洲AV系列 |