Cav*_*rob 15 c# scheduled-tasks console-application
我有一个在Windows Server 2003上运行的C#控制台应用程序,其目的是读取一个名为Notifications的表和一个名为"NotifyDateTime"的字段,并在达到该时间时发送一封电子邮件.我通过任务计划程序安排它按小时运行,检查NotifyDateTime是否在该小时内,然后发送通知.
这似乎是因为我在数据库中有通知日期/时间应该有一个比每小时重新运行这个东西更好的方法.
是否有一个轻量级的进程/控制台应用程序,我可以在服务器上运行,从表中读取当天的通知,并在它们到期时准确发布它们?
我认为服务,但这似乎有点矫枉过正.
jwa*_*zko 26
我的建议是编写简单的应用程序,它使用Quartz.NET.
创造2个职位:
更重要的是,
我强烈建议你为此目的创建Windows服务,只是不要让寂寞的控制台应用程序不断运行.在同一帐户下有权访问服务器的人可能会意外终止它.更重要的是,如果服务器将重新启动,您必须记住手动重新打开此类应用程序,同时可以将服务配置为自动启动.
如果您正在使用Web应用程序,则可以始终在IIS应用程序池进程中托管此逻辑,尽管这是一个坏主意.这是因为默认情况下会定期重新启动此类进程,因此您应该更改其默认配置,以确保在未使用应用程序时它仍在半夜工作.除非您的计划任务将被终止.
更新(代码示例):
Manager类,用于调度和取消调度作业的内部逻辑.出于安全原因实施为单身人士:
internal class ScheduleManager
{
private static readonly ScheduleManager _instance = new ScheduleManager();
private readonly IScheduler _scheduler;
private ScheduleManager()
{
var properties = new NameValueCollection();
properties["quartz.scheduler.instanceName"] = "notifier";
properties["quartz.threadPool.type"] = "Quartz.Simpl.SimpleThreadPool, Quartz";
properties["quartz.threadPool.threadCount"] = "5";
properties["quartz.threadPool.threadPriority"] = "Normal";
var sf = new StdSchedulerFactory(properties);
_scheduler = sf.GetScheduler();
_scheduler.Start();
}
public static ScheduleManager Instance
{
get { return _instance; }
}
public void Schedule(IJobDetail job, ITrigger trigger)
{
_scheduler.ScheduleJob(job, trigger);
}
public void Unschedule(TriggerKey key)
{
_scheduler.UnscheduleJob(key);
}
}
Run Code Online (Sandbox Code Playgroud)
第一项工作,用于从数据库收集所需信息和安排通知(第二项工作):
internal class Setup : IJob
{
public void Execute(IJobExecutionContext context)
{
try
{
foreach (var kvp in DbMock.ScheduleMap)
{
var email = kvp.Value;
var notify = new JobDetailImpl(email, "emailgroup", typeof(Notify))
{
JobDataMap = new JobDataMap {{"email", email}}
};
var time = new DateTimeOffset(DateTime.Parse(kvp.Key).ToUniversalTime());
var trigger = new SimpleTriggerImpl(email, "emailtriggergroup", time);
ScheduleManager.Instance.Schedule(notify, trigger);
}
Console.WriteLine("{0}: all jobs scheduled for today", DateTime.Now);
}
catch (Exception e) { /* log error */ }
}
}
Run Code Online (Sandbox Code Playgroud)
第二份工作,用于发送电子邮件:
internal class Notify: IJob
{
public void Execute(IJobExecutionContext context)
{
try
{
var email = context.MergedJobDataMap.GetString("email");
SendEmail(email);
ScheduleManager.Instance.Unschedule(new TriggerKey(email));
}
catch (Exception e) { /* log error */ }
}
private void SendEmail(string email)
{
Console.WriteLine("{0}: sending email to {1}...", DateTime.Now, email);
}
}
Run Code Online (Sandbox Code Playgroud)
数据库模拟,仅用于此特定示例的目的:
internal class DbMock
{
public static IDictionary<string, string> ScheduleMap =
new Dictionary<string, string>
{
{"00:01", "foo@gmail.com"},
{"00:02", "bar@yahoo.com"}
};
}
Run Code Online (Sandbox Code Playgroud)
申请主要条目:
public class Program
{
public static void Main()
{
FireStarter.Execute();
}
}
public class FireStarter
{
public static void Execute()
{
var setup = new JobDetailImpl("setup", "setupgroup", typeof(Setup));
var midnight = new CronTriggerImpl("setuptrigger", "setuptriggergroup",
"setup", "setupgroup",
DateTime.UtcNow, null, "0 0 0 * * ?");
ScheduleManager.Instance.Schedule(setup, midnight);
}
}
Run Code Online (Sandbox Code Playgroud)
输出:

如果您要使用服务,只需将此主逻辑放入OnStart方法(我建议在单独的线程中启动实际逻辑而不是等待服务启动,同样避免可能的超时 - 不在此特定示例中显然,但总的来说):
protected override void OnStart(string[] args)
{
try
{
var thread = new Thread(x => WatchThread(new ThreadStart(FireStarter.Execute)));
thread.Start();
}
catch (Exception e) { /* log error */ }
}
Run Code Online (Sandbox Code Playgroud)
如果是这样,将逻辑封装在一些包装器中,例如WatchThread,它将捕获来自线程的任何错误:
private void WatchThread(object pointer)
{
try
{
((Delegate) pointer).DynamicInvoke();
}
catch (Exception e) { /* log error and stop service */ }
}
Run Code Online (Sandbox Code Playgroud)
计划任务可以安排在特定时间仅运行一次(而不是每小时、每天等),因此一种选择是在数据库中的特定字段发生更改时创建计划任务。
您没有提及您使用哪个数据库,但有些数据库支持触发器的概念,例如在 SQL 中:http ://technet.microsoft.com/en-us/library/ms189799.aspx
| 归档时间: |
|
| 查看次数: |
17440 次 |
| 最近记录: |