Scheduled task with window service
Problem
Set up the some scheduled tasks running in the backgroud to take care of data update or sync for every 15 mins, or everyday or every week
Solution
Option 1
Windows Task Scheduler
- Click the Start button.
- Click Control Panel.
- Click System and Maintenance.
- Click Administrative Tools.
- Double-click Task Scheduler.
Option 2
Use Window Service as task scheduler
Overview of design
The design here is a simplified version, which I built for previous projects. In the real world, I need to tailor it for different project with different purpose, but fundamental design of architect is the same. IMO, the design below can support most cases, which needs scheduled backgroud task service.
+----------------------------------+ Register as
| System.ServiceProces ----- |--------o Window Service
+----------------------------------+
| +----------------------------+ |
| | Thread (Infinite) | |
| +----------------------------+ |
| | +----------------------+ | |
| | | MySchedulerService | | |
| | +----------------------+ | |
| | | while ( true ) | | |
| | | { | | |
| | | Task.Process() | | |
| | | } | | |
| | +----------------------+ | |
| +----------------------------+ |
+----------------------------------+
+------------+
| ITask |
+------------+
| Process() |
+------------+
/|\
|
+------------------+
| BaseTask | -----------o Customized Task inherit BaseTask
+------------------+
| lastProcessTime | ------------o Last process time
| intervalTime | ------------o Customize for next process time
| IsReadyProcess() | ------------o Check taks is ready to process
+------------------+
Create customized Window Service
Use ServiceProcess to create Window Service
- The System.ServiceProcess allow you to implement, install, and control Windows service applications. Services are long-running executables that run without a user interface.
- The project must have main method the entry point
namespace MyScheduler
{
using System.ServiceProcess;
class Program
{
static void Main(string[] args)
{
#if DEBUG
MySchedulerService debugService = new MySchedulerService();
debugService.onDebug();
System.Threading.Thread.Sleep(System.Threading.Timeout.Infinite);
#else
ServiceBase.Run(new ServiceBase[] { new MySchedulerService() });
#endif
}
}
}
Create customized ServiceBase
- Implementing a service involves inheriting from the ServiceBase class and defining specific behavior to process when start, stop, pause, and continue commands are passed in, as well as custom behavior and actions to take when the system shuts down.
namespace MyScheduler
{
using System;
using System.IO;
using System.Collections.Generic;
using System.Threading;
public partial class MySchedulerService : System.ServiceProcess.ServiceBase
{
public const string START_SERVICE = "start.service";
public const string STOP_SERVICE = "stop.service";
public void onDebug()
{
OnStart(null);
}
protected override void OnStart(string[] args)
{
System.IO.File.Create(AppDomain.CurrentDomain.BaseDirectory
+ TART_SERVICE);
ThreadStart tsTask = new ThreadStart(TaskLoop);
Thread rtkTask = new Thread(tsTask);
rtkTask.Start();
}
static void TaskLoop()
{
while (true)
{
// Exceute scheduled task
ScheduledTask();
// Then, wait for certain time interval
System.Threading.Thread.Sleep(TimeSpan.FromMilliseconds(500));
}
}
static void ScheduledTask()
{
// Task code which is executed periodically
try
{
// Call customized tasks
var types = Assembly.GetExecutingAssembly()
.GetExportedTypes()
.Where(p => typeof(ITask)
.IsAssignableFrom(p.BaseType));
foreach (var t in types)
{
var task = (ITask)Activator.CreateInstance(t);
if (taskSettings.Keys.Contains(t.Name))
{
task.TaskSetting = taskSettings[t.Name];
}
tasks.Add(task);
}
}
catch (Exception e)
{
// TO Something here
}
}
protected override void OnStop()
{
// Insert code here to close all the open IO or conection.
System.IO.File.Create(AppDomain.CurrentDomain.BaseDirectory
+ STOP_SERVICE);
}
private void InitializeComponent()
{
this.ServiceName = "MySchedulerService";
}
protected override void Dispose(bool disposing)
{
OnStop();
base.Dispose(disposing);
}
}
}
Task interface and class
- interface ITask has only one method
namespace MyScheduler
{
public interface ITask
{
void Process();
}
}
- BaseTask
namespace MyScheduler
{
using System;
public class BaseTask : ITask
{
protected DateTime? lastProcessTime = null;
// interval time uses second as unit
// e.g. 1 min of intervaling time is 60
protected int intervalTime = 0 ;
protected bool IsReadyToProcess()
{
bool isReadyToProcess = true;
if (lastProcessTime.HasValue)
{
if (lastProcessTime.Value.AddSeconds(intervalTime) > DateTime.Now)
{
isReadyToProcess = false;
}
}
return isReadyToProcess;
}
public virtual void Process()
{
throw new NotImplementedException();
}
}
}
- Sample Task1 and Task2
Task1
namespace MyScheduler
{
using System;
using System.Linq;
using System.Data.Entity;
public class Task1 : BaseTask
{
public override void Process()
{
if (base.IsReadyToProcess())
{
System.IO.File.Create(AppDomain.CurrentDomain.BaseDirectory
+ "Task-1-" + DateTime.Now.ToString("dd-MM-yyyy"));
}
}
}
}
Task2
namespace MyScheduler
{
using System;
using System.Linq;
using System.Data.Entity;
public class Task2 : BaseTask
{
public override void Process()
{
if (base.IsReadyToProcess())
{
System.IO.File.Create(AppDomain.CurrentDomain.BaseDirectory
+ "Task-2-"
+ DateTime.Now.ToString("dd-MM-yyyy"));
}
}
}
}
Create Window Service installer
- In Solution Explorer, double-click MyScheduledService.cs.
- In the Code Editor window, right-click
Design View
, and then clickProperties
- In the
Properties
pane, click theAdd Installer
link. - In the Properties pane for MyScheduledServiceInstaller, change the
ServiceNameproperty
toMyScheduledService
. - In the Code Editor window in
Design view
, clickMyScheduledServiceProcessInstaller
. - In the Properties pane, change the
Account
property toLocalSystem
(TheLocalService
andNetworkService
values are available only in Microsoft Windows XP).
PreInstaller
- It inherits from System.Configuration.Install.Installer. This is the base class for all custom installers in the .NET Framework. Installers are components that help install applications on a computer.
namespace MyScheduler
{
[RunInstaller(true)]
public partial class ProjectInstaller : System.Configuration.Install.Installer
{
public ProjectInstaller()
{
InitializeComponent();
}
private void MySchedulerServiceInstaller_AfterInstall(
object sender, InstallEventArgs e)
{
}
}
}
Install Window Service
C:\Windows\Microsoft.Net\Framework\v4.0.30319\InstallUtil.exe /i <app_path>\MyScheduler.exe
Uninstall Window Service
C:\Windows\Microsoft.Net\Framework\v4.0.30319\InstallUtil.exe /u <app_path>\MyScheduler.exe