Một câu hỏi lặp đi lặp lại trên các diễn đàn ASP.NET thường là “Làm sao để tạo một task chạy theo lịch trong ứng dụng web?”. Ví dụ, yêu cầu là gửi mail hàng ngày sẽ chạy vào mỗi một giờ cố định trong ngày, nhưng thực tế sẽ có rất nhiều các công việc có thể cần được thực hiện khác. Theo đó, hàng tá các thành viên trên diễn đàn đưa ra các gợi ý như cài Windows Services hoặc chạy lệnh với Task Scheduler nhưng thực tế là hầu hết các chủ website đều không có quyền chạy các services này vì họ chỉ mua hosting thôi.
Tôi đã tìm kiếm được một giải pháp như kiểu kích hoạt task đó chạy trong sự kiện Session_Start của file Global.asax trước đây. Tuy nhiên nó có vấn đề là việc chạy task phụ thuộc vào lượt truy cập của ứng dụng của bạn. Và tất nhiên nó sẽ không phù hợp với các task quan trọng cần thực thi đúng thời điểm. Một giải pháp rất hay ở đây được tìm thấy là sử dụng Quartz.NET, một thư viện đặt lịch chạy tác vụ mã nguồn mở có sẵn qua nuget. Bài viết này sẽ hướng dẫn cách triển khai cơ bản để bạn có thể chạy được một tác vụ gửi mail tự động. Mình chọn WebForm để demo cách sử dụng Quartz.NET, nhưng với MVC và Web Pages thì cũng tương tự như vậy.
Cách dễ nhất là cài đặt Quartz.NET qua Nuget. Bạn có thể cài qua câu lệnh Install-Package Quartz trong cửa sổ Package Manager Console bằng cách click chuột phải vào project trong Visual Studio cửa sổ Solution Explorer và chọn Manage Nuget Packages.
Chú ý: Nếu sử dụng WebMatrix, chỉ cần click vào nút Nuget trong thanh ribbon và bỏ qua thông báo lỗi.
Sau đó tìm kiếm từ khóa ‘quartz’ và click nút Install khi bạn tìm thấy Quartz.NET
Đơn giản nhất, Quartz bao gồm 3 thành phần chính là 1 job, 1 trigger và 1 scheduler. Một job là một tác vụ được thực hiện. Trigger chỉ ra cách thức và thời gian job được thực thi. Và cuối cùng job và trigger được đăng ký với scheduler, thành phần này sẽ đảm bảo rằng job được thực thi theo thời gian cấu hình trong trigger.
Một job là một class. Để làm việc với Quartz, nó phải được triển khai từ interface của Quartz là Ijob có một phương thức là Execute. Phương thức này định nghĩa các logic được thực thi. Đây là một ví dụ của job:
using Quartz;
using System;
using System.Net;
using System.Net.Mail;
namespace ScheduledTaskExample.ScheduledTasks
{
public class EmailJob : IJob
{
public void Execute(IJobExecutionContext context)
{
using (var message = new MailMessage("user@gmail.com", "user@live.co.uk"))
{
message.Subject = "Test";
message.Body = "Test at " + DateTime.Now;
using (SmtpClient client = new SmtpClient
{
EnableSsl = true,
Host = "smtp.gmail.com",
Port = 587,
Credentials = new NetworkCredential("user@gmail.com", "password")
})
{
client.Send(message);
}
}
}
}
}
Phương thức Execute cần một đối tượng của interface IJobExecutionContext như một tham số. Scheduler sẽ truyền nó trong khi gọi phương thức Execute của job. Nó chứa các thông tin cấu hình về job. Trong ví dụ cơ bản này, không dùng gì đến các dữ liệu này của context nhưng có thể một số job chúng ta sẽ cần dùng đến. Tất cả ví dụ này nhằm mục đích gửi một email. Thân của phương thức chứa bất cứ cái gì mà bạn muốn làm. Nó có thể là truy vấn database và gửi mail cho các khách hàng…
Phần tiếp theo là demo về scheduler sẽ cài đặt và job với trigger sẽ được tạo và gán:
using Quartz;
using Quartz.Impl;
using System;
namespace ScheduledTaskExample.ScheduledTasks
{
public class JobScheduler
{
public static void Start()
{
IScheduler scheduler = StdSchedulerFactory.GetDefaultScheduler();
scheduler.Start();
IJobDetail job = JobBuilder.Create<EmailJob>().Build();
ITrigger trigger = TriggerBuilder.Create()
.WithDailyTimeIntervalSchedule
(s =>
s.WithIntervalInHours(24)
.OnEveryDay()
.StartingDailyAt(TimeOfDay.HourAndMinuteOfDay(0, 0))
)
.Build();
scheduler.ScheduleJob(job, trigger);
}
}
}
Tôi đã gọi class là JobScheduler, nhưng bạn có thể gọi bất cứ là gì. Trong thực tế, đoạn code không cần đặt trong class này mà đặt bất cứ đâu bạn muốn. Trong một ứng dụng web, nó sẽ là sự kiện Application_Start trong Global.asax. Trong ví dụ này, đoạn code thực tế sẽ đặt trong phương thức gọi là Start (Bạn có thể đặt tên là gì tùy thích). Một scheduler được tạo và khởi chạy trong 2 dòng. Sau đó một job được tạo sử dụng Quartz.NET trong phương thức JobBuilder.Create<T>, trong đó T là kiểu của job được tạo. Trong trường hợp này, nó là thể hiện của EmailJob được định nghĩa ở trên.
Tiếp theo, một trigger được khởi tạo. Như tôi đã nhắc đến ở trên, trigger định nghĩa khi nào job được thực thi. Trong trường hợp này, scheduler được chỉ định chạy hàng ngày vào nửa đêm và sẽ chạy cứ mỗi 24 giờ. Nó thực thi hàng ngày và các tùy chọn thực thi có thể linh hoạt và cho phép đặt lịch theo thời gian thực. Đây là ví dụ khác về trigger.
ITrigger trigger = TriggerBuilder.Create()
.WithIdentity("trigger1", "group1")
.StartNow()
.WithSimpleSchedule(x => x
.WithIntervalInSeconds(10)
.RepeatForever())
.Build();
Trigger được đặt một cái tên và nhóm nó có thể dễ dàng định danh nếu bạn cần tham chiếu trong lập trình. Nó được thiets kế để chạy t rực tiếp sau mỗi 10 giây cho đến khi kết thúc (hoặc bị dừng vì lý do nào đó). Fluent API giúp cho việc tạo trigger dễ nhìn và dễ hiểu hơn.
Cuối cùng, job và trigger được đăng ký với scheduler. Tất cả những gì cần thiết đã sẵn sàng để thực thi. Như đã nhắc đến ở trên, nơi tốt nhất để làm điều này là sự kiện Application_Start trong Global.asax, để gọi phương thức JobScheduler.Start được thêm vào bên cạnh các tác vụ khởi động khác:
using ScheduledTaskExample.ScheduledTasks;
using System;
using System.Web;
using System.Web.Optimization;
using System.Web.Routing;
namespace ScheduledTaskExample
{
public class Global : HttpApplication
{
void Application_Start(object sender, EventArgs e)
{
// Code that runs on application startup
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
JobScheduler.Start();
}
}
}
Nếu bạn cần chạy tác vụ theo lịch như một phần của ứng dụng ASP.NET với tính chính xác mà bạn lại không có quyền truy cập vào các hệ thống chạy tác vụ theo lịch trên server thì Quartz.NET là một giải pháp tuyệt vời.
Trích nguồn từ: (Dịch từ: www.mikesdotnetting.com)