尽最大努力避免并发问题,即使在实现this锁定方式后仍然会弹出并发错误。
支付提供商有时会非常快速地发送更新(有时甚至会在一秒内发送3次),这使得应用程序很难赶上,因为我们想要做更多的操作。 然后,用户也从支付提供商返回,执行相同的代码。最后,我们有多个请求来自不同的会话,这段代码应该确保一次只能有一个更新。
我的代码:
public InvoiceModel UpdateInvoicePaymentStatus(PaymentModel payment)
{
InvoiceModel invoice = _invoiceRepository.GetByMspId(payment.ExternalOrderId);
//lock per invoice Id, so we can have multiple different invoice updates, but single updates for one invoice
object miniLockInvoice = MiniUpdateLock.GetOrAdd(invoice.InvoiceId, new object());
LoggingManagement.AddOwnErrorObjects("outside lock, for id:" + invoice.InvoiceId, Log.LogLevel.INFO,
miniLockInvoice);
lock (miniLockInvoice)
{
Log.AddOwnErrorObjects("inside lock, for id:" + invoice.InvoiceId, Log.LogLevel.INFO,
miniLockInvoice);
invoice = _invoiceRepository.GetByIdWithCrudRelations(invoice.InvoiceId);//get again inside lock
//do a lot of logic, send out emails (we don't want to send multiple of course)
Log.AddOwnErrorObjects("UpdateInvoicePaymentStatus", Log.LogLevel.INFO, invoice, payment);
base.Update(invoice.InvoiceId, invoice);
object temp;
if (MiniUpdateLock.TryGetValue(invoice.InvoiceId, out temp) && temp == miniLockInvoice)
MiniUpdateLock.TryRemove(invoice.InvoiceId, out temp);
Log.AddOwnErrorObjects("inside end of lock, for id:" + invoice.InvoiceId + " removed item", Log.LogLevel.INFO,
temp);
}
Log.AddOwnErrorObjects("outside end of lock, for id:" + invoice.InvoiceId, Log.LogLevel.INFO);
return invoice;
}
现在在存储库中,最终执行以下代码(通用repo)。现在请记住,正在检索的实体具有timestamp
变量以避免并发更新。在ModelToEntity
内,时间戳不会更改或更改。
public virtual void Update(int id, TModel model)
{
TEntity entity = GetEntityById(id);
entity = ModelToEntity(model, entity);
DbContext.Entry(entity).State = EntityState.Modified;
DbContext.SaveChanges();
}
因为你可以看到我在锁内外放了很多日志来追踪发生的事情。
+-----------------------------------------+-----------+----------+
| Event | session1 | session2 |
+-----------------------------------------+-----------+----------+
| outside lock, for id | 3:55:54 | 3:55:55 |
| inside lock, for id: | 3:55:54 | 3:56:00 |
| UpdateInvoicePaymentStatus | 3:56:00 | 3:56:08 |
| inside end of lock, for id: remove item | exception | 3:56:09 |
| outside end of lock, for id: | exception | 3:56:09 |
+-----------------------------------------+-----------+----------+
为了达到这个目的,我可能会更好地记录毫秒,但是session2
在session1
传递UpdateInvoicePaymentStatus
事件之后进入锁定但是在抛出异常之前,也发生在3上:56:00。所以在调用更新数据库方法之前。
锁定无法正常工作或其他内容正在进行我不理解的事情,异常跟踪显示在从此代码块(或路径)中更新实体时发生了这种情况。我在这两个会议之前跟踪了两个会话,他们很好地在另一个更高级别上锁定了对方。那个锁只是一个静态对象。