🌹欢迎来到《小5讲堂》🌹
🌹这是《C#》系列文章,每篇文章将以博主理解的角度展开讲解。🌹
🌹温馨提示:博主能力有限,理解水平有限,若有不对之处望指正!🌹
目录
- 前言
- 效果
- Quartz.NET插件
- 1. 创建调度服务类
- 2. 实现动态方法作业
- 3. 在类库中暴露启动接口
- 4. 使用示例
- 在类库中定义要调用的方法
- 在主程序中调用类库
- 5.目标任务类
- 6. 高级配置选项
- 注意事项
- 推荐文章
前言
任务调度一直都有在用,但是自己确实没有从零开始搭建何尝试创建,
最近有伙伴咨询任务调度怎么使用,因此自己也尝试了一边搭建,好在现在有AI工具,简单问一句就能出来我想要的效果,一起来看看吧。
效果
设置每隔10秒执行一次
Quartz.NET插件
要在类库中启动Quartz.NET动态调度功能,你需要创建一个可被主程序调用的入口点。以下是完整的实现方案:
1. 创建调度服务类
在你的类库中创建一个调度服务类,封装所有调度逻辑:
using Quartz;
using Quartz.Impl;
using System;
using System.Collections.Specialized;
using System.Threading.Tasks;
public class DynamicSchedulerService
{
private IScheduler _scheduler;
public async Task StartAsync()
{
// 1. 创建调度器
var factory = new StdSchedulerFactory(GetSchedulerProperties());
_scheduler = await factory.GetScheduler();
// 2. 启动调度器
await _scheduler.Start();
}
public async Task StopAsync()
{
if (_scheduler != null)
{
await _scheduler.Shutdown();
}
}
public async Task ScheduleMethodAsync(
string methodName,
string className,
string cronExpression,
string jobName = null,
string groupName = "default")
{
if (_scheduler == null)
{
throw new InvalidOperationException("调度器未启动,请先调用StartAsync()");
}
jobName ??= $"job_{methodName}_{Guid.NewGuid().ToString("N")}";
// 创建作业
var job = JobBuilder.Create<DynamicMethodJob>()
.WithIdentity(jobName, groupName)
.UsingJobData("MethodName", methodName)
.UsingJobData("ClassName", className)
.Build();
// 创建触发器
var trigger = TriggerBuilder.Create()
.WithIdentity($"trigger_{jobName}", groupName)
.WithCronSchedule(cronExpression)
.Build();
// 安排作业
await _scheduler.ScheduleJob(job, trigger);
}
private NameValueCollection GetSchedulerProperties()
{
// 可以在这里配置线程池、作业存储等设置
return new NameValueCollection
{
// 示例配置,根据需求调整
["quartz.threadPool.threadCount"] = "10",
["quartz.scheduler.instanceName"] = "DynamicScheduler"
};
}
}
2. 实现动态方法作业
在类库中创建动态方法作业类:
using Quartz;
using System;
using System.Reflection;
using System.Threading.Tasks;
public class DynamicMethodJob : IJob
{
public Task Execute(IJobExecutionContext context)
{
try
{
var dataMap = context.JobDetail.JobDataMap;
var typeName = dataMap.GetString("ClassName");
var methodName = dataMap.GetString("MethodName");
if (string.IsNullOrEmpty(typeName) || string.IsNullOrEmpty(methodName))
{
throw new ArgumentException("ClassName和MethodName不能为空");
}
var type = Type.GetType(typeName);
if (type == null)
{
throw new TypeLoadException($"无法加载类型: {typeName}");
}
var method = type.GetMethod(methodName, BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance);
if (method == null)
{
throw new MissingMethodException($"在类型{typeName}中找不到方法{methodName}");
}
object instance = null;
if (!method.IsStatic)
{
instance = Activator.CreateInstance(type);
}
// 调用方法
method.Invoke(instance, null);
return Task.CompletedTask;
}
catch (Exception ex)
{
// 记录日志或处理异常
Console.WriteLine($"作业执行失败: {ex}");
throw new JobExecutionException(ex);
}
}
}
3. 在类库中暴露启动接口
创建一个公共接口供外部调用:
public interface IDynamicScheduler
{
Task StartSchedulerAsync();
Task StopSchedulerAsync();
Task ScheduleMethodAsync(string methodName, string className, string cronExpression, string jobName = null, string groupName = "default");
}
public class DynamicScheduler : IDynamicScheduler
{
private readonly DynamicSchedulerService _schedulerService = new DynamicSchedulerService();
public Task StartSchedulerAsync() => _schedulerService.StartAsync();
public Task StopSchedulerAsync() => _schedulerService.StopAsync();
public Task ScheduleMethodAsync(string methodName, string className, string cronExpression, string jobName = null, string groupName = "default")
{
return _schedulerService.ScheduleMethodAsync(methodName, className, cronExpression, jobName, groupName);
}
}
4. 使用示例
在类库中定义要调用的方法
public class SampleTasks
{
public static void SendDailyReport()
{
Console.WriteLine($"{DateTime.Now}: 正在发送每日报告...");
// 实际发送报告的逻辑
}
public void ProcessData()
{
Console.WriteLine($"{DateTime.Now}: 正在处理数据...");
// 实际处理数据的逻辑
}
}
在主程序中调用类库
using Quartz;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using TaskV2.Base;
namespace TaskV2
{
public class Program
{
static async Task Main(string[] args)
{
// 创建调度器实例
IDynamicScheduler scheduler = new DynamicScheduler();
try
{
// 启动调度器
await scheduler.StartSchedulerAsync();
Console.WriteLine("调度器已启动");
/*
// 调度静态方法 - 每天上午9点执行
await scheduler.ScheduleMethodAsync(
"SendDailyReport",
"YourNamespace.SampleTasks", // 完整类名(包括命名空间)
"0 0 9 * * ?"); // 每天9点
*/
/*
// 调度实例方法 - 每30分钟执行一次
await scheduler.ScheduleMethodAsync(
"ProcessData",
"YourNamespace.SampleTasks",
"0 0/30 * * * ?");
*/
// 查询表 - 执行
List<JobModel> list = new List<JobModel>();
list.Add(new JobModel()
{
methodName = "TestData",
className = "TaskV2.Work.AccessTokenShop",
cronExpression = "0/10 * * * * ?",
});
// 调度实例方法
foreach (JobModel obj in list)
{
await scheduler.ScheduleMethodAsync(
obj.methodName,
obj.className,
obj.cronExpression);
}
Console.WriteLine("按任意键停止...");
Console.ReadKey();
}
finally
{
// 停止调度器
await scheduler.StopSchedulerAsync();
Console.WriteLine("调度器已停止");
}
}
}
}
5.目标任务类
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TaskV2.Work
{
public class AccessTokenShop
{
public int Id = 1;
public void TestData()
{
Console.WriteLine($"TestData{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}");
}
}
}
6. 高级配置选项
如果你需要更复杂的配置,可以扩展DynamicSchedulerService
:
// 在DynamicSchedulerService类中添加
public async Task ScheduleMethodWithParametersAsync(
string methodName,
string className,
string cronExpression,
object[] parameters,
string jobName = null,
string groupName = "default")
{
jobName ??= $"job_{methodName}_{Guid.NewGuid().ToString("N")}";
var job = JobBuilder.Create<DynamicMethodJob>()
.WithIdentity(jobName, groupName)
.UsingJobData("MethodName", methodName)
.UsingJobData("ClassName", className)
.UsingJobData("Parameters", parameters) // 传递参数
.Build();
var trigger = TriggerBuilder.Create()
.WithIdentity($"trigger_{jobName}", groupName)
.WithCronSchedule(cronExpression)
.Build();
await _scheduler.ScheduleJob(job, trigger);
}
然后在DynamicMethodJob
中处理参数:
// 修改DynamicMethodJob的Execute方法
var parameters = context.JobDetail.JobDataMap.Get("Parameters") as object[];
method.Invoke(instance, parameters);
注意事项
- 类型解析:确保
Type.GetType()
能找到你的类,可能需要使用程序集限定名称 - 依赖注入:如果需要使用DI容器创建实例,可以修改作业工厂
- 异常处理:确保作业中的异常被适当处理
- 生命周期:长时间运行的应用需要注意调度器的生命周期管理
- 日志记录:建议添加日志记录以便调试
这样实现后,你的类库就提供了一个完整的、可动态调度方法的Quartz.NET解决方案,可以在任何.NET应用程序中使用。
推荐文章
【C#】Quartz.NET怎么动态调用方法,并且根据指定时间周期执行,动态配置类何方法以及Cron表达式,有请DeepSeek
【C#】.net core6.0无法访问到控制器方法,直接404。由于自己的不仔细,出现个低级错误,这让DeepSeek看出来了,是什么错误呢,来瞧瞧
【C#】Html转Pdf,Spire和iTextSharp结合,.net framework 4.8
【C#】事务(进程 ID 64)与另一个进程被死锁在锁资源上,并且已被选作死锁牺牲品。请重新运行该事务。不能在具有唯一索引“XXX_Index”的对象“dbo.Test”中插入重复键的行。
【C#】使用DeepSeek帮助评估数据库性能问题,C# 使用定时任务,每隔一分钟移除一次表,再重新创建表,和往新创建的表追加5万多条记录
【C#】合理使用DeepSeek相关AI应用为我们提供强有力的开发工具,在.net core 6.0框架下使用JsonNode动态解析json字符串,如何正确使用单问号和双问号做好空值处理
【C#】已经实体类和动态实体类的反射使用方法,两分钟回顾,码上就懂
【C#】使用vue3的axios发起get和post请求.net framework部署的API显示跨域
【C#】.net core 6.0 webapi 使用core版本的NPOI的Excel读取数据以及保存数据
【C#】pdf按页分割文件,以及分页合并,效果还不错,你值得拥有
【C#】未能加载文件或程序集“CefSharp.Core.Runtime.dll”或它的某一个依赖项。找不到指定的模块。
【C#】.net core 6.0 在program时间格式统一json格式化,并列举program默认写法和简化写法
【C#】.net core 6.0 ApiController,API控制器方法,API接口以实体类作为接收参数应该注意的点
【C#】 SortedDictionary,查找字典中是否存在给定的关键字
【C#】.net core 6.0 MVC返回JsonResult显示API接口返回值不可被JSON反序列化
【C#】.net core 6.0 使用第三方日志插件Log4net,配置文件详细说明
【C#】使用代码实现龙年春晚扑克牌魔术(守岁共此时),代码实现篇
【C#】使用代码实现龙年春晚扑克牌魔术(守岁共此时),流程描述篇