一、前言

在进行 Web 项目开发的过程中,可能会存在一些需要经常访问的静态数据,针对这种在程序运行过程中可能几乎不会发生变化的数据,我们可以尝试在程序运行前写入到缓存中,这样在系统后续使用时就可以直接从缓存中进行获取,从而减缓因为频繁读取这些静态数据造成的应用数据库服务器的巨大承载压力。

既然需要在程序运行前将静态数据写入到缓存中,毫无疑问我们需要在程序运行前执行一些自定义功能的代码,那么在本章中,我将会介绍如何在 ASP.NET Core 项目中,实现在程序启动前执行某些特定功能的代码。

二、Step by Step

1、先说结论

因为这一篇文章更多的是在说明我在解决这个问题时的一步步思考,并没有涉及到代码的编写,所以下面的内容可能对你的帮助并不是很大,所以这里提前将实现的方式告诉大家。对于这个问题来说,只需要将我们想要执行的代码放到下面代码中注释所在的位置,即可实现我们的需求。

public class Program
{
  public static void Main(string[] args)
  {
    var host = CreateHostBuilder(args).Build();     // do what you want     host.Run();
  } public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}

2、前车之鉴

在尝试如何在 ASP.NET Core 中实现这一功能需求前,我们可以看看在 .NET Framework 中如何实现这一功能,是不是可以对我们在后续的功能实现中提供某些借鉴。

对于采用 .NET Framework 的应用程序来说,项目创建后会生成一个 Global.asax 文件,在这个类文件中存在着 Application_Start 这样的一个方法,而 Application_Start 这个方法实际上是在当应用程序接收到第一个 HTTP 请求时触发,也就是说,当系统运行后第一次接收到用户的请求,就会触发 Application_Start 中的代码逻辑,后续不管再接收到多少的请求,都不会再触发该方法。

例如在这个基于 .NET Framework 构建的 MVC 项目模板中,在程序运行前需要执行注册路由信息、注册过滤器、注册使用 bundle 压缩后的 js、css 文件等等。

但是在 ASP.NET Core 项目中,并没有原生存在这样的方法,那么我们如何在 ASP.NET Core 应用中自己动手实现类似的功能呢?

3、后事之师

了解了在之前版本中的实现方式,现在我们仔细看看 Application_Start 这个方法中执行的每行代码的功能,是不是特别像我们在 ASP.NET Core 项目中使用的各种中间件?

然而,如果你有使用过 ASP.NET Core 后就会知道,ASP.NET Core 中的中间件是会在每次请求时都会触发的,虽然我们可以在我们自定义的中间件中设置缓存中不存在数据就写入,存在就直接跳过的代码逻辑,但是既然除了第一次访问时才会真正执行该中间件的功能,后面完全用不到,因此,对于我这种略微强迫症的童鞋来说,这个真的不能忍。。。

既然中间件不可以,而我们需要的仅仅是只运行一次,提到 .NET Core,不知道你的第一印象是什么,对于我个人来说,无处不在的依赖注入,可能是我在 18 年开始学习 .NET Core 时的第一印象。我们知道,对于 .NET Core 中原生的依赖注入组件,存在着三种生命周期:Singleton、Scoped 以及 Transient,对于这三种生命周期的具体解释,还是推荐博客园里蒋金楠老师的一篇文章(电梯直达)。

对于采用 Singleton 方式注入的服务来说,因为是一种类似于全局单例的形式,不管后续从何处进行访问,都会访问的是同一个实例,那么,这里是不是就可以在此基础上实现我们的需求了呢?

很不幸,这里其实是有个很严重的逻辑上的问题的,依赖注入最终的目的是为了实现将我们定义的服务契约与实现进行解耦,实现服务的消费者只需要告诉依赖注入容器自己所需要服务的类型(服务接口 or 抽象服务类),就能自动得到与之匹配的服务实例。

简单点说就是,消费方要告诉服务提供方你要开始使用某个服务了,我才能给你提供对应的服务,就像我们去饭店吃饭,在点了菜后,没有必要关心厨师是用天然气 or 煤气给你烧的菜,但是能不能上菜的关键在于我们有没有点菜。因此,这个问题最终还是落在了我们应该在程序中的什么地方去调用我们设定好的方法。

绕了一圈,似乎我们的想法越来越偏,离我们想要实现的越来越远,既然路偏了,那就直接回到起点吧,抛弃我们在 .NET Framework 项目中的经验,重新从 ASP.NET Core 项目的启动流程开始看起。

