在日常的软件开发中,经常需要执行一些定时任务,比如发送报告、备份数据库等。虽然Windows任务计划程序和MSSQL Server代理任务调度器可以完成这些工作,但它们要么不够灵活,要么操作复杂。因此,决定编写一个自己的任务调度器,它可以通过一个易于管理的ASP.NET页面来操作。
本项目支持每日和每周的任务调度(未来计划增加更多功能),并提供两种任务步骤:运行脚本和执行存储过程。对于一个任务,可以添加多个步骤,并且可以启用或禁用任务,或者立即运行任务。
本项目的主要目标是发送每周报告,并集中管理需要自动化的任务,如备份任务和其他数据库维护任务。不想依赖Windows任务计划程序或MS SQL Server中的作业调度器,所以创建了自己的调度器,它有一个清晰的管理页面,可以轻松地管理任务。
项目主要分为两个部分:
MS SQL Server数据库:(文件:JOBS_db.zip)这个数据库包含了一些表和存储过程。它构成了系统的框架,存储任务数据和其他相关信息。此外,还有一些管理任务的存储过程,例如:插入、编辑和删除任务或任务步骤。这些存储过程通过.NET应用程序调用。
Visual Studio项目:(文件:jobs.zip)这个解决方案包含了项目的管理页面。它有一些SQL执行例程,用于插入、编辑或删除任务或任务步骤。它提供了任务的清晰概览,并提供了一些额外的功能,如启用/禁用或立即运行任务。
关于这个项目的主要问题是,如何在不需要启动浏览器或其他任何东西的情况下,在预定的时间在后台执行任务。答案非常简单:只需要在SQL Server代理中创建一个每天每10秒运行一次的作业。它就像一个无限循环,什么都不做;只是启动一个存储过程,执行并处理存储在JOBS表中的任务。这样,所有预定义的任务都可以在正确的时间在后台静默执行。
需要放置在循环作业中的存储过程叫做begin_jobs(也在zip文件中)。它为JOBS表创建一个游标,并遍历必须在正确时间执行的作业记录,然后逐步触发它们。
CREATE PROCEDURE [dbo].[begin_jobs] AS BEGIN SET NOCOUNT ON; DECLARE @job_id int DECLARE jobCursor CURSOR FOR SELECT id FROM JOBS WHERE [enabled] = 1 AND valid = 1 AND job_is_running = 0 AND next_run <= GETDATE() OPEN jobCursor FETCH FROM jobCursor INTO @job_id WHILE (@@FETCH_STATUS = 0) BEGIN EXEC [dbo].[execute_job] @job_id FETCH FROM jobCursor INTO @job_id END CLOSE jobCursor DEALLOCATE jobCursor END
一旦数据库结构建立,就需要更多的存储过程来管理任务。以下示例将详细描述通过ASP.NET页面插入任务的过程。插入新行到JOBS表的存储过程如下:
CREATE PROCEDURE [dbo].[insert_job] @scheduler_type varchar(1024), @description varchar(1024), @monday bit, @tuesday bit, @wednesday bit, @thursday bit, @friday bit, @saturday bit, @sunday bit, @occurs_at datetime AS BEGIN SET NOCOUNT ON; DECLARE @schedule_type_id int, @job_id int SET @schedule_type_id = (SELECT TOP 1 id FROM SCHEDULE_TYPES WHERE UPPER([description]) = UPPER(@schedule_type)) IF @description = '' OR @description IS NULL OR @schedule_type_id IS NULL RETURN INSERT INTO JOBS (schedule_type_id, [description]) VALUES (@schedule_type_id, @description) SET @job_id = (SELECT @@IDENTITY) INSERT INTO SCHEDULES (job_id, monday, tuesday, wednesday, thursday, friday, saturday, sunday, occurs_at) VALUES (@job_id, @monday, @tuesday, @wednesday, @thursday, @friday, @saturday, @sunday, @occurs_at) -- UPDATE next_run EXEC [dbo].[update_next_run] @job_id -- UPDATE schedule_description EXEC [dbo].[update_schedule_description] @job_id END
ASP.NET应用程序将在点击创建任务按钮时调用上述过程:
using System.Data; using System.Data.SqlClient; using HelperLib; private string connStringJobs = ConfigurationManager.ConnectionStrings["jobs"].ToString(); protected void btnCreate_Click(object sender, EventArgs e) { //...some security checks SqlHelper mySqlHelper = new SqlHelper(connStringJobs, CommandType.StoredProcedure, "insert_job", new SqlParameter("@schedule_type", dropListScheduleTypes.SelectedItem.Text), new SqlParameter("@description", txtDescription.Text), new SqlParameter("@monday", monday), new SqlParameter("@tuesday", tuesday), new SqlParameter("@wednesday", wednesday), new SqlParameter("@thursday", thursday), new SqlParameter("@friday", friday), new SqlParameter("@saturday", saturday), new SqlParameter("@sunday", sunday), new SqlParameter("@occurs_at", occurs_at)); mySqlHelper.ExecuteNonQuery(); mySqlHelper.Close(); Response.Redirect("Default.aspx"); }
connStringJobs变量的值来自web.config文件:
<add name="jobs" connectionString="server=xxx;database=JOBS;UID=userreader;PWD=nopass" providerName="System.Data.SqlClient" />
它有一个硬编码的用户名和密码,这也需要在SQL Server中创建为数据库用户登录。建议是给它管理员权限,因为这个用户将执行JOB表中的所有作业。这不是最好的安全方法,但如果计划在自己的任务中运行脚本,那么在没有给该用户管理员权限的情况下运行外部脚本可能会遇到一些权限问题。如果只运行存储过程,那么在相关数据库上执行权限就足够了,包括JOBS数据库。在JOBS数据库上执行权限是必须的。
SqlHelper类也应该解释一下,因为它不是标准的C#类。它是收集的SQL工具。它简化了与SQL Server的连接,并有许多其他有用的功能和方法,使编码更短。如果有人需要它的源代码,就问。
将JOBS.bak(JOBS_db.zip)数据库恢复到SQL Server数据库中。可以使用Microsoft SQL Server Management Studio Restore向导来完成这个操作。右键单击数据库,然后单击“还原数据库...”。然后按照屏幕上的说明进行操作。成功恢复数据库后,将在JOBS数据库中看到上述提到的表和存储过程,还可以在里面找到一些示例任务,稍后可以在管理页面中删除。
在JOBS数据库中创建一个数据库用户登录,并给它管理员权限。将其命名为userreader,密码设置为nopass。
用Visual Studio打开jobs.sln(jobs.zip)解决方案,并修改web.config文件中的两个连接字符串以满足需求,然后将其发布到站点上。