我们在数据库中有一个电子邮件队列表。它包含主题,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);
}
}
}
}
问题在于,它偶尔会发送两次电子邮件。
上面的代码有什么理由可以解释这个双重发送吗?
按照马修斯的建议进行小测试
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,则返回:
实测值
未找到
未找到
但是仍然不确定它是否是问题的根本原因。我认为锁定肯定会阻止双重发送。
答案 0 :(得分:1)
您所拥有的问题是由于Entity Framework执行其内部缓存的方式。
为了提高性能,Entity Framework将缓存实体以避免数据库命中。
当您在DbSet上执行某些操作时,实体框架将更新其缓存。
实体框架不理解您的"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,不仅可以节省调试时间,还可以让您的代码更容易理解。