在我的上一篇文章《在ASP.NET Core中创建基于Quartz.NET托管服务轻松实现作业调度》,我展示了如何使用ASP.NET Core创建Quartz.NET托管服务并使用它来按计划运行后台任务。不幸的是,由于Quartz.NET API的工作方式,在Quartz作业中使用Scoped依赖项注入服务有些麻烦。说明下这篇文章部分采用机翻。

作者:依乐祝

译文地址:https://www.cnblogs.com/yilezhu/p/12757411.html

原文地址:https://andrewlock.net/using-scoped-services-inside-a-quartz-net-hosted-service-with-asp-net-core/

在这篇文章中,我将展示一种简化工作中使用Scoped服务的方法。您可以使用相同的方法来管理EF Core的工作单元模式和其他面向切面的模型。

这篇文章是上篇文章引申出来的,因此,如果您还没有阅读的话,建议您先阅读上篇文章。

回顾-自定义JobFactory和单例的IJob

在上篇博客的最后,我们有一个实现了IJob接口并向控制台简单输出信息的HelloWorldJob

public class HelloWorldJob : IJob
{private readonly ILogger<HelloWorldJob> _logger;public HelloWorldJob(ILogger<HelloWorldJob> logger){_logger = logger;}public Task Execute(IJobExecutionContext context){_logger.LogInformation("Hello world!");return Task.CompletedTask;}
}

我们还有一个IJobFactory的实现,以便我们在需要时从DI容器中检索作业的实例:

public class SingletonJobFactory : IJobFactory
{private readonly IServiceProvider _serviceProvider;public SingletonJobFactory(IServiceProvider serviceProvider){_serviceProvider = serviceProvider;}public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler){return _serviceProvider.GetRequiredService(bundle.JobDetail.JobType) as IJob;}public void ReturnJob(IJob job) { }
}

这些服务都在Startup.ConfigureServices()中以单例形式注册:

services.AddSingleton<IJobFactory, SingletonJobFactory>();
services.AddSingleton<HelloWorldJob>();

对于这个非常基本的示例来说,这很好,但是如果您需要在IJob内部使用一些范围服务呢?例如,也许您需要使用EF Core DbContext遍历所有客户,并向他们发送电子邮件,并更新客户记录。我们假设这个任务为EmailReminderJob

权宜之计

我在上一篇文章中展示的解决方案是将IServiceProvider注入到您的IJob的文档中,手动创建一个范围,并从中检索必要的服务。例如:

public class EmailReminderJob : IJob
{private readonly IServiceProvider _provider;public EmailReminderJob( IServiceProvider provider){_provider = provider;}public Task Execute(IJobExecutionContext context){using(var scope = _provider.CreateScope()){var dbContext = scope.ServiceProvider.GetService<AppDbContext>();var emailSender = scope.ServiceProvider.GetService<IEmailSender>();// fetch customers, send email, update DB}return Task.CompletedTask;}
}

在许多情况下,这种方法绝对可以。如果不是将实现直接放在工作内部(如我上面所做的那样),而是使用中介者模式来处理诸如工作单元或消息分发之类的跨领域问题,则尤其如此。

如果不是这种情况,您可能会受益于创建一个可以为您管理这些工作的帮助类。

QuartzJobRunner

要解决这些问题,您可以创建一个IJob的“中间” 实现,这里我们命名为QuartzJobRunner,该实现位于IJobFactory和要运行的IJob之间。我将很快介绍作业实现,但是首先让我们更新现有的IJobFactory实现以无论请求哪个作业,始终返回QuartzJobRunner的实例,:

using Microsoft.Extensions.DependencyInjection;
using Quartz;
using Quartz.Spi;
using System;
public class JobFactory : IJobFactory
{private readonly IServiceProvider _serviceProvider;public JobFactory(IServiceProvider serviceProvider){_serviceProvider = serviceProvider;}public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler){return _serviceProvider.GetRequiredService<QuartzJobRunner>();}public void ReturnJob(IJob job) { }
}

如您所见,该NewJob()方法始终返回QuartzJobRunner的实例。我们将在Startup.ConfigureServices()中将QuartzJobRunner注册为单例模式,因此我们不必担心它没有被明确释放。

services.AddSingleton<QuartzJobRunner>();

我们将在QuartzJobRunner中创建实际所需的IJob实例。QuartzJobRunner中的job会创建范围,实例化IJob的请求并执行它:

using Microsoft.Extensions.DependencyInjection;
using Quartz;
using System;
using System.Threading.Tasks;
public class QuartzJobRunner : IJob
{private readonly IServiceProvider _serviceProvider;public QuartzJobRunner(IServiceProvider serviceProvider){_serviceProvider = serviceProvider;}public async Task Execute(IJobExecutionContext context){using (var scope = _serviceProvider.CreateScope()){var jobType = context.JobDetail.JobType;var job = scope.ServiceProvider.GetRequiredService(jobType) as IJob;await job.Execute(context);}}
}

在这一点上,您可能想知道,通过添加这个额外的间接层,我们获得了什么好处?主要有以下两个主要优点:

