原文:Running async tasks on app startup in ASP.NET Core (Part 2)
作者:Andrew Lock
译者:Lamond Lu


在我的上一篇博客中,我介绍了如何在ASP.NET Core应用程序启动时运行一些一次性异步任务。本篇博客将继续讨论上一篇的内容,如果你还没有读过,我建议你先读一下前一篇。

在本篇博客中,我将展示上一篇博文中提出的“在Program.cs中手动运行异步任务”的实现方法。该实现会使用一些简单的接口和类来封装应用程序启动时的运行任务逻辑。我还会展示一个替代方法,这个替代方法是在Kestral服务器启动时,使用IServer接口。

在应用程序启动时运行异步任务

这里我们先回顾一下上一遍博客内容,在上一篇中,我们试图寻找一种方案,允许我们在ASP.NET Core应用程序启动时执行一些异步任务。这些任务应该是在ASP.NET Core应用程序启动之前执行,但是由于这些任务可能需要读取配置或者使用服务,所以它们只能在ASP.NET Core的依赖注入容器配置完成后执行。数据库迁移,填充缓存都可以这种异步任务的使用场景。

我们在一篇文章的末尾提出了一个相对完善的解决方案,这个方案是在Program.cs中“手动”运行任务。运行任务的时机是在IWebHostBuilder.Build()IWebHost.RunAsync()之间。

这种实现方式是可行的,但是有点乱。这里我们将许多不应该属于Program.cs职责的代码放在了Program.cs中,让它看起来有点臃肿了,所以这里我们需要将数据库迁移相关的代码移到另外一个类中。

这里更麻烦的问题是,我们必须要手动调用任务。如果你在多个应用程序中使用相同的模式,那么最好能改成自动调用任务。

在依赖注入容器中注册启动任务

这里我将使用基于IStartupFilterIHostService使用的模式。它们允许你在依赖注入容器中注册它们的实现类,并在应用程序启动前获取到这些接口的所有实现类,并依次执行它们。

所以,这里首先我们创建一个简单的接口来启动任务。

并且创建一个在依赖注入容器中注册任务的便捷方法。

最后,我们添加一个扩展方法,在应用程序启动时找到所有已注册的IStartupTasks,按顺序运行它们,然后启动IWebHost:

以上就是所有的代码。

下面为了看一下它的实际效果,我将继续使用上一篇中EF Core数据库迁移的例子

例子:异步迁移数据库

实现IStartupTask和实现IStartupFilter非常的相似。你可以从依赖注入容器中注入服务。为了使用依赖注入容器中的服务,这里我们需要手动注入一个IServiceProvider对象,并手动创建一个Scoped服务。

EF Core的数据库迁移启动任务类似以下代码:

现在,我们可以在ConfigureServices方法中使用依赖注入容器添加启动任务了。

最后我们更新一下Program.cs, 使用RunWithTasksAsync()方法替换Run()方法。

以上代码利用了C# 7.1中引入的异步Task Main的特性。从功能上来说,它与我上一篇博客中的手动代码等同,但是它有一些优点。

  • 它的任务实现代码没有放在Program.cs中。

  • 由于上一条的优点,开发人员可以很容易的添加额外的任务。

  • 如果不运行任何任务,它的功能和RunAsync是一样的

对于以上方案,有一个问题需要注意。这里我们定义的任务会在IConfiguration和依赖注入容器配置完成之后运行,这也就意味着,当任务执行时,所有的IStartupFilter都没有运行,中间件管道也没有配置。

就我个人而言,我不认为这是一个问题,因为我暂时想不出任何可能。到目前为止,我所编写的任务都不依赖于IStartupFilter和中间件管道。但这也并不意味着没有这种可能。

不幸的是,使用当前的WebHost代码并没有简单的方法(尽管 在.NET Core 3.0中当ASP.NET Core作为IHostedService运行时,这可能会发生变化)。 问题是应用程序是引导(通过配置中间件管道并运行IStartupFilters)和启动在同一个函数中。 当你在Program.cs中调用WebHost.Run()时,在内部程序会调用WebHost.StartAsync,如下所示,为简洁起见,其中只包含了日志记录和一些其他次要代码:

这里问题是我们想要在BuildApplication()Server.StartAsync之间插入代码,但是现在没有这样做的机制。

我不确定我所给出的解决方案是否优雅,但它可以工作,并为消费者提供更好的体验,因为他们不需要修改Program.cs

使用IServer的替代方案

为了实现在BuildApplication()Server.StartAsync()之间运行异步代码,我能想到的唯一办法是我们自己的实现一个IServer实现(Kestrel)! 对你来说,听到这个可能感觉非常可怕 - 但是我们真的不打算更换服务器,我们只是去装饰它。

TaskExecutingServer在其构造函数中获取了一个IServer实例 - 这是ASP.NET Core注册的原始Kestral服务器。我们将大部分IServer的接口实现直接委托给Kestrel,我们只是拦截对StartAsync的调用并首先运行注入的任务。

这个实现最困难部分是使装饰器正常工作。正如我在上一篇文章中所讨论的那样,使用带有默认ASP.NET Core容器的装饰可能会非常棘手。我通常使用Scrutor来创建装饰器,但是如果你不想依赖另一个库,你总是可以手动进行装饰, 但一定要看看Scrutor是如何做到这一点的!

