专栏首页dotNET知音知识全聚集 .Net Core 技术突破 | 如何实现一个模块化方案一

知识全聚集 .Net Core 技术突破 | 如何实现一个模块化方案一

简介

模块化的介绍一共2篇

这一篇我们实现一个功能非常简单的StartupModules模块化。

第二篇我们来实现一个ABP的模块化效果。

思考

其实来简单想一下模块化的实验思路,写个接口=>模块类继承该接口=>项目启动反射检索=>调用接口实现。 那么具体到代码实践应该怎么写呢。

开始

第一步

第一步就是写一个模块化接口类的嘛! 新建类 IStartupModule

然后写一个反射检索全文谁继承了这个接口的方法 新建类 StartupModulesOptions

代码解释:Activator.CreateInstance 与指定参数匹配程度最高的构造函数来创建指定类型的实例 ps:白话文就是,你给我Type我给你创建个对应的实例 更一个有意思的是 Assembly.GetEntryAssembly()! 这个! 是不是很好奇怕 ps:我第一次看到这个语法也蒙了,问了好多人大家都没用过,这个语法同TS中的断言,是非null类型断言,意思就是我断言我这个方法返回的内容绝对不是null。

第二步

到这里来看我们是不是已经拿到了所有继承接口的模块那么怎么在该调用的地方调用呢,缺啥写啥

第三步

运行代码也有了,我该怎么调用呢。 接下来只要在 在Program 的 WebHost 调用.UseStartupModules() 流程就可以加载我们的 ConfigureServices 了

中间来插播一下

请让我掏出来一个器大的东西来说 他就是: IStartupFilter ,这个东西是个啥东西呢。来看一段源码

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

本文分享自微信公众号 - dotNET知音(AAshiyou),作者:初久的私房菜

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-09-18

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • MassTransit Get Started->

    MassTransit本身定位轻量级的服务总线,并支持多种传输方式如:RabbitMQ、Azure Service Bus、ActiveMQ、Amazon SQ...

    李明成
  • .Net Core 环境下构建强大且易用的规则引擎

    在业务的早期时代,也许使用硬编码或者逻辑判断就可以满足要求。但随着业务的发展,越来越多的问题会暴露出来:

    李明成
  • 从AppDomain迁移到AssemblyLoadContext

    基本上AssemblyLoadContext是AppDomain的继承者,它提供相同而且更多的功能-除了安全边界(隔离)。最小的安全边界是进程,因此你将需要使用...

    李明成
  • 分布式中Redis实现Session终结篇

      上一篇使用Redis实现Session共享方式虽然可行,但是实际操作起来却很麻烦,现有代码已经是这个样子了,总不可能全部换掉吧!好吧,这是个很实际的问题,那...

    用户1168362
  • 广播站 | 全新3.0版本揭秘!本周四企点君教你新功能怎么用~

    ? 企点3.0版本重磅来袭 新功能有哪些?适用于怎样的企业业务场景? 有哪些功能优势和亮点? …… 本周四,企点君带你揭开 3.0新版本的神秘面纱 结合业务场...

    腾讯企点
  • Gradle多渠道打包(动态设定App名称,应用图标,替换常量,更改包名,变更渠道)

    最近有个需求一次要打包9个类型的App,而且常量和String.xml都有变量。虽然之前也是一直存在变量,但是每次也仅仅只打包一个。这让我每次改变量,打包9个。...

    用户2802329
  • xmake从入门到精通8:切换编译模式

    xmake是一个基于Lua的轻量级现代化c/c++的项目构建工具,主要特点是:语法简单易上手,提供更加可读的项目维护,实现跨平台行为一致的构建体验。

    ruki
  • 3018: [Usaco2012 Nov]Distant Pastures

    3018: [Usaco2012 Nov]Distant Pastures Time Limit: 1 Sec  Memory Limit: 128 MB Su...

    HansBug
  • .NET/C# 判断某个类是否是泛型类型或泛型接口的子类型

    2018-09-01 08:28

    walterlv
  • 一个简单的Windows Socket可复用框架

    一个简单的Windows Socket可复用框架 说起网络编程,无非是建立连接,发送数据,接收数据,关闭连接。曾经学习网络编程的时候用Java写了一些小的聊天程...

    Florian

扫码关注云+社区

领取腾讯云代金券