  • 我们可以将EmailReminderJob注册为范围服务,并直接将任何依赖项注入其构造函数中

  • 我们可以将其他横切关注点转移到QuartzJobRunner类中。

作业可以直接使用作用域服务

由于作业实例是从IServiceProvder作用域中解析来的,因此您可以在作业实现的构造函数中安全地使用作用域服务。这使的EmailReminderJob的实现更加清晰,并遵循构造函数注入的典型模式。如果您不熟悉DI范围界定问题,则可能很难理解它们,因此任何对您不利的事情在我看来都是一个好主意:

[DisallowConcurrentExecution]
public class EmailReminderJob : IJob
{private readonly AppDbContext _dbContext;private readonly IEmailSender _emailSender;public EmailReminderJob(AppDbContext dbContext, IEmailSender emailSender){_dbContext = dbContext;_emailSender = emailSender;}public Task Execute(IJobExecutionContext context){// fetch customers, send email, update DBreturn Task.CompletedTask;}
}

这些IJob的实现可以使用以下任何生存期(作用域或瞬态)来在Startup.ConfigureServices()中注册(JobSchedule仍然可以是单例):

services.AddScoped<EmailReminderJob>();
services.AddSingleton(new JobSchedule(jobType: typeof(EmailReminderJob),cronExpression: "0 0 12 * * ?")); // every day at noon

QuartzJobRunner可以处理横切关注点

QuartzJobRunner处理正在执行的IJob的整个生命周期:它从容器中获取,执行并释放它(在释放范围时)。因此,它很适合处理其他跨领域问题。

例如,假设您有一个需要更新数据库并将事件发送到消息总线的服务。您可以在每个单独的IJob实现中处理所有这些问题,也可以将跨领域的“提交更改”和“调度消息”操作移到QuartzJobRunner中。

这个例子显然是非常基础的。如果这里的代码适合您,我建议您观看吉米·博加德(Jimmy Bogard)的“六小段失败线”演讲,其中描述了一些问题!

public class QuartzJobRunner : IJob
{private readonly IServiceProvider _serviceProvider;public QuartzJobRunner(IServiceProvider serviceProvider){_serviceProvider = serviceProvider;}public async Task Execute(IJobExecutionContext context){using (var scope = _serviceProvider.CreateScope()){var jobType = context.JobDetail.JobType;var job = scope.ServiceProvider.GetRequiredService(jobType) as IJob;var dbContext = _serviceProvider.GetRequiredService<AppDbContext>();var messageBus = _serviceProvider.GetRequiredService<IBus>();await job.Execute(context);// job completed, save dbContext changesawait dbContext.SaveChangesAsync();// db transaction succeeded, send messagesawait messageBus.DispatchAsync();}}
}

这里的QuartzJobRunner实现与上一个非常相似,但是在执行的我们请求的IJob之前,我们从DI容器中解析了DbContext和消息总线服务。当作业成功执行后(即未抛出异常),我们将所有未提交的更改保存在中DbContext,并在消息总线上调度事件。

将这些方法移到QuartzJobRunner中应该可以减少IJob实现中的重复代码,并且可以更容易地移到更正式的管道和其他模式(如果您希望以后这样做的话)。

可替代解决方案

我喜欢本文中显示的方法(使用中间QuartzJobRunner类),主要有两个原因:

  • 您的其他IJob实现不需要任何有关创建作用域的基础结构的知识,只需完成标准构造函数注入即可

  • IJobFactory中不需要做做任何特殊处理工作。该QuartzJobRunner通过创建和处理作用域隐式地处理这个问题。

但是,此处显示的方法并不是在工作中使用范围服务的唯一方法。马修·阿伯特(Matthew Abbot) 在这个文章中演示了一种方法,该方法旨在以正确处理运行后的作业的方式实现IJobFactory。它有点笨拙,因为你必须匹配接口API,但可以说它更接近你应该实现它的方式!我个人认为我会坚持使用这种QuartzJobRunner方法,但是你可以选择最适合您的方法????

总结

在本文中,我展示了如何创建中间层IJob,该中间层QuartzJobRunner在调度程序需要执行作业时创建。该运行程序负责创建一个DI范围,实例化请求的作业并执行它,因此最终IJob实现可以在其构造函数中使用作用域中的服务。您也可以使用此方法在QuartzJobRunner中配置基本管道,尽管对此有更好的解决方案,例如装饰器或MediatR库中的行为。


http://www.taodudu.cc/news/show-911363.html

相关文章:

  • 在 Visual Studio 2019 中为 .NET Core WinForm App 启用窗体设计器
  • 视频号,张小龙的星辰大海
  • [半翻] 设计面向DDD的微服务
  • 借助Redis完成延时任务
  • 多亏我缓存技术过硬!疫情防控项目上线,我只用了5天!
  • ABP框架使用拦截器动态配置租户过滤器
  • 在.NET Core中用最原生的方式读取Nacos的配置
  • .NET Core下的开源分布式任务调度系统ScheduleMaster-v2.0低调发布
  • 后端开发都应该了解点接口的压力测试(Apache Bench版)
  • 深入理解kestrel的应用
  • Istio 中的 Sidecar 注入及透明流量劫持过程详解
  • 用long类型让我出了次生产事故,写代码还是要小心点
  • 链路追踪在ERP系统中的应用实践
  • 谈谈登录密码传输这件小事
  • Istio1.5 Envoy 数据面 WASM 实践
  • 使用c# .net core开发国标gb28181 sip +流媒体服务完成视频监控实例教程 亲身完美体验过程...
  • 硬核技能k8s初体验
  • Asp.Net.Core WebApi 版本控制
  • 用asp.net core结合fastdfs打造分布式文件存储系统
  • 在远程 CSM 课程中体验线上工作坊
  • C#验证IP是否为局域网地址的三种方法
  • 在C#中使用RESTful API的几种好方法
  • 自制 .NET Core 路由调试中间件
  • 有哪些你踏入社会才明白的道理
  • 从GC的SuppressFinalize方法带你深刻认识Finalize底层运行机制
  • C# 9 新特性:代码生成器、编译时反射
  • .NET开源工具类库:Masuit.Tools
  • DevOps vs. Agile:它们有什么共同点?
  • 从堆里找回“丢失”的代码
  • 简述使用REST API 的最佳实践

【半译】在ASP.NET Core中创建内部使用作用域服务的Quartz.NET宿主服务相关推荐

  1. 在ASP.NET Core中创建自定义端点可视化图

    在上篇文章中,我为构建自定义端点可视化图奠定了基础,正如我在第一篇文章中展示的那样.该图显示了端点路由的不同部分:文字值,参数,动词约束和产生结果的端点: 在本文中,我将展示如何通过创建一个自定义的D ...

  2. 如何在ASP.NET Core中创建自定义AuthorizeAttribute?

    本文翻译自:How do you create a custom AuthorizeAttribute in ASP.NET Core? I'm trying to make a custom aut ...

  3. 在ASP.NET Core中创建基于Quartz.NET托管服务轻松实现作业调度

    在这篇文章中,我将介绍如何使用ASP.NET Core托管服务运行Quartz.NET作业.这样的好处是我们可以在应用程序启动和停止时很方便的来控制我们的Job的运行状态.接下来我将演示如何创建一个简 ...

  4. 重学ASP.NET Core 中的标记帮助程序

    标记帮助程序是什么 标记帮助程序使服务器端代码可以在 Razor 文件中参与创建和呈现 HTML 元素. 例如,内置的 ImageTagHelper 可以将版本号追加到图片名称.  每当图片发生变化时 ...

  5. ASP.NET Core中为指定类添加WebApi服务功能

    POCO Controller是 ASP.NET Core 中的一个特性,虽然在2015年刚发布的时候就有这个特性了,可是大多数开发者都只是按原有的方式去写,而没有用到这个特性.其实,如果利用这个特性 ...

  6. asp.net nancy_如何在ASP.Net Core中使用Nancy

    asp.net nancy Nancy是用于构建基于HTTP的服务的轻量级框架. Nancy更喜欢约定而不是配置,并提供对GET,HEAD,POST,PUT,DELETE和PATCH操作的支持. Na ...

  7. 如何在ASP.Net Core中使用Nancy

    Nancy是用于构建基于HTTP的服务的轻量级框架. Nancy更喜欢约定而不是配置,并提供对GET,HEAD,POST,PUT,DELETE和PATCH操作的支持. Nancy在MIT许可下也是开源 ...

  8. ASP.NET Core 中简单Session登录校验

    ASP.NET Core 中简单Session登录校验:从Session的配置添加.到请求过滤.再到页面操作.推荐相关阅读:ASP.NET 会话状态概述  ASP.NET Cookie 概述  ASP ...

  9. [译]ASP.NET Core中使用MediatR实现命令和中介者模式

    在本文中,我将解释命令模式,以及如何利用基于命令模式的第三方库来实现它们,以及如何在ASP.NET Core中使用它来解决我们的问题并使代码简洁.因此,我们将通过下面的主题来进行相关的讲解. 什么是命 ...

最新文章

  1. 做俯卧撑的10个技巧
  2. [云炬创业基础笔记]第十一章创业计划书测试9
  3. android 360状态栏显示,如何从Android应用程序显示360全景(How to display a 360 panorama from android application)...
  4. 第一篇博客记录下自己刚学习的问题
  5. 【2016.11.17】HTML学习笔记第二天
  6. CNET:乔布斯留给我们的12条宝贵经验
  7. python中的time的时间戳_python中的时间time、datetime、str和时间戳
  8. android 改变音乐格式,音乐速度变更器app-音乐速度变更器下载v9.4.4-hu安卓版-西西软件下载...
  9. 消息队列以及非常牛的kafka
  10. python3.8.2 安装 win7下安装
  11. 天气数据垂手可得-IBM SPSS Modeler 18.0扩展应用实操练习
  12. 智能电网与配网自动化工程建设
  13. Linux 安装WinUSB
  14. 软件测试 查看网页打开时间,网页响应时间101(三)
  15. sdk编译文件报错conflicting types for ‘kill‘
  16. 小米手机通过adb安装应用异常处理
  17. Local、LocalLow 和 Roaming
  18. 一款极简优雅抖音在线qu水印HTML源码
  19. FRP内网穿透服务免费使用的详细教程
  20. 好用的社群工具有什么功能

热门文章

  1. Java 集合练习——3
  2. 正在创建系统还原点_如何使Windows在启动时自动创建系统还原点
  3. 转整型_SPI转can芯片CSM300详解、Linux驱动移植调试笔记
  4. Vuejs——组件——slot内容分发
  5. windows远程连接linux-安装xfce界面,ubuntn添加新用户
  6. Linux入门之进程管理(4)之进程与文件
  7. 12篇学通C#网络编程——第一篇 基础之进程线程
  8. SystemCenter2012SP1实践(23)私有云的权限设计
  9. 轻量级HTTP服务器Nginx(安装篇)
  10. K8S原来如此简单(一)K8S核心组件与基本原理