我将Entity Framework 6.0和SQL Server 2016用于我的ASP.Net网站。我最近发现我的一个功能的并发问题。该功能用于处理未付订单,有时该功能针对同一键和同一时间执行多次(因为多个用户可以同时访问它)。
这是它的样子。
public void PaidOrder(string paymentCode)
{
using (MyEntities db = new MyEntities())
{
using (DbContextTransaction trans = db.Database.BeginTransaction())
{
try
{
Order_Payment_Code payment = db.Order_Payment_Code.Where(item => item.PaymentCode == paymentCode).FirstOrDefault();
if(payment.Status == PaymentStatus.NotPaid)
{
//This Scope can be executed multiple times
payment.Status = PaymentStatus.Paid;
db.Entry(payment).State = EntityState.Modified;
db.SaveChanges();
//Continue processing Order
trans.Commit();
}
}
catch (Exception ex)
{
trans.Rollback();
}
}
}
}
我不明白的是,为什么if
语句中的范围即使在事务内部也可以多次执行?事务不是要隔离数据吗?还是我对交易的理解是错误的?如果是这样,那么使我的if
语句中的作用域仅执行一次的正确方法是什么?
答案 0 :(得分:1)
序列化EF SQL Server事务的一种简单可靠的方法是使用Application Lock。
将此方法添加到您的DbContext中:
public void GetAppLock(string lockName)
{
var sql = "exec sp_getapplock @lockName, 'exclusive';";
var pLockName = new SqlParameter("@lockName", SqlDbType.NVarChar, 255);
pLockName.Value = lockName;
this.Database.ExecuteSqlCommand(sql, pLockName);
}
在开始交易后立即调用它。
public void PaidOrder(string paymentCode)
{
using (MyEntities db = new MyEntities())
{
using (DbContextTransaction trans = db.Database.BeginTransaction())
{
db.GetAppLock("PaidOrder");
Order_Payment_Code payment = db.Order_Payment_Code.Where(item => item.PaymentCode == paymentCode).FirstOrDefault();
if(payment.Status == PaymentStatus.NotPaid)
{
//This Scope can be executed multiple times
payment.Status = PaymentStatus.Paid;
db.Entry(payment).State = EntityState.Modified;
db.SaveChanges();
//Continue processing Order
}
trans.Commit();
}
}
}
那么即使您有多个前端服务器,一次也只能运行该事务的一个实例。因此,这就像互斥量,可在访问同一数据库的所有客户端中使用。