用abp vNext快速开发Quartz.NET定时任务管理界面

今天这篇文章我将经过实例代码带着你们一步一步经过abp vNext这个asp.net core的快速开发框架来进行Quartz.net定时任务调度的管理界面的开发。大伙最好跟着一块儿敲一下代码,固然源码我会上传到github上,有兴趣的小伙伴能够在文章底部查看源码连接。html

做者:依乐祝
原文连接:http://www.javashuo.com/article/p-xsaecxlv-kt.htmlgit

写在前面

有几天没更新博客了,一方面由于比较忙,另外一方面是由于最近在准备组织咱们霸都合肥的.NET技术社区首次非正式的线下聚会,忙着联系人啊,这里欢迎有兴趣的小伙伴加我wx:jkingzhu进行详细的了解,固然也欢迎同行加我微信,而后我拉你进入咱们合肥.NET技术社区微信群跟大伙进行交流。github

概念

开始以前还有必要跟大伙说一下abp vNext以及Quartz.net是什么,防止有小白。若是对这两个概念很是熟悉的话能够直接阅读下一节。项目最终实现的效果以下图所示:web

1551252591337

abp vNext是什么

提及abp vNext就要从另外一个概念开始提及了,那就是大名鼎鼎的ABP了。
ABP 官方的介绍是:ASP.NET Boilerplate 是一个用最佳实践和流行技术开发现代 WEB 应用程序的新起点,它旨在成为一个通用的 WEB 应用程序基础框架和项目模板。基于 DDD 的经典分层架构思想,实现了众多 DDD 的概念(但没有实现全部 DDD 的概念)。
而ABPVNext的出现是为了抛弃掉.net framework 版本下的包袱,从新启动的 abp 框架,目的是为了放弃对传统技术的支持,让 asp.net core 可以自身作到更加的模块化,目前这块的内容还不够成熟。缘由是缺乏组件信息和内容。
若是你想用于生产环境建议你可使用ABP,若是你勇于尝试,敢于创新的话能够直接使用abp vNext进行开发的。
abp vNext官网:https://abp.io/
github:https://github.com/abpframework/abp
文档:https://abp.io/documentssql

Quartz.NET是什么

Quartz.NET是一个强大、开源、轻量的做业调度框架,你可以用它来为执行一个做业而建立简单的或复杂的做业调度。它有不少特征,如:数据库支持,集群,插件,支持cron-like表达式等等。目前已经正式支持了.NET Core 和async/await。
说白了就是你可使用Quartz.NET能够很方便的开发定时任务诸如平时的工做中,定时轮询数据库同步,定时邮件通知,定时处理数据等。数据库

实例演练

这一节咱们经过实例进行操做,相信跟着作的你也可以把代码跑起来。json

ABP vNext代码

既然咱们这次演练的项目是使用的abp vNext这个asp.net core的快速开发框架来完成的,因此首先在项目开始以前,你须要到ABP vNext的官网上去下载项目代码。英文站打开慢的话,能够访问中文子域名进行访问:https://cn.abp.io/Templates 。下面给出具体步骤:c#

  1. 打开https://cn.abp.io/Templates 而后如图填写对应的项目名称,这里我用的Czar.AbpDemo 项目类型选择ASP.NET Core MVC应用程序,由于这个是带有UI界面的web项目,数据库提供程序选择EFCore这个你们都比较熟悉,而后点击建立就能够了。微信

    1551248124416

  2. 下载后,解压到一个文件夹下面,而后用vs打开解决方案,看到以下图所示的项目结构架构

    1551248279486

  3. 这里简单介绍下,每一个项目的做用,具体的就不过多介绍了,在下面的实战代码中慢慢体会吧

    • .Domain 为领域层.
    • .Application 为应用层.
    • .Web 为是表示层.
    • .EntityFrameworkCore 是EF Core集成.

    解决方案还包含配置好的的单元&集成测试项目, 以便与于EF CoreSQLite 数据库配合使用.

  4. 查看.Web项目下appsettings.json文件中的 链接字符串并进行相应的修改,怎么改不要问我:

    {
      "ConnectionStrings": {
        "Default": "Server=localhost;Database=CzarAbpDemo;Trusted_Connection=True;MultipleActiveResultSets=true"
      }
    }
  5. 右键单击.Web项目并将其设为启动项目

    1551248539374

  6. 打开包管理器控制台(Package Manager Console), 选择.EntityFrameworkCore项目做为默认项目并运行Update-Database命令:

    1551248591831

  7. 如今能够运行应用程序,它将会打开home页面:

    1551248689114

  8. 点击“Login” 输入用户名admin, 密码1q2w3E*, 登陆应用程序.

    启动模板包括 身份管理(identity management) 模块. 登陆后将提供身份管理菜单,你能够在其中管理角色,用户及其权限. 这个不过多讲解了,本身去动手操做一番吧

集成Quartz.NET管理功能

这部分咱们将实现Quartz.NET定时任务的管理功能,为了进行Quartz.NET定时任务的管理,咱们还须要定义一个表来进行Quartz.NET定时任务的信息的承载,并完成这个表的增删改查功能,这样咱们在对这个表的数据进行操做的同时来进行Quartz.NET定时任务的操做便可实现咱们的需求。话很少说,开始吧。这部分咱们再分红两个小节:JobInfo的增删改查功能的实现,Quartz.NET调度任务功能的增删改查的实现。

JobInfo的增删改查功能的实现

这个部分你将体会到我为何使用abp vNext框架来进行开发了,就是由于快~~~~

  1. 建立领域实体对象JobInfo,这个在领域层代码以下:

    1551249480050

  2. 将咱们的JobInfo实体添加到DBContext中,这样应该在EF层

    1551249406105

  3. 添加新的Migration并更新到数据库中,这个应该算EFCore的基础了吧,两个步骤,一个“Add-Migration” 而后“Update-Database”更新到数据库便可

    Add-Migration "Add_JobInfo_Entity"
    Update-Database
  4. 应用层建立页面显示实体BookDto 用来在 基础设施层 和 应用层 传递数据

    1551249983515

  5. 一样的你还须要在应用层建立一个用来传递增改的Dto对象
    1551250041669

  6. 万事俱备,只欠服务了,接下来咱们建立一下JobInfo的服务接口以及服务接口的实现了,这里有个约定,就是全部的服务AppService结尾,就跟控制器都以Controller结尾的概念差很少。

    1551250166378

    服务实现:

    1551250189323

    注释还算清真,相信你应该能看懂。

  7. 这里abp vNext框架就会自动为咱们实现增删改查的API Controllers接口的实现(能够经过swagger进行查看),还会自动 为全部的API接口建立了JavaScript 代理.所以,你能够像调用 JavaScript function同样调用任何接口.

    以下图所示

    1551250400532
    是否是,感受什么都还没作,全部接口都已经实现的感受。

  8. 新增一个菜单任务调度的菜单,以下代码所示:

    1551250546971

  9. 对应的,咱们须要在Pages/JobSchedule 这个路径下面建立对应的Index.cshtml页面,以及新增,编辑的页面。因为内容太多,这里就不贴代码了,只给你们贴下图:

    Index.cshtml

    1551250659492

    CreateModal.cshtml代码以下:

    1551250688733

  10. 而后咱们运行起来查看下:

    1551250773502

  11. 点击,右上角的新增,会弹出新增界面,点击每一行的操做,会弹出删除(删除,这里只作了一个假功能),编辑的两个选项。

  12. 到此,JobInfo的增删改查就作好了,是否是很简单,这就是abp vNext赋予咱们的高效之处。

Quartz.NET调度任务功能的增删改的实现

