知識全彙集 .Net Core 技術突破 | 如何實現一個模塊化方案一

簡介

模塊化的介紹一共2篇git

這一篇咱們實現一個功能很是簡單的StartupModules模塊化。github

第二篇咱們來實現一個ABP的模塊化效果。web

思考

其實來簡單想一下模塊化的實驗思路,寫個接口=>模塊類繼承該接口=>項目啓動反射檢索=>調用接口實現。
那麼具體到代碼實踐應該怎麼寫呢。app

開始

第一步

第一步就是寫一個模塊化接口類的嘛!
新建類 IStartupModuleide

而後寫一個反射檢索全文誰繼承了這個接口的方法
新建類 StartupModulesOptions模塊化


代碼解釋: Activator.CreateInstance 與指定參數匹配程度最高的構造函數來建立指定類型的實例
ps:白話文就是,你給我Type我給你建立個對應的實例
更一個有意思的是 Assembly.GetEntryAssembly()! 這個! 是否是很好奇怕
ps:我第一次看到這個語法也蒙了,問了好多人你們都沒用過,這個語法同TS中的斷言,是非null類型斷言,意思就是我斷言我這個方法返回的內容絕對不是null。函數

第二步

到這裏來看咱們是否是已經拿到了全部繼承接口的模塊那麼怎麼在該調用的地方調用呢,缺啥寫啥
學習

第三步

運行代碼也有了,我該怎麼調用呢。
接下來只要在 在Program 的 WebHost 調用.UseStartupModules() 流程就能夠加載咱們的 ConfigureServices 了測試

中間來插播一下

請讓我掏出來一個器大的東西來講 他就是: IStartupFilter ,這個東西是個啥東西呢。來看一段源碼ui

namespace Microsoft.AspNetCore.Hosting
{
    public interface IStartupFilter
    {
        Action<IApplicationBuilder> Configure(Action<IApplicationBuilder> next);
    }
}

這裏咱們看到了 Configure 他返回一個 IApplicationBuilder 他是怎麼用的呢
咱們新建一個空的Web項目的時候不知道有沒有注意過 UseStaticFiles 這個函數

public void Configure(IApplicationBuilder app)
        {
            app.UseStaticFiles();
            app.UseRouting();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllerRoute("default", "{controller=Home}/{action=Index}/{id?}");
            });
        }

在這個方法中,你能夠直接使用方法提供的IApplicationBuilder參數,而且能夠向其中添加各類中間件。使用IStartupFilter, 你能夠指定並返回一個Action類型的泛型委託,這意味你除了可使用方法提供的泛型委託配置IApplicationBuilder對象, 還須要返回一個泛型委託。

咱們來看一段代碼 Build(); 這個會調用BuildApplication方法

public class Program
{
    public static void Main(string[] args)
    {
        var host = new WebHostBuilder()
            .UseKestrel()    
            .UseContentRoot(Directory.GetCurrentDirectory())
            .UseStartup<Startup>()
            .Build();

        host.Run(); 
    }
}

private RequestDelegate BuildApplication()
{
    ..
    IApplicationBuilder builder = builderFactory.CreateBuilder(Server.Features);
    builder.ApplicationServices = _applicationServices;

    var startupFilters = _applicationServices.GetService<IEnumerable<IStartupFilter>>();
    Action<IApplicationBuilder> configure = _startup.Configure;
    foreach (var filter in startupFilters.Reverse())
    {
        configure = filter.Configure(configure);
    }

    configure(builder);

    return builder.Build();
}

首先,此方法建立IApplicationBuilder的實例,該實例將用於構建中間件管道,並將ApplicationServices設置爲已配置的DI容器。
接下來的代碼塊很意思。首先,從DI容器中獲取了一個集合IEnumerable<IStartupFilter>
咱們能夠配置多個IStartupFilter來造成一個管道,因此這個方法只是從容器中取出它們。
如今咱們經過循環遍歷每一個IStartupFilter(以相反的順序),傳入Startup.Configure方法,而後更新局部變量configure來建立Configure方法的管道。

第四步

咱們本身如何來實現 一個IStartupFilter 讓他幫咱們調用 Configure。

第五步

在 WebHostBuilderExtensions類 UseStartupModules 方法 ConfigureServices 下用 IStartupFilter 注入實現
這樣在Build() 的時候就會調用模塊的方法了

ActivatorUtilities.CreateInstance<ModulesStartupFilter>(sp, runner) // 第二個參數是在建立實例的時候 給構造函數注入的第一個參數

測試一下

新建 Core Web項目 在 Program.cs
Host.CreateDefaultBuilder(args)
 .ConfigureWebHostDefaults(webBuilder =>
    {
         // 進行模塊映射
         webBuilder.UseStartupModules().UseStartup<Startup>();
});

在 Startup.cs ConfigureServices和Configure 下打一個 Console.WriteLine

新建 類 HangfireStartupModule 繼承 IStartupModule
public class HangfireStartupModule : IStartupModule
{
        public void ConfigureServices(IServiceCollection services)
        {
            Console.WriteLine("HangfireStartupModule----ConfigureServices");
        }
        public void Configure(IApplicationBuilder app)
        {
            Console.WriteLine("HangfireStartupModule----Configure");
        }
}
結果

一個很是簡單的模塊化就完工了,固然這個是基礎版本,只能拿來借鑑思路學習下。

補充模塊的 ConfigureServices 和 Configure 傳遞上下文

新建類 ConfigureServicesContext 和 ConfigureMiddlewareContext

public class ConfigureMiddlewareContext
    {
        public ConfigureMiddlewareContext(IConfiguration configuration, IWebHostEnvironment hostingEnvironment, IServiceProvider serviceProvider, StartupModulesOptions options)
        {
            Configuration = configuration;
            HostingEnvironment = hostingEnvironment;
            ServiceProvider = serviceProvider;
            Options = options;
        }

        public IConfiguration Configuration { get; }

        public IWebHostEnvironment HostingEnvironment { get; }
        public IServiceProvider ServiceProvider { get; }

        public StartupModulesOptions Options { get; }
    }

    public class ConfigureServicesContext
    {
        public ConfigureServicesContext(IConfiguration configuration, IWebHostEnvironment hostingEnvironment, StartupModulesOptions options)
        {
            Configuration = configuration;
            HostingEnvironment = hostingEnvironment;
            Options = options;
        }

        public IConfiguration Configuration { get; }
        public IWebHostEnvironment HostingEnvironment { get; }
        public StartupModulesOptions Options { get; }
    }

修改 StartupModuleRunner 的方法

public void ConfigureServices(IServiceCollection services, IConfiguration configuration, IWebHostEnvironment hostingEnvironment)
        {
            var ctx = new ConfigureServicesContext(configuration, hostingEnvironment, _options);
            foreach (var cfg in _options.StartupModules)
            {
                cfg.ConfigureServices(services, ctx);
            }
        }

        public void Configure(IApplicationBuilder app, IConfiguration configuration, IWebHostEnvironment hostingEnvironment)
        {
            using (var scope = app.ApplicationServices.CreateScope()) {
                var ctx = new ConfigureMiddlewareContext(configuration, hostingEnvironment, scope.ServiceProvider, _options);

                foreach (var cfg in _options.StartupModules)
                {
                    cfg.Configure(app, ctx);
                }
            }
               
        }

鳴謝

玩雙截棍的熊貓、NETCore-大黃瓜

思路來源:https://github.com/henkmollema/StartupModules

友聯: https://github.com/DestinyCore/Destiny.Core.Flow

相關文章
相關標籤/搜索