在 ASP.NET Core 应用的启动过程中存在着两个非常重要的对象,对应到我们采用的 ASP.NET Core 3.X 的项目中则是 Host 以及 HostBuilder。这里的 Host 就是承载我们 Web 应用运行的载体,而 HostBuilder 则是用来构建 Host 对象的。

PS:因为 ASP.NET Core 3.0 开始加入了 对于 gRPC 框架和 Windows Service 的支持,同时为了与其它非 Web 服务器方案进行集成,因此将原来的 WebHost 和 WebHostBuilder 替换成了新的通用主机(generic-host)配置的模式 。当然,在 3.X 版本你还是可以使用 WebHost 和 WebHostBuilder 的,不过当然是不推荐的。

因为对于 ASP.NET Core 应用程序来说,本质上其实只是一个控制台应用,所以现在我们来看看对于一个控制台应用中最重要的文件:Program.cs, Program 类中的代码如下所示。

public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
} public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}

代码很少,功能也很简单,简单来说,在 Main 方法中构建 HostBuilder 对象,然后去运行它,达到启动我们 Web 应用宿主的目的。

当然,在构建 HostBuilder 对象的过程中,会配置 Kestrel 服务器,会设置 ContentRoot,会加载配置文件等等一系列的动作,因为自己水平太次,尝试了一下,还是解释不好,如果你想要深入了解的话,建议配合博客园里面的这两篇文章一起食用(200行代码,7个对象——让你了解 ASP.NET Core 框架的本质ASP.NET Core 2.0 : 七.一张图看透启动背后的秘密)。虽然参考文章中都是基于 ASP.NET Core 2.X 版本进行解释说明的,但其实最终的差异不是很大。

不知你是否找到了这个类中对于我们最重要的一点,在 Main 方法中,我们是先构建、再去运行,因此,我们是不是可以在构建完成后,先等一等,把我们想要实现的功能先调用了,再去运行我们的程序。嗯,让我们改造下 Main 方法中的代码。

public class Program
{
public static void Main(string[] args)
{
    var host = CreateHostBuilder(args).Build();     // Get logger
    //
    var logger = host.Services.GetRequiredService<ILogger<Program>>();
    logger.LogInformation("haha, I ran before web host starting");     host.Run();
  } public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}

从上面的图中可以看到,在我们的 Web 应用的宿主程序还未启动之前,控制台就已经打印出了我们自己设定的信息,之后,才是启动我们的 Web 应用,这里是请求我们的 API 接口。同时可以发现,在模拟多次请求时,并不会再次触发我们预设的事件。

三、总结

这一篇文章中并没有包含代码,更多的是针对我之前在开发中遇到的一个问题,自己在解决过程中的一个案例说明,希望可以在你以后遇到这类问题时可以提供一些帮助。离 2020 年的农历新年也没有几天了,按目前的进度,估计就是年前的最后一篇博客了,我也要收拾收拾心情,准备过年了。最后,送大家一张表情包,献给得知你是最后一个放假的童鞋,哈哈哈,提前祝大家新年快乐丫。

四、参考

  1. [ASP.NET Core 3框架揭秘] 依赖注入[8]:服务实例的生命周期
  2. 200行代码,7个对象——让你了解 ASP.NET Core 框架的本质
  3. ASP.NET Core 2.0 : 七.一张图看透启动背后的秘密
  4. ASP.NET Core 3.0 的新增功能

