Asp.Net Core2.0 基于QuartzNet任务管理系统

Quartz.NET官网地址:https://www.quartz-scheduler.net/

Quartz.NET文档地址:https://www.quartz-scheduler.net/documentation/index.html

Quartz.NET是一个开源的作业调度框架,是OpenSymphony 的 Quartz API的.NET移植,它用C#写成,可用于winformasp.net应用中。它提供了巨大的灵活性而不牺牲简单性。你能够用它来为执行一个作业而创建简单的或复杂的调度。它有很多特征,如:数据库支持,集群,插件,支持cron-like表达式等等。

现在Quartz.NET3.0已支持Asp.Net Core,3.0新功能如下:

新功能

  • 具有异步/等待支持的基于任务的作业,内部以异步/等待方式工作

  • 支持.NET Core / netstandard 2.0和.NET Framework 4.5.2及更高版本

  • 通过提供程序名称SQLite-Microsoft支持Microsoft.Data.Sqlite,旧的提供程序SQLite也仍然有效

  • 增加了SQL Server内存优化表和Quartz.Impl.AdoJobStore.UpdateLockRowSemaphoreMOT的初步支持

  • Common.Logging从相关性中删除

  • ILMerge进程中删除的C5集合不再需要

  • 在插件启动时添加对作业调度XML文件的急切验证的支持

  • TimeZoneUtil中添加对额外的自定义时区解析器功能的支持

变化

  • 作业和插件现在位于独立的程序集NuGetQuartz.JobsQuartz.Plugins

  • ADO.NET提供者名称已被简化,提供者名称没有版本,例如SqlServer-20 => SqlServer

  • API方法已被重新使用,主要使用IReadOnlyCollection,这隐藏了两个HashSets和List小号

  • LibLog一直隐藏于内部(ILog等),就像它原本打算的那样

  • SimpleThreadPool消失了,旧的拥有的线程消失了

  • 调度程序方法已更改为基于任务,请记住等待它们

  • IJob接口现在返回一个任务

  • 一些IList属性已更改为IReadOnlyList以正确反映意图

  • SQL Server CE支持已被删除

  • DailyCalendar现在将日期时间用于排除的日期,并具有ISet接口来访问它们

  • IObjectSerializer有新的方法,void Initialize(),必须实现

  • IInterruptableJob取消了上下文的CancellationToken

Quartz API的关键接口和类是

  • IScheduler - 与调度程序交互的主要API。

  • IJob - 您希望由调度程序执行的组件实现的接口。

  • IJobDetail - 用于定义作业的实例。

  • ITrigger - 定义执行给定Job的时间表的组件。

  • JobBuilder - 用于定义/构建定义作业实例的JobDetail实例。

  • TriggerBuilder - 用于定义/构建触发器实例

一、Quartz.NET基本使用

1、新建Asp.Net Core 项目,使用NuGet添加Quartz,或使用程序包管理器引用,命令如下:
Install-Package Quartz

 如果你想添加JSON序列化,只需要以同样的方式添加Quartz.Serialization.Json包。

