首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 程式設計綜合問答

ASP.NET 定時任務導出 Excel:為何 Global.asax Timer 不靠譜,正確架構是什麼?

     編輯:程式設計綜合問答

你的代碼核心問題

protected void Application_Start(...) {
    if (DateTime.Today.Day == 15 || DateTime.Today.Day == 30) { // 邏輯錯誤:Equals 比較的是 DateTime 物件非 Day
        Timer timer = new Timer();
        timer.Elapsed += MyTask;
        timer.Enabled = true;
    }
}

致命缺陷:

  1. IIS 應用程式池回收機制: 預設 20 分鐘無請求進入閒置 -> 停止工作進程 -> Timer 徹底消失。下次請求才重啟,但 Application_Start 只跑一次,若重啟當天不是 15/30 號,任務永遠不會註冊。
  2. 重疊部署/多實例: Web Farm 或多執行緒下,多個 Worker Process 同時跑 Timer -> 重複導出、檔案衝突。
  3. HttpContext 為 Null: 背景執行緒 (Elapsed 事件) 無 HttpContext.Current,你的 CreateExcel 使用 Response 直接拋異常。
  4. 邏輯判斷錯誤: DateTime.Today.Equals(15) 永遠為 false,應為 DateTime.Today.Day == 15。

正確架構選型對比

方案可靠性實作難度適用場景
Windows Service / 排程工作⭐⭐⭐⭐⭐中**生產環境標準首選**。獨立進程、不受 IIS 回收影響、可設定復原重啟、支援長時間任務。
Hangfire / Quartz.NET (進程內排程庫)⭐⭐⭐⭐低不想部署獨立服務。Hangfire 持久化到 DB,重啟不丟任務,有儀表板監控。需注意 Web App 閒置停止問題 (需設定「永遠啟動」+「預載」)。
Azure WebJobs / AWS Lambda / 雲端排程器⭐⭐⭐⭐⭐低雲端部署首選。Serverless、按執行付費、高可用、內建重試。
SQL Server Agent Job⭐⭐⭐⭐中邏輯極簡 (單純存過程導出檔案)。DBA 維護、極穩定。
Global.asax + Timer⭐低**僅適合本地測試、極低流量內網、容忍遺漏任務的玩具專案。**

推薦方案一:Windows Service (最穩健、傳統企業標配)

核心結構

public partial class ExcelExportService : ServiceBase {
    private Timer _timer;
    public ExcelExportService() { InitializeComponent(); }

    protected override void OnStart(string[] args) {
        _timer = new Timer(CheckAndExecute, null, TimeSpan.Zero, TimeSpan.FromHours(1)); // 每小時檢查一次
    }

    private void CheckAndExecute(object state) {
        // 邏輯:今天是 15 或 30 號 且 尚未執行過 (需持久化標記,如寫 DB/檔案/註冊表)
        if ((DateTime.Today.Day == 15 || DateTime.Today.Day == DateTime.DaysInMonth(DateTime.Today.Year, DateTime.Today.Month)) 
            && !AlreadyExecutedToday()) {
            
            ExecuteExport(); // 你的導出邏輯,注意:這裡沒有 HttpContext,改寫成檔案流/資料庫操作
            MarkExecutedToday();
        }
    }
    
    private void ExecuteExport() {
        DataTable dt = GetDataFromDB();
        // 使用 NPOI / Open XML / EPPlus 生成檔案到磁碟/網路磁碟/資料庫 Blob
        // File.WriteAllBytes(path, GenerateExcel(dt)); 
    }
}

推薦方案二:Hangfire (留在 ASP.NET 專案內,幾行代碼搞定)

1. NuGet 安裝

Install-Package Hangfire.Core
Install-Package Hangfire.SqlServer (或 PostgreSQL, MySQL 等)

2. Startup.cs / Global.asax 配置

// Global.asax.cs Application_Start
GlobalConfiguration.Configuration.UseSqlServerStorage("YourConnectionString");
app.UseHangfireDashboard("/hangfire"); // 選裝:監控頁面
app.UseHangfireServer();

// 註冊排程 (僅需註冊一次,重啟自動恢復)
RecurringJob.AddOrUpdate(() => ExportExcelJob(), Cron.Day(15, 30)); // 每月 15、30 號
// 或 Cron.Monthly(15) + 手動邏輯判斷月末

3. Job 實作 (無 HttpContext 依賴)

public void ExportExcelJob() {
    // 這裡直接操作 DB、檔案系統、雲端儲存
    var data = _dbContext.GetReportData();
    var bytes = _excelService.Generate(data);
    _blobStorage.Save($"reports/{DateTime.Today:yyyyMMdd}.xlsx", bytes);
    _notificationService.Send("報表已生成");
}

關鍵設定 (解決 IIS 閒置停止): IIS 應用程式池 -> 進階設定 -> 閒置逾時 = 0 (關閉) + 啟動模式 = AlwaysRunning。網站 -> 預載 = True。否則 Hangfire Server 會隨 Worker Process 睡著。

你的 CreateExcel 代碼重構要點 (移除 Response 依賴)

  • 將 Response.Write 改為 MemoryStream / FileStream / NPOI workbook.Write(stream)。
  • 移除 HttpUtility.UrlEncode、Response.Charset 等 Web 專屬代碼。
  • 檔案名編碼邏輯移至下載端 (Web API/Controller) 處理,背景任務只負責生成 bytes/存檔。

總結

**請立即放棄 Global.asax Timer 方案。** 它在生產環境中不可靠、不可控、難除錯。
- **有伺服器控制權** → 寫 **Windows Service** (最穩、最傳統、DBA/運維最愛)。
- **想留在專案裡、愛用儀表板** → 裝 **Hangfire** (設定好 IIS AlwaysRunning 即可)。
- **上雲了** → 直接用雲端原生排程 (WebJobs, Cloud Scheduler, EventBridge)。
核心原則:**背景任務不屬於 HTTP 請求週期,勿綁定 HttpContext。**

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved