2020年5月18日 星期一

Quartz.NET 讓你的網站也可以開始自動排程

前言


目前工作的專案如果有排程的需求(ex. 每天寄信、產生報表之類的)

大部分都是寫一隻程式然後放進Windows排程去執行,

如果是要產生報表資料則會用Mysql的event去跑。

日前收到了一包.net core 的source code,發現裡頭用了 Pomelo.AspNetCore.TimedJob 這個

可以定期執行任務的類別庫,才知道原來有這類型工具可以用啊

所以也想在目前手上的mvc.net 專案裡,增加相同的功能。

servey了一下,發現蠻人多推薦Quartz.NET這個套件,

立馬下載來用,果然是好物!


安裝

直接從NuGet直接下載安裝吧









練習

主要需要實作3個相關的介面、類別

建立排程器:IScheduler、StdSchedulerFactory
建立工作:IJobDetail、IJob:
建立觸發器:ITrigger、TriggerBuilder


1. 首先建立工作的類別
[DisallowConcurrentExecutionAttribute]
public class Job_UpdateDeviceOnlineStatus : IJob
{
    public Task Execute(IJobExecutionContext context)
    {
        return Task.Run(()=> {
            //update device online status 
        });
    }
}
工作內容是去檢查遠端device是否連線,先不多述

加上[DisallowConcurrentExecutionAttribute]

可以避免類別instance兩次,代表同時間只執行一次工作


2. 修改Global.asax,讓程式啟動的時候去加入排程工作
IScheduler scheduler = null;

protected void Application_Start()
{
    ...前略
    StartSchedulerJon();
}

protected void Application_End(Object sender, EventArgs e)
{
    scheduler?.Shutdown();
}

private async void StartSchedulerJon()
{
    try
    {
        //建立排程器
        var factory = new Quartz.Impl.StdSchedulerFactory();
        scheduler = await factory.GetScheduler();

        //建立工作
        IJobDetail job = JobBuilder.Create<Job_UpdateDeviceOnlineStatus>()
            .WithIdentity("OnlineStatusUpdate")
            .Build();

        //建立觸發器
        ITrigger trigger = TriggerBuilder.Create()
            .WithSimpleSchedule(x => x
                .WithIntervalInMinutes(5)
                .RepeatForever())
            .WithIdentity("trigger_OnlineStatusUpdate")
            .StartNow()
            .Build();

        await scheduler.ScheduleJob(job, trigger);
        await scheduler.Start();

    }
    catch(Exception ex)
    {
        //log
        Console.Error.Write(ex.Message);
    }
}

trigger的時間有兩種表示方式,另一種是用CRON表示法

想將windows排程工作轉到.net應用程式裡,這種方式或許比較適合
ITrigger trigger2 = TriggerBuilder.Create()
    .WithCronSchedule("0 0/5 * * * * ") //每5分鐘跑一次
    .WithIdentity("trigger_use_cron")
    .StartNow()
    .Build();


3. 修改Web.config
一些排程的參數放在config檔裡(ex. 時間、enable),可以比較彈性的去調整配置
這次先不做~懶


4.IIS 8.0 設定
*IIS需安裝"應用程式初始化"
*修改應用程式集區的進階設定,將「啟動模式」從預設的「OnDemand」改為「AlwaysRunning」
*修改網站的進階設定,把「預先載入已啟用」(Preload Enabled)由預設的 False 改為 True。


後記

簡單的實作就可以有強力大功能,

也要繼續觀查長期執行下來對Server有沒有什麼其它影響

其它詳細的內容請參考官網文件說明



參考
Huanlin學習筆記
官網

2020年5月17日 星期日

Dynamic LinQ 讓LinQ更彈性好用的Extension

無意間發現的好工具來記錄一下。

當我們使用Linq做排序或where查詢時會用expression表示如下
var query = SampleList.Where(c=>c.online==true).OrderBy(c=>c.Name);

但當我們只能用欄位名稱的字串來操作時,則需要額外多寫一堆程式碼來實現
IEnumerable orderby(string condition, IEnumerable acc)
{
    if(condition=="online")
        return acc.OrderBy(c=>c.online);
    else if(condition == "name")
        return acc.OrderBy(c=>c.Name);
    .......
}
很冗長吧…
如果我table欄位有一堆不就寫不完了
Dynamic Linq直接幫我們解決這方面的問題,直接來看吧。

先從Nuget 下載吧


宣告一個class
public class Car
{
    public string Brand { set; get; }
    public string Color { set; get; }
    public int Count { set; get; }
}

sample code


Where 要用參數方式代入 (@0, @1...)
List cars = new List()
{
    new Car(){Brand="honda", Color="White", Count=5 },
    new Car(){Brand="toyota", Color="Red", Count=10 },
    new Car(){Brand="Benz", Color="Black", Count=3 }
};
            
Console.WriteLine("原始順序");
foreach(var c in cars)
    Console.WriteLine($"{c.Brand} {c.Color} {c.Count}");

Console.WriteLine("\r\n使用order count desc");
var query = cars.OrderBy("Count desc");//用descending也ok
foreach (var c in query)
    Console.WriteLine($"{c.Brand} {c.Color} {c.Count}");

Console.WriteLine("\r\n使用where color=white");
var query2 = cars.Where(" Color=@0", "White");
foreach (var c in query2)
    Console.WriteLine($"{c.Brand} {c.Color} {c.Count}");

result


很方便吧,整個大大簡省了coding的時間

但這方法無法在編譯時知道是否有錯誤發生,只能在runtime時期才知道程式有錯

所以要做好錯誤處理,就是記得要加上try...catch啦








參考
dotblog搞搞就懂

使用MVC Bundle後,CSS參考的相對路徑不對了??

MVC Bundle相當好用,可以把一堆css, js包在一起 bundles.Add(new StyleBundle("~/css/main").Include( "~/Content/Layout/main.css", ...