欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2022-08-10 11:47:19
今天这篇文章我将通过实例代码带着大家一步一步通过abp vNext这个asp.net core的快速开发框架来进行Quartz.net定时任务调度的管理界面的开发。大伙最好跟着一起敲一下代码,当然源码我会上传到github上,有兴趣的小伙伴可以在文章底部查看源码链接。 作者:依乐祝 原文链接:htt ......

今天这篇文章我将通过实例代码带着大家一步一步通过abp vnext这个asp.net core的快速开发框架来进行quartz.net定时任务调度的管理界面的开发。大伙最好跟着一起敲一下代码,当然源码我会上传到github上,有兴趣的小伙伴可以在文章底部查看源码链接。

作者:依乐祝
原文链接:

写在前面

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

概念

开始之前还有必要跟大伙说一下abp vnext以及quartz.net是什么,防止有小白。如果对这两个概念非常熟悉的话可以直接阅读下一节。项目最终实现的效果如下图所示:

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

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官网:
github:
文档:

quartz.net是什么

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

实例演练

这一节我们通过实例进行操作,相信跟着做的你也能够把代码跑起来。

abp vnext代码

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

  1. 打开https://cn.abp.io/templates 然后如图填写对应的项目名称,这里我用的czar.abpdemo 项目类型选择asp.net core mvc应用程序,因为这个是带有ui界面的web项目,数据库提供程序选择efcore这个大家都比较熟悉,然后点击创建就可以了。

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

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

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

  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项目并将其设为启动项目

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

  6. 打开包管理器控制台(package manager console), 选择.entityframeworkcore项目作为默认项目并运行update-database命令:

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

  7. 现在可以运行应用程序,它将会打开home页面:

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

  8. 点击“login” 输入用户名admin, 密码1q2w3e*, 登录应用程序.

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

集成quartz.net管理功能

这部分我们将实现quartz.net定时任务的管理功能,为了进行quartz.net定时任务的管理,我们还需要定义一个表来进行quartz.net定时任务的信息的承载,并完成这个表的增删改查功能,这样我们在对这个表的数据进行操作的同时来进行quartz.net定时任务的操作即可实现我们的需求。话不多说,开始吧。这部分我们再分成两个小节:jobinfo的增删改查功能的实现,quartz.net调度任务功能的增删改查的实现。

jobinfo的增删改查功能的实现

这个部分你将体会到我为什么使用abp vnext框架来进行开发了,就是因为快~~~~

  1. 创建领域实体对象jobinfo,这个在领域层代码如下:

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

  2. 将我们的jobinfo实体添加到dbcontext中,这样应该在ef层

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

  3. 添加新的migration并更新到数据库中,这个应该算efcore的基础了吧,两个步骤,一个“add-migration” 然后“update-database”更新到数据库即可

    add-migration "add_jobinfo_entity"
    update-database
  4. 应用层创建页面显示实体bookdto 用来在 基础设施层 和 应用层 传递数据

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

  5. 同样的你还需要在应用层创建一个用来传递增改的dto对象
    用abp vNext快速开发Quartz.NET定时任务管理界面

  6. 万事俱备,只欠服务了,接下来我们创建一下jobinfo的服务接口以及服务接口的实现了,这里有个约定,就是所有的服务appservice结尾,就跟控制器都以controller结尾的概念差不多。

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

    服务实现:

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

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

  7. 这里abp vnext框架就会自动为我们实现增删改查的api controllers接口的实现(可以通过swagger进行查看),还会自动 为所有的api接口创建了javascript 代理.因此,你可以像调用 javascript function一样调用任何接口.

    如下图所示

    用abp vNext快速开发Quartz.NET定时任务管理界面
    是不是,感觉什么都还没做,所有接口都已经实现的感觉。

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

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

  9. 对应的,我们需要在pages/jobschedule 这个路径下面创建对应的index.cshtml页面,以及新增,编辑的页面。由于内容太多,这里就不贴代码了,只给大家贴下图:

    index.cshtml

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

    createmodal.cshtml代码如下:

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

  10. 然后我们运行起来查看下:

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

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

  12. 到此,jobinfo的增删改查就做好了,是不是很简单,这就是abp vnext赋予我们的高效之处。

quartz.net调度任务功能的增删改的实现

在使用quartz.net之前,你需要通过nuget进行下安装,然后才能进行调用。这里我不会给你详细讲解quartz.net的使用,因为这将占用大量的篇幅,并偏离本文的主旨

  1. 安装quartz.net的nuget包:

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

  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接口:

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

  4. 至此quartz.net调度任务功能完成

集成

这里我们按照之前的思路对jobinfo跟quartz.net任务进行集成

  1. 新增时,启动任务:

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

  2. 编辑时,更新任务

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

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

功能演示

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

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

功能扩展

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

源码地址

github:https://github.com/yilezhu/abpquzatzdemo

总结

本文只是简单的利用abp vnext框架进行quartz.net任务调度进行ui的管理,实现的功能也比较简单,大家完全可以在此基础上进行扩展完善,最后感谢大伙的阅读。