WCF服务 - 队列&批量邮件(通过SMTP发送电子邮件)

时间:2014-09-25 10:11:10

标签: c# asp.net asp.net-mvc wcf email

我有一个通过命名管道工作的WCF服务,它从ASP.NET MVC应用程序接收数据:

[ServiceContract]
public interface IEmailProcessor
{
    [OperationContract(AsyncPattern = true)]
    void SendEmails(string subject, string body, string from, string to);
}

服务获取电子邮件的主题,正文,发件人和收件人。然后它将电子邮件放在一起并通过SMTP发送。

客户端(MVC应用程序)一次发送几封电子邮件,所以我希望服务本身异步工作(客户端只调用SendEmails方法几次,然后WCF服务器负责其余的工作)。但由于新电子邮件地址的灰名单,我的电子邮件服务器有时会拒绝发送电子邮件。这就是为什么我希望WCF服务器也将这些电子邮件排队并尝试逐个发送它们(如果发生错误,请重试几次)。

我已经阅读了有关WCF async和MSMQ方法的几个主题,但在我的情况下,最好的方法是什么?我应该创建两个WCF服务(客户端 - 服务器和服务器 - 服务器)吗?或者也许使用多线程?对我来说,不使用任何内置的SMTP解决方案也很重要,因为我想扩展我的服务以处理其他消息,而不仅仅是电子邮件。

2 个答案:

答案 0 :(得分:1)

对于完全相同的要求,我已为自己制作了整个电子邮件解决方案,但我没有使用MSMQ队列。

以下是步骤

  1. 创建异步WCF Web服务
  2. 使用任务等待异步技术,使用task.run(()=> AsyncSendEmail)在服务中运行发送电子邮件线程
  3. 跳过try catch上的所有smptpexception并运行sp来更新表字段 isEmailSent = false 如果有错误
  4. 通过客户端" yourwebapplication"订阅web服务。
  5. 通过执行task.factory.startNew(()=> proxy.sendEmail(paramters))来调用服务。
  6. 创建一个简单的Windows任务计划程序任务,以调用在预定时间段内运行服务以重试发送电子邮件Simple Window Task Schedular

  7. 要读取未送达,您的交换帐户中的电子邮件失败我使用第三方服务组件Admin System Software并更新表字段isDelivered = No,ErrorDescription = errorDesc

  8. 希望这个解决方案可以帮助你......

    Shaz

答案 1 :(得分:0)

我遵循了zaitsman的建议,并使用ConcurrentQueue在服务中创建了队列。我还使用了创建新线程的示例:http://msdn.microsoft.com/en-us/library/7a2f3ay4%28v=vs.80%29.aspx

我在我的WCF服务器上添加了新的线程:

    var thread = new Thread(EmailThread.Instace.DoWork);
    thread.Start();

然后我的服务中的SendEmails方法使用Enqueue将msg添加到ConcurrentQueue

    public void SendEmails(string from, string to, string subject, string body)
    {
        var msg = new MailMessage(from, to, subject, body)
        {
            IsBodyHtml = true,
            BodyEncoding = Encoding.UTF8,
            DeliveryNotificationOptions = DeliveryNotificationOptions.OnFailure
        };

        EmailThread.Instace.Messages.Enqueue(msg);
    }

这是EmailThread类,它正在后台工作(即接受新的电子邮件到队列并顺序出列。

internal class EmailThread
{
    //Ensure that only one instance of EmailThread can exist
    private static readonly EmailThread Instance = new EmailThread();
    public static EmailThread Instace { get { return Instance; } }

    //Here is our Queue
    public ConcurrentQueue<MailMessage> Messages { get; private set; }

    private EmailThread()
    {
        Messages = new ConcurrentQueue<MailMessage>();
    }

    // This method is called when the thread is started and repeated once a 10 seconds
    public void DoWork()
    {
        while (!_shouldStop)
        {
            Console.WriteLine("email sender thread: working...");

            if (!Messages.IsEmpty)
            {
                //define SMTP credentials here
                var smtp = new SmtpClient()

                var failed = new Queue<MailMessage>();

                MailMessage message;
                while (Messages.TryDequeue(out message))
                {
                    try
                    {
                        smtp.Send(message);
                        Console.WriteLine("email sender thread: successfully sent email...");
                    }
                    catch (SmtpException)
                    {
                        //Enqueue again if failed
                        failed.Enqueue(message);
                        Console.WriteLine("email sender thread: error sending email, enqueuing...");
                    }
                }

                foreach (var mailMessage in failed)
                {
                    Messages.Enqueue(mailMessage);
                }

                smtp.Dispose();
            }
            Thread.Sleep(10000);
        }
        Console.WriteLine("email sender thread: terminating gracefully.");
    }
    public void RequestStop()
    {
        _shouldStop = true;
    }
    // Volatile is used as hint to the compiler that this data
    // member will be accessed by multiple threads.
    private volatile bool _shouldStop;

}