2、简单实例,代码如下:
using Five.QuartzNetJob.ExecuteJobTask.Service;using Quartz;using Quartz.Impl;using System;using System.Collections.Generic;using System.Collections.Specialized;using System.Text;using System.Threading.Tasks;namespace Five.QuartzNetJob.Web.Controllers
{    public class TestTask{        public async Task StartTestAsync() {            try{                // 从工厂中获取调度程序实例NameValueCollection props = new NameValueCollection{{ "quartz.serializer.type", "binary" }};StdSchedulerFactory factory = new StdSchedulerFactory(props);IScheduler scheduler = await factory.GetScheduler();               // 开启调度器await scheduler.Start();                // 定义这个工作,并将其绑定到我们的IJob实现类IJobDetail job = JobBuilder.Create<HelloJob>().WithIdentity("job1", "group1").Build();                // 触发作业立即运行,然后每10秒重复一次,无限循环ITrigger trigger = TriggerBuilder.Create().WithIdentity("trigger1", "group1").StartNow().WithSimpleSchedule(x => x.WithIntervalInSeconds(10).RepeatForever()).Build();                // 告诉Quartz使用我们的触发器来安排作业await scheduler.ScheduleJob(job, trigger);                // 等待60秒await Task.Delay(TimeSpan.FromSeconds(60));                // 关闭调度程序await scheduler.Shutdown();}            catch (SchedulerException se){                await Console.Error.WriteLineAsync(se.ToString());}}}
}

 HelloJob内容如下:

using Quartz;using System;using System.Collections.Generic;using System.Text;using System.Threading.Tasks;namespace Five.QuartzNetJob.ExecuteJobTask.Service
{    public class HelloJob : IJob{        public  Task Execute(IJobExecutionContext context){Console.Out.WriteLineAsync("Greetings from HelloJob!");            return Task.CompletedTask;}}
}

执行效果如下:

Quartz的版本3.0.3中删除了IJob实现的异地调用,也就是不支持asyncawait异步调用,3.0.2版本支持异步调用。

二、触发器类型

 1、SimpleTrigger触发器(简单触发器)

SimpleTrigger的属性包括:开始时间和结束时间,重复计数和重复间隔。重复计数可以是零,一个正整数或常数值SimpleTrigger.RepeatIndefinitely。重复时间间隔属性必须是TimeSpan.Zero或正的TimeSpan值。请注意,重复间隔为0会导致触发器的“重复计数”触发同时发生。

SimpleTrigger实例使用TriggerBuilder(用于触发器的主属性)和WithSimpleSchedule扩展方法(用于SimpleTrigger特定的属性)构建。

在特定的时间内建立触发器,无需重复,代码如下:

// 触发器构建器默认创建一个简单的触发器,实际上返回一个ITriggerISimpleTrigger trigger = (ISimpleTrigger)TriggerBuilder.Create().WithIdentity("trigger1", "group1").StartAt(DateTime.Now) //指定开始时间为当前系统时间.ForJob("job1", "group1") //通过JobKey识别作业.Build();

在特定的时间建立触发器,然后每十秒钟重复十次:

// 触发器构建器默认创建一个简单的触发器,实际上返回一个ITriggerITrigger trigger = trigger = TriggerBuilder.Create().WithIdentity("trigger2", "group2").StartAt(DateTime.Now) // 指定开始时间.WithSimpleSchedule(x => x.WithIntervalInSeconds(10).WithRepeatCount(10)) // 请注意,重复10次将总共重复11次.ForJob("job2", "group2") //通过JobKey识别作业                   .Build();

构建一个触发器,将在未来五分钟内触发一次:

// 触发器构建器默认创建一个简单的触发器,实际上返回一个ITriggerITrigger trigger = trigger = (ISimpleTrigger)TriggerBuilder.Create().WithIdentity("trigger3", "group3").StartAt(DateBuilder.FutureDate(5, IntervalUnit.Minute)) //使用DateBuilder将来创建一个时间日期.ForJob("job3", "group3") //通过JobKey识别作业.Build();

建立一个现在立即触发的触发器,然后每隔五分钟重复一次,直到22:00:

// 触发器构建器默认创建一个简单的触发器,实际上返回一个ITriggerITrigger trigger = trigger = TriggerBuilder.Create().WithIdentity("trigger4", "group4").WithSimpleSchedule(x => x.WithIntervalInMinutes(5)//每5秒执行一次        .RepeatForever()).EndAt(DateBuilder.DateOf(22, 0, 0))//晚上22点结束.Build();

建立一个触发器,在一个小时后触发,然后每2小时重复一次:

// 触发器构建器默认创建一个简单的触发器,实际上返回一个ITriggerITrigger  trigger = TriggerBuilder.Create().WithIdentity("trigger5") // 由于未指定组,因此“trigger5”将位于默认分组中.StartAt(DateBuilder.EvenHourDate(null)) // 获取下个一小时时间                 .WithSimpleSchedule(x => x.WithIntervalInHours(2)//执行间隔2小时        .RepeatForever()).Build(); 

因此简单的任务调度使用SimpleTrigger完全够用,如果SimpleTrigger还是不能满足您的需求请往下看。

2、CronTrigger触发器

如果你需要一个基于类似日历的概念而不是精确指定的SimpleTrigger时间间隔的工作调度计划,CronTriggers通常比SimpleTrigger更有用。

使用CronTrigger,您可以在每周一,周三的上午9点至上午10点之间指定开始时间表,例如“每星期五中午”或“每个工作日和上午9点30分”,或者“每5分钟”和星期五”。

即使如此,就像SimpleTrigger一样,CronTrigger有一个startTime,它指定了时间表的生效时间,还有一个(可选的)endTime,用于指定应该停止时间表的时间。

这里不在详细介绍Cron

Cron表达式在线生成器:http://cron.qqe2.com/

Cron表达式详细介绍:https://www.jianshu.com/p/e9ce1a7e1ed1

每天早上8点到下午5点建立一个触发器,每隔一分钟就会触发一次:

// 触发器构建器默认创建一个简单的触发器,实际上返回一个ITriggerITrigger trigger = TriggerBuilder.Create().WithIdentity("Job1", "group1").WithCronSchedule("0 0/2 8-17 * * ?")//使用Cron表达式.ForJob("Job1", "group1").Build();

建立一个触发器,每天在上午10:42开始执行:

// 触发器构建器默认创建一个简单的触发器,实际上返回一个ITriggerITrigger trigger = TriggerBuilder.Create().WithIdentity("Job2", "group2").WithSchedule(CronScheduleBuilder.DailyAtHourAndMinute(10, 42)) // 在这里使用CronScheduleBuilder的静态辅助方法.ForJob("Job2", "group2").Build();

构建一个触发器,将在星期三上午10:42在除系统默认值之外的TimeZone中触发:

// 触发器构建器默认创建一个简单的触发器,实际上返回一个ITriggerITrigger trigger = TriggerBuilder.Create().WithIdentity("Job3", "group3").WithCronSchedule("0 42 10 ? * WED", x => x.InTimeZone(TimeZoneInfo.FindSystemTimeZoneById("Central America Standard Time"))).ForJob("Job3", "group3").Build();

总结: Quartz.NET的3.0版本跟之前的版本api接口变化并不大。只是在3.0版本中添加了异步调用,并支持.net core。简单的任务调度使用官网中的实例即可满足需求。

三、后端项目管理

在网上找到很多关于asp.net 的任务管理,但没有找到.net core 相关的后端任务管理。

我就根据官网还有网上dalao开源分享的项目实现了个简单的后端任务。

这里也开源出来让园区dalao批评指导。欢迎各位dalao留言批评!

项目地址:https://github.com/YANGKANG01/QuartzNetJob

项目结构

项目使用asp.net core 2.0--->adminlte-2.4.2-->SqlSugar-4.6.4.3。

数据库表可使用SqlSugar来生成。

public IActionResult Index()
{    //生成表    //var db = DataHelper.GetInstance();    //db.CodeFirst.InitTables(typeof(OperateLog), typeof(OperateLog));return View();}

 

结构如下:

Five.QuartzNetJob.DataService.DataHelper----------------------------------是ORM层,使用的是开源框架SqlSugar(官网地址:http://www.codeisbug.com/Doc/8)

Five.QuartzNetJob.DataService.Models---------------------------------------是实体类

Five.QuartzNetJob.ExecuteJobTask.Service---------------------------------IJob实现层

QuartzNet.Entity---------------------------------------------------------------------调度中心相关实体类

QuartzNet2.Core--------------------------------------------------------------------非.Net Core版本的调度管理中心,使用的是.net framework 4.6

Five.QuartzNetJob.Utils.Tool-----------------------------------------------------通用工具类库

Five.QuartzNetJob.Web-----------------------------------------------------------后端

项目很简单,就只实现了增删改查。

统一管理任务调度,项目运行时开启任务调度并执行已开启的任务。

因为项目太过于简单,就不在详细介绍。

下面贴出任务调度中心代码,欢迎各位dalao发表意见:

using QuartzNet.Entity;using Quartz;using Quartz.Impl;using System;using System.Collections.Specialized;using System.Threading.Tasks;using Five.QuartzNetJob.Utils.Tool;using System.Reflection;using System.Collections.Generic;namespace QuartzNet3.Core
{    /// <summary>/// 任务调度中心    /// </summary>public class SchedulerCenter{        /// <summary>/// 任务调度对象        /// </summary>public static readonly SchedulerCenter Instance;        static SchedulerCenter(){Instance = new SchedulerCenter();}        private Task<IScheduler> _scheduler;        /// <summary>/// 返回任务计划(调度器)        /// </summary>/// <returns></returns>private Task<IScheduler> Scheduler{            get{                if (this._scheduler != null){                    return this._scheduler;}                // 从Factory中获取Scheduler实例NameValueCollection props = new NameValueCollection{{ "quartz.serializer.type", "binary" },                    //以下配置需要数据库表配合使用,表结构sql地址:https://github.com/quartznet/quartznet/tree/master/database/tables//{ "quartz.jobStore.type","Quartz.Impl.AdoJobStore.JobStoreTX, Quartz"},                    //{ "quartz.jobStore.driverDelegateType","Quartz.Impl.AdoJobStore.StdAdoDelegate, Quartz"},                    //{ "quartz.jobStore.tablePrefix","QRTZ_"},                    //{ "quartz.jobStore.dataSource","myDS"},                    //{ "quartz.dataSource.myDS.connectionString",AppSettingHelper.MysqlConnection},//连接字符串                    //{ "quartz.dataSource.myDS.provider","MySql"},                    //{ "quartz.jobStore.useProperties","true"}};StdSchedulerFactory factory = new StdSchedulerFactory(props);                return this._scheduler = factory.GetScheduler();}}      /// <summary>/// 运行指定的计划(映射处理IJob实现类)        /// </summary>/// <param name="jobGroup">任务分组</param>/// <param name="jobName">任务名称</param>/// <returns></returns>public async Task<BaseQuartzNetResult> RunScheduleJob<T>(string jobGroup, string jobName) where T : ScheduleManage{BaseQuartzNetResult result;            //开启调度器await this.Scheduler.Result.Start();            //创建指定泛型类型参数指定的类型实例T t = Activator.CreateInstance<T>();            //获取任务实例ScheduleEntity scheduleModel = t.GetScheduleModel(jobGroup, jobName);            //添加任务var addResult = AddScheduleJob(scheduleModel).Result;            if (addResult.Code == 1000){scheduleModel.Status = EnumType.JobStatus.已启用;t.UpdateScheduleStatus(scheduleModel);                //用给定的密钥恢复(取消暂停)IJobDetailawait this.Scheduler.Result.ResumeJob(new JobKey(jobName, jobGroup));result = new BaseQuartzNetResult{Code = 1000,Msg = "启动成功"};}            else{result = new BaseQuartzNetResult{Code = -1};}            return result;}        /// <summary>/// 添加一个工作调度(映射程序集指定IJob实现类)        /// </summary>/// <param name="m"></param>/// <returns></returns>private async Task<BaseQuartzNetResult> AddScheduleJob(ScheduleEntity m){            var result = new BaseQuartzNetResult();            try{                //检查任务是否已存在var jk = new JobKey(m.JobName, m.JobGroup);                if (await this.Scheduler.Result.CheckExists(jk)){                    //删除已经存在任务await this.Scheduler.Result.DeleteJob(jk);}                //反射获取任务执行类var jobType = FileHelper.GetAbsolutePath(m.AssemblyName, m.AssemblyName + "." + m.ClassName);                // 定义这个工作,并将其绑定到我们的IJob实现类IJobDetail job = new JobDetailImpl(m.JobName, m.JobGroup, jobType);                //IJobDetail job = JobBuilder.CreateForAsync<T>().WithIdentity(m.JobName, m.JobGroup).Build();                // 创建触发器                ITrigger trigger;                //校验是否正确的执行周期表达式if (!string.IsNullOrEmpty(m.Cron) && CronExpression.IsValidExpression(m.Cron)){trigger = CreateCronTrigger(m);}                else{trigger = CreateSimpleTrigger(m);}                // 告诉Quartz使用我们的触发器来安排作业await this.Scheduler.Result.ScheduleJob(job, trigger);result.Code = 1000;}            catch (Exception ex){                await Console.Out.WriteLineAsync(string.Format("添加任务出错{0}", ex.Message));result.Code = 1001;result.Msg = ex.Message;}            return result;}        /// <summary>/// 运行指定的计划(泛型指定IJob实现类)        /// </summary>/// <param name="jobGroup">任务分组</param>/// <param name="jobName">任务名称</param>/// <returns></returns>public async Task<BaseQuartzNetResult> RunScheduleJob<T, V>(string jobGroup, string jobName) where T : ScheduleManage, new() where V : IJob{BaseQuartzNetResult result;            //开启调度器await this.Scheduler.Result.Start();            //创建指定泛型类型参数指定的类型实例T t = Activator.CreateInstance<T>();            //获取任务实例ScheduleEntity scheduleModel = t.GetScheduleModel(jobGroup, jobName);            //添加任务var addResult = AddScheduleJob<V>(scheduleModel).Result;            if (addResult.Code == 1000){scheduleModel.Status = EnumType.JobStatus.已启用;t.UpdateScheduleStatus(scheduleModel);                //用给定的密钥恢复(取消暂停)IJobDetailawait this.Scheduler.Result.ResumeJob(new JobKey(jobName, jobGroup));result = new BaseQuartzNetResult{Code = 1000,Msg = "启动成功"};}            else{result = new BaseQuartzNetResult{Code = -1};}            return result;}        /// <summary>/// 添加任务调度(指定IJob实现类)        /// </summary>/// <typeparam name="T"></typeparam>/// <param name="m"></param>/// <returns></returns>private async Task<BaseQuartzNetResult> AddScheduleJob<T>(ScheduleEntity m) where T : IJob{            var result = new BaseQuartzNetResult();            try{                //检查任务是否已存在var jk = new JobKey(m.JobName, m.JobGroup);                if (await this.Scheduler.Result.CheckExists(jk)){                    //删除已经存在任务await this.Scheduler.Result.DeleteJob(jk);}                //反射获取任务执行类                // var jobType = FileHelper.GetAbsolutePath(m.AssemblyName, m.AssemblyName + "." + m.ClassName);                // 定义这个工作,并将其绑定到我们的IJob实现类                //IJobDetail job = new JobDetailImpl(m.JobName, m.JobGroup, jobType);IJobDetail job = JobBuilder.CreateForAsync<T>().WithIdentity(m.JobName, m.JobGroup).Build();                // 创建触发器                ITrigger trigger;                //校验是否正确的执行周期表达式if (!string.IsNullOrEmpty(m.Cron) && CronExpression.IsValidExpression(m.Cron)){trigger = CreateCronTrigger(m);}                else{trigger = CreateSimpleTrigger(m);}                // 告诉Quartz使用我们的触发器来安排作业await this.Scheduler.Result.ScheduleJob(job, trigger);result.Code = 1000;}            catch (Exception ex){                await Console.Out.WriteLineAsync(string.Format("添加任务出错", ex.Message));result.Code = 1001;result.Msg = ex.Message;}            return result;}        /// <summary>/// 暂停指定的计划        /// </summary>/// <param name="jobGroup">任务分组</param>/// <param name="jobName">任务名称</param>/// <param name="isDelete">停止并删除任务</param>/// <returns></returns>public BaseQuartzNetResult StopScheduleJob<T>(string jobGroup, string jobName, bool isDelete = false) where T : ScheduleManage, new(){BaseQuartzNetResult result;            try{                this.Scheduler.Result.PauseJob(new JobKey(jobName, jobGroup));                if (isDelete){Activator.CreateInstance<T>().RemoveScheduleModel(jobGroup, jobName);}result = new BaseQuartzNetResult{Code = 1000,Msg = "停止任务计划成功!"};}            catch (Exception ex){result = new BaseQuartzNetResult{Code = -1,Msg = "停止任务计划失败"};}            return result;}        /// <summary>/// 恢复运行暂停的任务        /// </summary>/// <param name="jobName">任务名称</param>/// <param name="jobGroup">任务分组</param>public async void ResumeJob(string jobName, string jobGroup){            try{                //检查任务是否存在var jk = new JobKey(jobName, jobGroup);                if (await this.Scheduler.Result.CheckExists(jk)){                    //任务已经存在则暂停任务await this.Scheduler.Result.ResumeJob(jk);                    await Console.Out.WriteLineAsync(string.Format("任务“{0}”恢复运行", jobName));}}            catch (Exception ex){                await Console.Out.WriteLineAsync(string.Format("恢复任务失败!{0}", ex));}}       /// <summary>/// 停止任务调度        /// </summary>public async void StopScheduleAsync(){            try{                //判断调度是否已经关闭if (!this.Scheduler.Result.IsShutdown){                    //等待任务运行完成await this.Scheduler.Result.Shutdown();                    await Console.Out.WriteLineAsync("任务调度停止!");}}            catch (Exception ex){                await Console.Out.WriteLineAsync(string.Format("任务调度停止失败!", ex));}}        /// <summary>/// 创建类型Simple的触发器        /// </summary>/// <param name="m"></param>/// <returns></returns>private ITrigger CreateSimpleTrigger(ScheduleEntity m){            //作业触发器if (m.RunTimes > 0){                return TriggerBuilder.Create().WithIdentity(m.JobName, m.JobGroup).StartAt(m.BeginTime)//开始时间.EndAt(m.EndTime)//结束数据.WithSimpleSchedule(x => x.WithIntervalInSeconds(m.IntervalSecond)//执行时间间隔,单位秒.WithRepeatCount(m.RunTimes))//执行次数、默认从0开始.ForJob(m.JobName, m.JobGroup)//作业名称               .Build();}            else{                return TriggerBuilder.Create().WithIdentity(m.JobName, m.JobGroup).StartAt(m.BeginTime)//开始时间.EndAt(m.EndTime)//结束数据.WithSimpleSchedule(x => x.WithIntervalInSeconds(m.IntervalSecond)//执行时间间隔,单位秒.RepeatForever())//无限循环.ForJob(m.JobName, m.JobGroup)//作业名称               .Build();}}        /// <summary>/// 创建类型Cron的触发器        /// </summary>/// <param name="m"></param>/// <returns></returns>private ITrigger CreateCronTrigger(ScheduleEntity m){            // 作业触发器return TriggerBuilder.Create().WithIdentity(m.JobName, m.JobGroup).StartAt(m.BeginTime)//开始时间.EndAt(m.EndTime)//结束时间.WithCronSchedule(m.Cron)//指定cron表达式.ForJob(m.JobName, m.JobGroup)//作业名称                   .Build();}}
}

 总结:

开发已个小项目搞了好久才搞完,期间零零散散的开发,还是太懒散了!平时积累不够,还是太菜了!!!!!欢迎各位大佬指导指定我这个新人!

原文:http://www.cnblogs.com/miskis/p/8487634.html


.NET社区新闻,深度好文,欢迎访问公众号文章汇总 http://www.csharpkit.com

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/321993.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

64位的Mac OS X也有Windows.Forms了

Mono长期以来一直为Linux、Mac和Windows提供Windows.Forms的纯C#实现。随着技术潮流的变化&#xff0c;Mono的Windows.Forms平台仍然没有得到很好的发展&#xff0c;还只是各种第三方库和应用程序的依赖&#xff0c;这使得它的重要性超过了那些基于WinForms的应用程序。Mono默认…

.Net 4.X 提前用上 .Net Core 的配置模式以及热重载配置

1. 前言在提倡微服务及 Serverless 越来越普及的当下&#xff0c;传统 .Net 应用的配置模式往往依赖于一个名为 web.config 的 XML 文件&#xff0c;在可扩展性和可读性与时代脱节了。当然&#xff0c;我不会怂恿一下子把所有应用迁移到 .Net Core 上&#xff0c;本文将在尽量不…

C# 枚举特性 FlagAttribute 的应用

写在前面枚举Enum 全称(Enumeration)&#xff0c;即一种由一组称为枚举数列表的命名常量组成的独特类型。可以看出枚举的出现是为了使我们可以在程序中方便的使用一些特定值的常量&#xff0c;一般的使用大家都比较熟悉&#xff0c;本文主要介绍枚举的特性 FlagAttribute。Flag…

重温.NET下Assembly的加载过程

最近在工作中牵涉到了.NET下的一个古老的问题&#xff1a;Assembly的加载过程。虽然网上有很多文章介绍这部分内容&#xff0c;很多文章也是很久以前就已经出现了&#xff0c;但阅读之后发现&#xff0c;并没能解决我的问题&#xff0c;有些点写的不是特别详细&#xff0c;让人…

看eShopOnContainers学一个EventBus

最近在看微软eShopOnContainers 项目&#xff0c;看到事件总线觉得不错&#xff0c;和大家分享一下看完此文你将获得什么&#xff1f;eShop中是如何设计事件总线的实现一个InMemory事件总线eShop中是没有InMemory实现的&#xff0c;这算是一个小小小的挑战发布订阅模式发布订阅…

创建基于MailKit和MimeKit的.NET基础邮件服务

邮件服务是一般的系统都会拥有和需要的功能&#xff0c;但是对于.NET项目来说&#xff0c;邮件服务的创建和使用会较为的麻烦。.NET对于邮件功能提供了System.Net.Mail用于创建邮件服务&#xff0c;该基础服务提供邮件的基础操作&#xff0c;并且使用也较为的简单。对于真正将该…

EF Core下利用Mysql进行数据存储在并发访问下的数据同步问题

小故事在开始讲这篇文章之前&#xff0c;我们来说一个小故事&#xff0c;纯素虚构&#xff08;真实的存钱逻辑并非如此&#xff09;小刘发工资后&#xff0c;赶忙拿着现金去银行&#xff0c;准备把钱存起来&#xff0c;而与此同时&#xff0c;小刘的老婆刘嫂知道小刘的品性&…

牛客练习赛50-记录

正题 比赛链接:https://ac.nowcoder.com/acm/contest/1080#question 成绩 本届 升高二届 总结 以后还是不要写太多自己不擅长的写法&#xff0c;空间要多检查&#xff0c;不要像个傻逼一样啥都写错。 尽量不要为了省一点空间和时间写一些不舒服的东西&#xff0c;尽量在能…

物联网框架ServerSuperIO在.NetCore实现跨平台的实践路线

正所谓天下大势&#xff0c;不跟风不行。你不跨平台&#xff0c;很low嘛。java说&#xff1a;你们能跨嘛&#xff0c;跨给我看看。C#说&#xff1a;不要强人所难嘛。java说&#xff1a;能部署在云上吗&#xff1f;docker&#xff1f;微服务&#xff1f;C#说&#xff1a;不要强人…

使用WebApiClient请求和管理Restful Api

前言本篇文章的内容是WebApiClient应用说明篇&#xff0c;如果你没有了解过WebApiClient&#xff0c;可以先阅读以下相关文章&#xff1a;WebApi client 的面向切面编程我来给.Net设计一款HttpClient.Net45下HttpClient的几个缺陷.net的retrofit--WebApiClient库.net的retrofit…

拥抱.NET Core系列:MemoryCache 缓存选项

MSCache项目MSCache 目前最新的正式版是 2.0.0&#xff0c;预览版是2.1.0&#xff0c;会与 .NETCore 2.1 一起发布。本篇用了2.0.0版本开源在 GitHub 上&#xff0c;仓库地址是&#xff1a;https://github.com/aspnet/CachingNuGet地址为&#xff1a;https://www.nuget.org/pac…

牛客练习赛51-记录

正题 比赛链接:https://ac.nowcoder.com/acm/contest/1083#question 成绩 可怜的zycT3zycT3zycT3被n0n0n0卡了半天&#xff0c;这里感谢一下排雷 总结 比赛状态较好&#xff0c;后面没有T6T6T6的题解 T1:abcT1:abcT1:abc 题目大意 给出一个字符串&#xff0c;求有多少个abc…

Metrics.net + influxdb + grafana 构建WebAPI的自动化监控和预警

前言这次主要分享通过Metrics.net influxdb grafana 构建WebAPI的自动化监控和预警方案。通过执行耗时&#xff0c;定位哪些接口拖累了服务的性能&#xff1b;通过请求频次&#xff0c;设置适当的限流和熔断机制&#xff0c;拦截非法或不合理的请求&#xff0c;保障服务的可用…

EF Core 2.1路线图:视图、GROUP BY和惰性加载

Entity Framework Core一直追随着初始Entity Framework的发展&#xff0c;并不断推陈出新。它首先推出的是对视图的支持&#xff0c;这听起来有些耸人听闻。在即将推出的EF Core 2.1之前&#xff0c;EF Core并未对数据库视图提供官方的支持&#xff0c;也不支持缺少主键的数据库…

计算机网络总结

一、计算机网络体系 &#xff08;1&#xff09;OSI分层 &#xff08;7层&#xff09; 物理层、数据链路层、网络层、传输层、会话层、表示层、应用层。 &#xff08;2&#xff09;TCP/IP分层&#xff08;4层&#xff09; 网络接口层、 网际层、运输层、 应用层。 &#xff0…

从技术角度讨论微服务

本文希望从技术角度来探讨下微服务&#xff0c;因此&#xff0c;不会过多地谈及如何根据业务进行微服务划分&#xff0c;更多是介绍微服务的相关技术&#xff0c;微服务的业务划分方法可参考“领域驱动设计“相关方法论。微服务的两个程度一、服务化复杂的单体架构会有以下的挑…

jzoj6344-[NOIP2019模拟2019.9.7]Huge Counting【组合数,状压dp】

正题 题目大意 定义函数f(x)(xf(x)(xf(x)(x为一个序列))) 若任意一个xi1x_i1xi​1那么有f(x)1f(x)1f(x)1 若有一个xi0x_i0xi​0那么有f(x)0f(x)0f(x)0 其他的&#xff0c;有f(x)(∑j1nf(x1...,xj−1,...xn))%2f(x)(\sum_{j1}^nf(x_{1}...,x_j-1,...x_n))\% 2f(x)(j1∑n​f(x1​…

拥抱.NET Core系列:MemoryCache 缓存域

MSCache项目MSCache 目前最新的正式版是 2.0.0&#xff0c;预览版是2.1.0&#xff0c;会与 .NETCore 2.1 一起发布。本篇用了2.0.0版本开源在 GitHub 上&#xff0c;仓库地址是&#xff1a;https://github.com/aspnet/CachingNuGet地址为&#xff1a;https://www.nuget.org/pac…

牛客小白月赛17-记录(附题解)

正题 比赛链接:https://ac.nowcoder.com/acm/contest/1085#question 成绩 总结 除了那道积分数学其他还好 后面没有FFF题的题解 T1:小sun的假期T1:小sun的假期T1:小sun的假期 题目大意 长度为nnn的序列&#xff0c;mmm个区间&#xff0c;求最大的没有被任何区间覆盖的区间…

安全、高效的MySQL DDL解决方案

MySQL作为目前应用最广泛的开源关系型数据库&#xff0c;是许多网站、应用和商业产品的主要数据存储。在生产环境&#xff0c;线上数据库常常面临着持续的、不断变化的表结构修改&#xff08;DDL&#xff09;&#xff0c;如增加、更改、删除字段和索引等等。其中一些DDL操作在M…