下面我们添加一个用于添加IStartupTask的扩展方法, 这个扩展方法做了两件事,一是将IStartupTask注册到依赖注入容器中,二是装饰了之前注册的IServer实例(这里为了简洁,我省略了Decorate方法的实现)。如果它发现IServer已经被装饰,它会跳过第二步,这样你就可以安全的多次调用AddStartupTask<T>方法。

使用这两段代码,我们不再需要再对Program.cs文件进行任何更改,并且我们是在完全构建应用程序后执行我们的任务,这其中也包括IStartupFilters和中间件管道。

启动过程的序列图现在看起来有点像这样:


以上就是这种实现方式全部的内容。它的代码非常少, 以至于我自己都在考虑是否要自己编写一个库。不过最后我还是在GitHub和Nuget上创建了一个库NetEscapades.AspNetCore.StartupTasks

这里我只编写了使用后一种IServer实现的库,因为它更容易使用,而且Thomas Levesque已经编写针对第一种方法可用的NuGet包。

在GitHub的实现中,我手动构造了装饰器,以避免强制依赖Scrutor。 但最好的方法可能就是将代码复制并粘贴到您自己的项目中。

总结

在这篇博文中,我展示了两种在ASP.NET Core应用程序启动时异步运行任务的方法。 第一种方法需要稍微修改Program.cs,但是“更安全”,因为它不需要修改像IServer这样的内部实现细节。 第二种方法是装饰IServer,提供更好的用户体验,但感觉更加笨拙。

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

  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程序启动时运行异步任务(1)

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

  3. ASP.NET Core 3.x启动时运行异步任务(一)

    这是一个大的题目,需要用几篇文章来说清楚.这是第一篇.   一.前言 在我们的项目中,有时候我们需要在应用程序启动前执行一些一次性的逻辑.比方说:验证配置的正确性.填充缓存.或者运行数据库清理/迁移等 ...

  4. ASP.NET Core 3.x启动时运行异步任务(二)

    这一篇是接着前一篇在写的.如果没有看过前一篇文章,建议先去看一下前一篇,这儿是传送门   一.前言 前一篇文章,我们从应用启动时异步运行任务开始,说到了必要性,也说到了几种解决方法,及各自的优缺点.最 ...

  5. java运行按钮在哪里_[tkinter按钮命令已在程序启动时运行

    我正在尝试进行tkinter练习,但出现此错误.基本上,当代码首次运行时,该命令立即运行,但是,仅在按下按钮时运行. from tkinter import * try: def the_functi ...

  6. 如何在ASP.NET Core中编写自定义日志记录提供程序

    目录 介绍 如何实现所需的接口 基础类和附件 FileLoggerProvider具体类及其附件 1. ConfigureLogging() 2. appsettings.json文件 介绍 源代码可 ...

  7. windows server 2008 r2 托管 asp.net core 程序无法启动

    windows server 2008 r2 托管 asp.net core 程序无法启动时,需安装以下更新: Update for Windows Server 2008 R2 x64 Editio ...

  8. 如何在ASP.NET Core中使用JSON Patch

    原文: JSON Patch With ASP.NET Core 作者:.NET Core Tutorials 译文:如何在ASP.NET Core中使用JSON Patch 地址:https://w ...

  9. asp英语是什么意思_如何在ASP.NET Core中使用JSON Patch

    原文: JSON Patch With ASP.NET Core 作者:.NET Core Tutorials 译文:如何在ASP.NET Core中使用JSON Patch 地址:https://w ...

最新文章

  1. 《软件测试的艺术》读书笔记 - 1
  2. c语言编译器内部错误,C++致命错误C1001:编译器中发生内部错误
  3. Android Scrollview嵌套RecyclerView导致滑动卡顿问题解决(屡试不爽)
  4. Question of the Day: Microsoft | Database, Multiple Questions in One
  5. html 设置两个标签的相对距离_如何准确计算一div相对另一div的相对距离?
  6. markdown与latex:如何写出具有数学符号意味的ln3\ln
  7. Centos之常用命令
  8. lua如何打印行号_【Lua与C#交互⑤】Lua中的注册表和引用系统
  9. 脚本之家python专题_一个简单的python读写文件脚本
  10. Android原生PDF功能实现,掌握了这些Android高级工程师必备知识,
  11. pip 批量完全卸载包
  12. pythonds模块_python之模块
  13. jQuery 中console.log什么意思
  14. 制定小目标的软件APP哪款好
  15. (转)罗振宇跨年演讲:哪来直接登顶的人生,只有不断迭代的历程
  16. 关于SEO优化的建议
  17. 顺序表实现简单的【扑克牌】
  18. 小猿圈python之python期末考试测试题(二)_小猿圈Python开发面试题(二)
  19. linux相关命令------文件内容显示以及文件其他命令
  20. mysql只能导出65535条_导出Excel超过65535条限制解决方案

热门文章

  1. 【leetcode】521. Longest Uncommon Subsequence I
  2. java web移植 遇到Project facet Java version 1.7 is not supported
  3. MySQL案例分析--QueryCache
  4. android键盘弹出,聊天背景不变形
  5. 找回创新能力 才是苹果的救命稻草
  6. 微软翻译api的使用介绍和注意事项
  7. erlang 编译之 to_core
  8. SQL--合并多条记录为一条记录
  9. 分库分表之历史表如何选择最佳分片路由规则
  10. 手把手教你学Dapr - 4. 服务调用