在 ASP.NET Core 程序启动前运行你的代码的更多相关文章

  1. 如何在ASP.NET Core程序启动时运行异步任务(3)

    原文:Running async tasks on app startup in ASP.NET Core (Part 3) 作者:Andrew Lock 译者:Lamond Lu 之前我写了两篇有关 ...

  2. 如何在ASP.NET Core程序启动时运行异步任务(2)

    原文:Running async tasks on app startup in ASP.NET Core (Part 2) 作者:Andrew Lock 译者:Lamond Lu 在我的上一篇博客中 ...

  3. 如何在ASP.NET Core程序启动时运行异步任务(1)

    原文:Running async tasks on app startup in ASP.NET Core (Part 1) 作者:Andrew Lock 译者:Lamond Lu 背景 当我们做项目 ...

  4. ASP.NET Core 的启动和运行机制

    目录 ASP .NET Core 的运行机制 ASP .NET Core 的启动 ASP .NET Core 的管道和中间件 参考 ASP .NET Core 的运行机制 Web Server: AS ...

  5. [转帖]以Windows服务方式运行ASP.NET Core程序

    以Windows服务方式运行ASP.NET Core程序 原作者blog: https://www.cnblogs.com/guogangj/p/9198031.htmlaspnet的blog 需要持 ...

  6. 从头认识一下docker-附带asp.net core程序的docker化部署

    从头认识一下docker-附带asp.net core程序的docker化部署 简介 在计算机技术日新月异的今天, Docker 在国内发展的如火如荼,特别是在一线互联网公司, Docker 的使用是 ...

  7. 如何优雅的利用Windows服务来部署ASP.NET Core程序

    上一篇文章中我给大家讲述了五种部署ASP.NET Core网站的方法,其中有一种方式是通过Windows服务来进行部署,这样既可以做到开启自启动,又不会因为iis的反向代理而损失部分性能.但是美中不足 ...

  8. Asp.Net Core 程序部署到Linux(centos)生产环境(二):docker部署

    运行环境 照例,先亮环境:软件的话我这里假设你已经批准好了.net core 运行环境,未配置可以看我的这篇[linux(centos)搭建.net core 运行环境] 腾讯云 centos:7.2 ...

  9. Asp.Net Core 程序部署到Linux(centos)生产环境(一):普通部署

    运行环境 照例,先亮底 centos:7.2 cpu:1核 2G内存 1M带宽 辅助工具:xshell xftp 搭建.net core运行环境 .net core 的运行环境我单独写了一篇,请看我的 ...

随机推荐

  1. tf.train.string_input_producer()

    处理从文件中读数据 官方说明 简单使用 示例中读取的是csv文件,如果要读tfrecord的文件,需要换成 tf.TFRecordReader import tensorflow as tf file ...

  2. Codevs 四子连棋 (迭代加深搜索)

    题目描述 Description 在一个4*4的棋盘上摆放了14颗棋子,其中有7颗白色棋子,7颗黑色棋子,有两个空白地带,任何一颗黑白棋子都可以向上下左右四个方向移动到相邻的空格,这叫行棋一步,黑白双 ...

  3. 深度解读 - TDD(测试驱动开发)

    转自:http://www.jianshu.com/p/62f16cd4fef3 本文结构: 什么是 TDD 为什么要 TDD 怎么 TDD FAQ 学习路径 延伸阅读 什么是 TDD TDD 有广义 ...

  4. CF 453C. Little Pony and Summer Sun Celebration

    CF 453C. Little Pony and Summer Sun Celebration 构造题. 题目大意,给定一个无向图,每个点必须被指定的奇数或者偶数次,求一条满足条件的路径(长度不超\( ...

  5. springBoot从入门到源码分析

    先分享一个springBoot搭建学习项目,和springboot多数据源项目的传送门:https://github.com/1057234721/springBoot 1. SpringBoot快速 ...

  6. 学习Java第二周

    这是学习java的第二周,又这样不知不觉的结束了 上周想要学习的这一周也都做到了,可是觉得进度有些慢了,学习了: 1. 接口和抽象类: 2. 集合与数组: 3. 方法的定义: 4. 递归算法: 5.对 ...

  7. Vue+Element实现网页版个人简历系统

    这篇文章介绍一个使用Vue+Element实现的个人简历系统,主要用到的技术有:vue.element.css3.css定位. 作者在window10进行开发,目前只在chrome上进行过测试,没有大 ...

  8. 20191121-4 Final发布用户使用报告

    此作业要求参见:https://edu.cnblogs.com/campus/nenu/2019fall/homework/10064 队名:组长 组长:杨天宇 组员:罗杨美慧,王歆瑶,魏鑫,梅小雨 ...

  9. $CF888G\ Xor-MST$ 最小生成树

    正解:最小生成树 解题报告: 传送门$QwQ$ 发现$Kruskal$和$Prime$都不太可做,于是考虑$B$算法. 先大概港下$B$算法的流程趴$QwQ$.大概就,每次对每个联通块找到最近的联通块 ...

  10. 洛谷$P2053\ [SCOI2007]$修车 网络流

    正解:网络流 解题报告: 传送门$QwQ$ 一个很妙的建图,,,说实话我麻油想到$QwQ$ 考虑对每个工人建$n$个点,表示这是他修的倒数第$i$辆车,就可以算出影响是$t\cdot i$,然后对每辆 ...