一個功能強大且易於使用的 .NET 8.0 排程工作器程式庫,提供完整的排程執行功能。
- 版本: 1.21.365
- 作者: Chen Jaofeng
- 許可證: MIT
- 目標框架: .NET 8.0
日期 | 版本 | 說明 |
---|---|---|
2025-08-07 | v1.21.365 | 1. 新增 AddSchedulePlaner 方法,取代 UseSchedulePlaner 2. 增加 UseSchedulePlaner 的過時標記3. 處理排程執行中的取消例外狀況 |
2025-08-06 | v1.20.353 | 1. 重構專案,新增完整文件說明 2. 新增測試專案,包含基本功能測試、排程項目測試、工作器測試等 3. 解決反射測試問題,新增 PlanAttributeReflectionTests.cs 4. 改善測試覆蓋率和穩定性 |
2023-06-05 | v1.12.235 | 首次發布 |
- ✅ 一次性執行 (
Once
) - 在指定時間執行一次 - ✅ 每日執行 (
Day
) - 以日為單位的週期執行 - ✅ 每週執行 (
Week
) - 以週為單位的週期執行 - ✅ 每月執行 (
Month
) - 在指定月份和日期執行 - ✅ 每月週執行 (
MonthWeek
) - 在指定月份的某週某日執行 - ✅ 生命週期執行 (
Startup
/Stoped
) - 程式啟動/停止時執行
使用 PlanAttribute
裝飾靜態方法,自動註冊為排程任務:
[Plan("09:00:00", 1)] // 每日早上9點執行
public static void DailyTask()
{
Console.WriteLine("每日任務執行");
}
[Plan("14:30:00", 1, WeekDays.Friday)] // 每週五下午2:30執行
public static void WeeklyReport()
{
Console.WriteLine("週報產生");
}
[Plan(PlanTypes.Startup)] // 程式啟動時執行
public static void InitializeTask()
{
Console.WriteLine("初始化任務");
}
支援簡易表達式格式:
一次性執行: "1 yyyy-MM-dd HH:mm:ss"
每日執行: "2 yyyy-MM-dd HH:mm:ss period"
每週執行: "3 yyyy-MM-dd HH:mm:ss period weekdays"
每月執行: "4 yyyy-MM-dd HH:mm:ss months days"
月週執行: "5 yyyy-MM-dd HH:mm:ss months weeknos weekdays"
程式啟動: "6"
程式停止: "7"
Install-Package CJF.Schedule
dotnet add package CJF.Schedule
<PackageReference Include="CJF.Schedule" Version="1.21.365" />
在 Program.cs
中註冊背景服務:
using CJF.Schedules;
using Microsoft.Extensions.Hosting;
var host = Host.CreateDefaultBuilder(args)
.ConfigureServices(services =>
{
// 使用 AddSchedulePlaner 方法註冊排程工作器服務
services.AddSchedulePlaner();
// 或使用自訂設定
// services.AddSchedulePlaner(opts =>
// {
// opts.Delay = 2; // 延遲 2 秒啟動
// opts.Interval = 30; // 每 30 秒檢查一次
// opts.AutoBind = true; // 自動綁定 PlanAttribute
// });
})
.Build();
host.Run();
using CJF.Schedules;
public class ScheduledTasks
{
// 每日上午8點執行
[Plan("08:00:00", 1)]
public static void MorningTask()
{
Console.WriteLine($"早晨任務執行: {DateTime.Now}");
}
// 每兩天下午3點執行
[Plan("15:00:00", 2)]
public static void BidailyTask()
{
Console.WriteLine($"隔日任務執行: {DateTime.Now}");
}
// 每週二和週四上午10點執行
[Plan("10:00:00", 1, WeekDays.Tuesday | WeekDays.Thursday)]
public static void WeeklyTask()
{
Console.WriteLine($"週期任務執行: {DateTime.Now}");
}
// 每月15號下午5點執行
[Plan("17:00:00", Months.All, Days.Day15)]
public static void MonthlyTask()
{
Console.WriteLine($"月度任務執行: {DateTime.Now}");
}
// 每月第一個週一上午9點執行
[Plan("09:00:00", Months.All, WeekNo.First, WeekDays.Monday)]
public static void MonthlyWeekTask()
{
Console.WriteLine($"月週任務執行: {DateTime.Now}");
}
// 程式啟動時執行
[Plan(PlanTypes.Startup)]
public static void StartupTask()
{
Console.WriteLine("程式啟動初始化完成");
}
// 程式停止時執行
[Plan(PlanTypes.Stoped)]
public static void ShutdownTask()
{
Console.WriteLine("程式正在關閉,執行清理作業");
}
}
主要的背景服務,繼承自 BackgroundService
,負責:
- 自動掃描帶有
PlanAttribute
的靜態方法 - 定期檢查排程執行時間(預設30秒間隔)
- 管理排程生命週期
裝飾器屬性,用於標記要排程執行的靜態方法:
- 支援多種建構函式以適應不同排程類型
- 自動解析排程參數
- 彈性的時間和週期設定
SchedulePlan
: 代表單一排程項目PlanCollection
: 管理多個排程項目TimePlan
: 處理時間計算邏輯
public sealed class PlanWorkerOptions
{
/// <summary>PlanWorker 開始執行後延遲執行排程項目的時間,單位秒</summary>
public int Delay { get; set; } = 0;
/// <summary>PlanWorker 排程項目檢查週期的間隔時間,單位秒</summary>
public int Interval { get; set; } = 30;
/// <summary>是否自動綁定具有 <see cref="PlanAttribute"/> 的靜態方法。</summary>
public bool AutoBind { get; set; } = true;
}
屬性說明:
Delay
: 程式啟動後延遲多少秒才開始執行排程檢查(預設:0 秒)Interval
: 每次檢查排程的間隔時間(預設:30 秒)AutoBind
: 是否自動綁定帶有PlanAttribute
的靜態方法(預設:true
)
var host = Host.CreateDefaultBuilder(args)
.ConfigureServices(services =>
{
// 使用 AddSchedulePlaner 方法註冊服務
services.AddSchedulePlaner(opts =>
{
opts.Delay = 2; // 延遲 2 秒啟動
opts.Interval = 30; // 每 30 秒檢查一次
opts.AutoBind = false; // 不自動綁定 PlanAttribute
});
})
.Build();
排程表達式使用空白區隔的欄位格式:
排程類型 | 格式 | 範例 |
---|---|---|
一次性 | 1 yyyy-MM-dd HH:mm:ss |
1 2025-12-31 23:59:59 |
每日 | 2 yyyy-MM-dd HH:mm:ss period |
2 2025-01-01 02:00:00 5 |
每週 | 3 yyyy-MM-dd HH:mm:ss period weekdays |
3 2025-01-01 02:00:00 1 1/3/5 |
每月 | 4 yyyy-MM-dd HH:mm:ss months days |
4 2025-01-01 02:00:00 A 1/15 |
月週 | 5 yyyy-MM-dd HH:mm:ss months weeknos weekdays |
5 2025-01-01 02:00:00 A 2/4 1/5 |
啟動時 | 6 |
6 |
停止時 | 7 |
7 |
[Plan("2 2024-01-01 09:00:00 1")] // 從2024年1月1日開始,每日上午9點執行
public static void ExpressionBasedTask()
{
Console.WriteLine("表達式排程執行");
}
[Plan("4 2025-01-01 12:00:00 A 1/15")] // 每月1號和15號中午12點執行
public static void BiweeklyTask()
{
Console.WriteLine("每月雙週任務執行");
}
using CJF.Schedules;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
// 取得 PlanWorker 服務
var planWorker = host.Services.GetRequiredService<PlanWorker>();
// 動態新增排程
planWorker.AppendPlan(new SchedulePlan(
"每 5 天執行",
"2 2025-01-01 03:00:00 5",
() => Console.WriteLine($"[Every 5 days] {DateTime.Now}")
));
planWorker.AppendPlan(new SchedulePlan(
"每週一三五",
"3 2025-01-01 08:00:00 1 1/3/5",
() => Console.WriteLine($"[Mon/Wed/Fri] {DateTime.Now}")
));
planWorker.AppendPlan(new SchedulePlan(
"每月最後一週的週五",
new TimePlan(DateTime.Now, Months.All, WeekNo.Last, WeekDays.Friday),
(plan) => Console.WriteLine($"[{plan.Name}] {DateTime.Now}")
));
public enum PlanTypes : int
{
None = 0, // 無
Once = 1, // 執行一次
Day = 2, // 每隔 N 天
Week = 3, // 每隔 N 週
Month = 4, // 每月
MonthWeek = 5, // 每月週
Startup = 6, // 程式啟動時
Stoped = 7, // 程式停止時
}
// 星期列舉(支援位元運算)
[Flags]
public enum WeekDays
{
None = 0, Sunday = 0x01, Monday = 0x02, Tuesday = 0x04,
Wednesday = 0x08, Thursday = 0x10, Friday = 0x20, Saturday = 0x40,
All = 0x7F
}
// 月份列舉(支援位元運算)
[Flags]
public enum Months { /* January = 0x01, ..., December = 0x0800, All = 0x0FFF */ }
// 週別列舉(支援位元運算)
[Flags]
public enum WeekNo { /* First = 0x01, ..., Last = 0x80, All = 0xBF */ }
// 日期列舉(支援位元運算)
[Flags]
public enum Days { /* Day1 = 0x01, ..., Day31 = 0x40000000, All = 0x7FFFFFFF */ }
using CJF.Schedules;
using Microsoft.Extensions.Hosting;
public class Program
{
public static void Main(string[] args)
{
var host = Host.CreateDefaultBuilder(args)
.UseSchedulePlaner()
.Build();
host.Run();
}
// 程式啟動時執行
[Plan(PlanTypes.Startup)]
static void OnStartup()
{
Console.WriteLine($"[Startup] {DateTime.Now}");
}
// 程式停止時執行
[Plan(PlanTypes.Stoped)]
static void OnShutdown()
{
Console.WriteLine($"[Shutdown] {DateTime.Now}");
}
// 每天 02:00:00 執行
[Plan("02:00:00", 1)]
static void DailyTask()
{
Console.WriteLine($"[Daily] {DateTime.Now}");
}
// 每週二、周五 09:30:00 執行
[Plan("09:30:00", 1, WeekDays.Tuesday | WeekDays.Friday)]
static void WeeklyReport()
{
Console.WriteLine($"[Weekly Report] {DateTime.Now}");
}
// 使用表示式:每月 1 號和 15 號的 12:00:00 執行
[Plan("4 2025-01-01 12:00:00 A 1/15")]
static void BiweeklyTask()
{
Console.WriteLine($"[Bi-weekly] {DateTime.Now}");
}
}
### 方法二:程式化新增排程
```csharp
using CJF.Schedules;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
public class Program
{
public static async Task Main(string[] args)
{
var host = Host.CreateDefaultBuilder(args)
.UseSchedulePlaner(opts => {
opts.Delay = 2; // 延遲 2 秒啟動
opts.Interval = 30; // 每 30 秒檢查一次
opts.AutoBind = false; // 不自動綁定 PlanAttribute
})
.Build();
// 取得 PlanWorker 服務
var planWorker = host.Services.GetRequiredService<PlanWorker>();
// 動態新增排程
planWorker.AppendPlan(new SchedulePlan(
"每 5 天執行",
"2 2025-01-01 03:00:00 5",
() => Console.WriteLine($"[Every 5 days] {DateTime.Now}")
));
planWorker.AppendPlan(new SchedulePlan(
"每週一三五",
"3 2025-01-01 08:00:00 1 1/3/5",
() => Console.WriteLine($"[Mon/Wed/Fri] {DateTime.Now}")
));
// 使用帶參數的動作
planWorker.AppendPlan(new SchedulePlan(
"每月第一週的週一",
"5 2025-01-01 10:00:00 A 1 1",
(plan) => Console.WriteLine($"[{plan.Name}] {DateTime.Now}")
));
planWorker.AppendPlan(new SchedulePlan(
"每月最後一週的週五",
new TimePlan(DateTime.Now, Months.All, WeekNo.Last, WeekDays.Friday),
(plan) => Console.WriteLine($"[{plan.Name}] {DateTime.Now}")
));
await host.RunAsync();
}
}
- Microsoft.Extensions.Hosting (9.0.8)
- .NET 8.0
歡迎提交 Pull Request 或回報 Issues:
本專案採用 MIT 許可證,詳見 LICENSE 檔案。
- 輕量級: 基於 .NET 的 BackgroundService,資源使用極少
- 彈性: 支援動態調整檢查間隔
- 可擴展: 易於擴展和客製化
- 執行緒安全: 各排程任務獨立執行,互不影響
- 欲使用自動綁定的排程方法必須是 靜態 (static) 方法
- 排程方法可以是
public
或private
- 支援兩種方法簽名:
Action
和Action<ISchedulePlan>
- 排程表達式中的日期時間使用當地時區
- 程式停止時會執行所有
PlanTypes.Stoped
排程
- 使用
CJF.Schedule.Test
專案作為測試參考 - 參考
PlanAttributeReflectionTests.cs
了解如何測試反射功能 - 查看測試專案的 README.md 獲取詳細的測試指南
- 新增
AddSchedulePlaner
方法,取代UseSchedulePlaner
- 增加
UseSchedulePlaner
的過時標記 - 處理排程執行中的取消例外狀況
- 支援 .NET 8.0
- 完整的排程類型支援
- 強化的屬性裝飾器功能
- 優化效能和穩定性
- 新增完整測試套件,包含94個通過測試
- 解決反射測試問題,提供隔離測試方案
- 支援 .NET 6.0
- 初始版本,首次發布
© 2025 Chen Jaofeng. All rights reserved.