【问题标题】:Emails being sent twice电子邮件被发送两次
【发布时间】:2014-03-26 19:09:02
【问题描述】:

我们在数据库中有一个电子邮件队列表。它包含主题、HTML 正文、地址、地址等。

Global.asax 中,每个间隔都会调用Process() 函数,该函数会发送一定数量的电子邮件。代码如下:

namespace v2.Email.Queue
{
    public class Settings
    {
        // How often process() should be called in seconds
        public const int PROCESS_BATCH_EVERY_SECONDS = 1;

        // How many emails should be sent in each batch.  Consult SES send rates.
        public const int EMAILS_PER_BATCH = 20;
    }

    public class Functions
    {
        private static Object QueueLock = new Object();

        /// <summary>
        /// Process the queue
        /// </summary>
        public static void Process()
        {
            lock (QueueLock)
            {
                using (var db = new MainContext())
                {
                    var emails = db.v2EmailQueues.OrderBy(c => c.ID).Take(Settings.EMAILS_PER_BATCH);
                    foreach (var email in emails)
                    {
                        var sent = Amazon.Emailer.SendEmail(email.FromAddress, email.ToAddress, email.Subject,
                                                                email.HTML);
                        if (sent)
                            db.ExecuteCommand("DELETE FROM v2EmailQueue WHERE ID = " + email.ID);
                        else
                            db.ExecuteCommand("UPDATE v2EmailQueue Set FailCount = FailCount + 1 WHERE ID = " + email.ID);
                    }
                }
            }
        }

问题是它时不时地发送一封电子邮件。

上面的代码有什么理由可以解释这种双重发送吗?

按照 Matthews 的建议进行小测试

const int testRecordID = 8296;
using (var db = new MainContext())
{
    context.Response.Write(db.tblLogs.SingleOrDefault(c => c.ID == testRecordID) == null ? "Not Found\n\n" : "Found\n\n");
    db.ExecuteCommand("DELETE FROM tblLogs WHERE ID = " + testRecordID);
    context.Response.Write(db.tblLogs.SingleOrDefault(c => c.ID == testRecordID) == null ? "Not Found\n\n" : "Found\n\n");
}
using (var db = new MainContext())
{
    context.Response.Write(db.tblLogs.SingleOrDefault(c => c.ID == testRecordID) == null ? "Not Found\n\n" : "Found\n\n");
}

有记录时返回:

找到

找到

找不到

如果我在删除 sql 查询后使用这个 method to clear the context cache,它会返回:

找到

找不到

找不到

但是仍然不确定这是否是问题的根本原因。我认为锁定肯定会停止双重发送。

【问题讨论】:

  • 我不熟悉 EF 如何缓存数据,但可能是上下文包含 EF 不知道您的原始 SQL 语句可能使缓存无效的缓存数据。此外,当队列中有电子邮件时,您的 Any 语句可能是负面优化。
  • 我认为问题是你process() 函数需要更多时间然后1sec 才能执行。导致第一次发送数据的情况在第二次条件检查中未更新。
  • @avi 这就是锁定的目的
  • @TomGullen:观看Amazon 非常有趣,我也在同一时间工作。除此之外,我能看到QueueLock 包含什么吗?
  • @Matthew 感谢您的关注,您说得对,负面优化已修正。 QueueLock 只是一个用于锁定静态方法的对象

标签: c# asp.net email locking queue


【解决方案1】:

您遇到的问题是由于实体框架执行其内部缓存的方式。

为了提高性能,Entity Framework 会缓存实体以避免数据库命中。

当您对DbSet 执行某些操作时,Entity Framework 将更新其缓存。

Entity Framework 不理解您的"DELETE FROM ... WHERE ..." 语句应该使缓存无效,因为 EF 不是 SQL 引擎(并且不知道您编写的语句的含义)。因此,要让 EF 完成其工作,您应该使用 EF 理解的 DbSet 方法。

for (var email in db.v2EmailQueues.OrderBy(c => c.ID).Take(Settings.EMAILS_PER_BATCH))
{
    // whatever your amazon code was...

    if (sent)
    {
        db.v2EmailQueues.Remove(email);
    }
    else
    {
        email.FailCount++;
    }
}

// this will update the database, and its internal cache.
db.SaveChanges(); 

附带说明,您应该尽可能地利用 ORM,这不仅可以节省调试时间,还可以让您的代码更易于理解。

【讨论】:

  • 很棒的解释和回答,非常感谢。已转换为 DbSet 方法。
猜你喜欢
  • 1970-01-01
  • 2015-02-26
  • 2014-06-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-02-01
相关资源
最近更新 更多