在使用Quartz.NET以前,你须要经过Nuget进行下安装,而后才能进行调用。这里我不会给你详细讲解Quartz.NET的使用,由于这将占用大量的篇幅,并偏离本文的主旨

  1. 安装Quartz.NET的Nuget包:

    1551251014507

  2. 新建一个ScheduleCenter 的任务调度中心,代码以下所示:

    /// <summary>
        /// 任务调度中心
        /// </summary>
        public class ScheduleCenter
        {
            private readonly ILogger _logger;
            public ScheduleCenter(ILogger<ScheduleCenter> logger)
            {
                _logger = logger;
            }
    
            /// <summary>
            /// 任务计划
            /// </summary>
            public IScheduler scheduler = null;
            public  async Task<IScheduler> GetSchedulerAsync()
            {
                if (scheduler != null)
                {
                    return scheduler;
                }
                else
                {
                    // 从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.usePropert ies","true"}
    
                    };
                    StdSchedulerFactory factory = new StdSchedulerFactory(props);
                    return await factory.GetScheduler();
    
                }
            }
    
            /// <summary>
            /// 添加调度任务
            /// </summary>
            /// <param name="jobName">任务名称</param>
            /// <param name="jobGroup">任务分组</param>
            /// <returns></returns>
            public async Task<bool> AddJobAsync(CreateUpdateJobInfoDto infoDto)
            {
                try
                {
                    if (infoDto!=null)
                    {
                        if (infoDto.StarTime == null)
                        {
                            infoDto.StarTime = DateTime.Now;
                        }
                        DateTimeOffset starRunTime = DateBuilder.NextGivenSecondDate(infoDto.StarTime, 1);
                        if (infoDto.EndTime == null)
                        {
                            infoDto.EndTime = DateTime.MaxValue.AddDays(-1);
                        }
                        DateTimeOffset endRunTime = DateBuilder.NextGivenSecondDate(infoDto.EndTime, 1);
                        scheduler = await GetSchedulerAsync();
                        JobKey jobKey = new JobKey(infoDto.JobName, infoDto.JobGroup);
                        if (await scheduler.CheckExists(jobKey))
                        {
                            await scheduler.PauseJob(jobKey);
                            await scheduler.DeleteJob(jobKey);
                        }
                        IJobDetail job = JobBuilder.Create<LogTestJob>()
                          .WithIdentity(jobKey)
                          .Build();
                        ICronTrigger trigger = (ICronTrigger)TriggerBuilder.Create()
                                                     .StartAt(starRunTime)
                                                     .EndAt(endRunTime)
                                                     .WithIdentity(infoDto.JobName, infoDto.JobGroup)
                                                     .WithCronSchedule(infoDto.CronExpress)
                                                     .Build();
                        await scheduler.ScheduleJob(job, trigger);
                        await scheduler.Start();
                        return true;
                    }
    
                    return false;//JobInfo为空
                }
                catch (Exception ex)
                {
                    _logger.LogException(ex);
                    return false;//出现异常
                }
            }
    
            /// <summary>
            /// 暂停指定任务计划
            /// </summary>
            /// <param name="jobName">任务名</param>
            /// <param name="jobGroup">任务分组</param>
            /// <returns></returns>
            public async Task<bool> StopJobAsync(string jobName, string jobGroup)
            {
                try
                {
                    JobKey jobKey = new JobKey(jobName, jobGroup);
                    scheduler = await GetSchedulerAsync();
                    if (await scheduler.CheckExists(jobKey))
                    {
                        await scheduler.PauseJob(new JobKey(jobName, jobGroup));
                        return true;
                    }
                    else
                    {
                        return false;//任务不存在
                    }
                }
                catch (Exception ex)
                {
                    _logger.LogException(ex);
                    return false;//出现异常
                }
            }
    
            /// <summary>
            /// 恢复指定的任务计划,若是是程序奔溃后 或者是进程杀死后的恢复,此方法无效
            /// </summary>
            /// <param name="jobName">任务名称</param>
            /// <param name="jobGroup">任务组</param>
            /// <returns></returns>
            public async Task<bool> ResumeJobAsync(string jobName, string jobGroup)
            {
                try
                {
                    JobKey jobKey = new JobKey(jobName, jobGroup);
                    scheduler = await GetSchedulerAsync();
                    if (await scheduler.CheckExists(jobKey))
                    {
                        //resumejob 恢复
                        await scheduler.ResumeJob(new JobKey(jobName, jobGroup));
                        return true;
                    }
                    else
                    {
                        return false;//不存在任务
                    }
    
                }
                catch (Exception ex)
                {
                    _logger.LogException(ex);
                    return false;//出现异常
                }
            }
    
            /// <summary>
            /// 恢复指定的任务计划,若是是程序奔溃后 或者是进程杀死后的恢复,此方法无效
            /// </summary>
            /// <param name="jobName">任务名称</param>
            /// <param name="jobGroup">任务组</param>
            /// <returns></returns>
            public async Task<bool> DeleteJobAsync(string jobName, string jobGroup)
            {
                try
                {
                    JobKey jobKey = new JobKey(jobName, jobGroup);
                    scheduler = await GetSchedulerAsync();
                    if (await scheduler.CheckExists(jobKey))
                    {
                        //DeleteJob 恢复
                        await scheduler.DeleteJob(jobKey);
                        return true;
                    }
                    else
                    {
                        return false;//不存在任务
                    }
    
                }
                catch (Exception ex)
                {
                    _logger.LogException(ex);
                    return false;//出现异常
                }
            }
        }
  3. 新建一个LogTestJob 的计划任务,代码以下所示,须要继承IJob接口:

    1551251169229

  4. 至此Quartz.NET调度任务功能完成

集成

这里咱们按照以前的思路对JobInfo跟Quartz.NET任务进行集成

  1. 新增时,启动任务:

    1551251315532

  2. 编辑时,更新任务

    1551251351318

  3. 这里细心的网友,可能注意到任务的删除是在编辑里面进行实现的。而列表页面的删除功能并无实现真正意义的功能的删除。

功能演示

上面咱们演示的任务是一个每5秒写入当前时间的一个任务,并实现了对这个任务的新增,删除,编辑的功能,这里大伙能够自行实现进行测试,也能够下载个人代码进行尝试。效果图以下所示:

1551251560062

功能扩展

目前只能对既定义好任务进行调度,后期能够根据任务的名称,如咱们实例中的测试任务LogTestJob 的名字找到这个任务,而后动态的进行处理。这样就能够在界面实现对多个任务进行调度了!固然还有其余的扩展,本文只是做为引子。

源码地址

GitHub:https://github.com/yilezhu/AbpQuzatzDemo

总结

本文只是简单的利用abp vNext框架进行Quartz.NET任务调度进行UI的管理,实现的功能也比较简单,你们彻底能够在此基础上进行扩展完善,最后感谢大伙的阅读。

相关文章
相关标签/搜索