我有一个ASP.NET MVC项目,通过SagePay进行付款(使用Jeremy Skinnners SagePayMVC解决方案)。解决方案还使用Entity Framework和UnitOfWork模式。
SagePay付款完成后,SagePay会回复通知网址。 这是我的SagePay控制器上的一个动作,看起来像这样:
[HttpPost]
public virtual ActionResult Notify(SagePayResponse response)
{
using (UnitOfWork unitOfWork = new UnitOfWork())
{
if (ResponseLegit(response,unitOfWork))
{
SavePaymentToDatabase(response,unitOfWork);
}
unitOfWork.SaveChanges();
}
}
总的来说,这似乎有效 - 但我们偶尔会在数据库中多次付款。似乎如果服务器特别繁忙,可能需要稍长时间才能响应,因此SagePay会发出第二个通知(有时甚至是几个)。
因此,我首次尝试阻止这种情况发生,就是添加支票,查看是否已存在与付款交易编号相匹配的付款。
Order order =GetRelatedOrderFromSagePayResponse(unitOfWork,response);
bool paymentAlreadyExists = PaymentExistsForOrder(order, response);
if (!paymentAlreadyExists)
{
SavePaymentToDatabase(response,unitOfWork);
}
然而,这并没有奏效。多笔付款会不时出现。
所以我认为正在发生的事情是服务器忙于其他一些未知任务,来自SagePay的通知开始排队,然后IIS赶上并将线程交给每个请求 - 这样它们就可以并行处理。因此,检查被否定,因为实体框架同时从数据库填充了对象,并且在检查它们时没有付款。
所以我认为解决方案是使用此方法中的Lock方法: how to lock an asp.net mvc action?
private static object NotifyLock= new object();
[HttpPost]
public virtual ActionResult Notify(SagePayResponse response)
{
lock (NotifyLock)
{
using (UnitOfWork unitOfWork = new UnitOfWork())
{
if (ResponseLegit(response,unitOfWork))
{
Order order=GetRelatedOrderFromSagePayResponse(unitOfWork,response);
bool paymentAlreadyExists = PaymentExistsForOrder(order, response);
if (!paymentAlreadyExists)
{
SavePaymentToDatabase(response,unitOfWork);
}
}
unitOfWork.SaveChanges();
}
}
}
这是否是Lock的合法用法,是否会同时停止多个通知进入通知操作,从而允许我的多笔付款检查工作?