C#控制台应用程序在预定时间发送电子邮件 [英] C# console app to send email at scheduled times

查看:200
本文介绍了C#控制台应用程序在预定时间发送电子邮件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经得到了Windows Server 2003中,其目的是阅读的表中调用声明和一个名为NotifyDateTime和现场到达该时间时发送电子邮件上运行的C#控制台应用程序。我把它通过任务计划安排每小时运行,检查,看看是否NotifyDateTime属于这一小时内,然后发送通知。

I've got a C# console app running on Windows Server 2003 whose purpose is to read a table called Notifications and a field called "NotifyDateTime" and send an email when that time is reached. I have it scheduled via Task Scheduler to run hourly, check to see if the NotifyDateTime falls within that hour, and then send the notifications.

这似乎是因为我有通知日期/数据库中的时候,应该有比重新运行这个东西每隔一小时一个更好的办法。

It seems like because I have the notification date/times in the database that there should be a better way than re-running this thing every hour.

有一个轻量级进程/控制台应用程序,我可以离开运行从表和问题,他们什么时候他们是因为在当天的通知,读取服务器上?

Is there a lightweight process/console app I could leave running on the server that reads in the day's notifications from the table and issues them exactly when they're due?

我想服务,但似乎矫枉过正。

I thought service, but that seems overkill.

推荐答案

我的建议是写简单的应用程序,它使用石英。 NET

My suggestion is to write simple application, which uses Quartz.NET.

创建2个作业:


  • 首先,大火每天一次,读取所有等待数据库计划于当天通知时间,创建一个基于他们一些触发器。

  • 其次,对于这样的触发器(由第一份工作准备)注册,发送通知。

更重要的是,

我强烈建议你这样的目的,创建窗口服务,只需不要有孤独的控制台应用程序运行不断。它可以通过人谁具有相同的帐户下对服务器的访问被意外终止。更重要的是,如果该服务器将重新启动,你必须记住要手动打开一次这样的应用,而服务可以配置为自动启动。

I strongly advice you to create windows service for such purpose, just not to have lonely console application constantly running. It can be accidentally terminated by someone who have access to the server under the same account. What's more, if the server will be restarted, you have to remember to turn such application on again, manually, while the service can be configured to start automatically.

如果您正在使用的Web应用程序,您可以总是有这种逻辑例如托管IIS应用程序池的过程中,虽然它是坏主意任何责任。这是因为这样的方法是在默认情况下重新启动定期,所以你应该更改其默认配置,以确保它仍然是在半夜,当不使用应用程序的工作。除非你计划的任务将被终止

If you're using web application you can always have this logic hosted e.g. within IIS Application Pool process, although it is bad idea whatsoever. It's because such process is by default periodically restarted, so you should change its default configuration to be sure it is still working in the middle of the night, when application is not used. Unless your scheduled tasks will be terminated.

更新(代码示例):

Manager类,调度和unscheduling工作的内在逻辑。出于安全原因,实现为一个单:

Manager class, internal logic for scheduling and unscheduling jobs. For safety reasons implemented as a singleton:

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);
    }
}



第一份工作,为收集从数据库和调度通知所要求的信息(第二职业):

First job, for gathering required information from the database and scheduling notifications (second job):

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 */ }           
    }
}

第二职业,用于发送电子邮件:

Second job, for sending emails:

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);
    }
}



数据库模拟,只是对于该特定实例的目的:

Database mock, just for purposes of this particular example:

internal class DbMock
{
    public static IDictionary<string, string> ScheduleMap = 
        new Dictionary<string, string>
        {
            {"00:01", "foo@gmail.com"},
            {"00:02", "bar@yahoo.com"}
        };
}

应用程序的主项:

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);
    }
}



输出:

Output:

如果你要使用服务,只是把这个主要逻辑到的OnStart 办法(我的意见开始实际的逻辑在一个单独的棉线不等待服务启动,并且在同一避免可能超时 - 未在这个特定的例子很明显,但在一般的):

If you're going to use service, just put this main logic to the OnStart method (I advice to start the actual logic in a separate thread not to wait for the service to start, and the same avoid possible timeouts - not in this particular example obviously, but in general):

protected override void OnStart(string[] args)
{
    try
    {
        var thread = new Thread(x => WatchThread(new ThreadStart(FireStarter.Execute)));
        thread.Start();
    }
    catch (Exception e) { /* log error */ }            
}

如果这样,封装逻辑在一些包装例如WatchThread这将捕捉任何错误,从螺纹:

If so, encapsulate the logic in some wrapper e.g. WatchThread which will catch any errors from the thread:

private void WatchThread(object pointer)
{
    try
    {
        ((Delegate) pointer).DynamicInvoke();
    }
    catch (Exception e) { /* log error and stop service */ }
}

这篇关于C#控制台应用程序在预定时间发送电子邮件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