【问题标题】:Entity Framework Concurrency insertion issue实体框架并发插入问题
【发布时间】:2015-10-21 11:34:20
【问题描述】:

我们正在开发一个 MVC 应用程序。在这个应用程序中,我们有一个支付模块。一旦用户开始定期订阅,应用程序将从贝宝收到两个支付完成的响应,具有相同的 TransactionId。 一种是通过“Success Url”,另一种是通过IPN 侦听器。 我们使用“交易”表来保存贝宝交易详情。 应用程序将检查数据库中是否存在“TransactionId”,同时从 Paypal 获得响应。所以最终结果是来自贝宝的第一个响应将插入到“交易”表中。

最近我们遇到了与实体框架工作并发相关的问题。如果两个响应并行出现,即使我们有检查 transactionid 是否存在的代码,两条记录都将插入到具有相同“trnasction id”的事务表中。 我们如何防止这种重复插入? 两次插入都是从不同的 CONTEXT 发生的。

       var ipnDetail = unitOfWork.TransactionDetailRepository.GetTransaction(transactionNumber);
            if (ipnDetail == null)
            {
}

我们对两次插入都使用相同的代码。唯一的区别是我们从不同的 EF 上下文中调用。

您还可以注意到第一个插入的条目比第二个插入的记录具有更长的时间。实际上我们是从代码中设置日期。 我们如何解决这个并发问题?

我们尝试使用“ObjectContext.Refresh”作为解决方案。但这对我们没有帮助。

((IObjectContextAdapter)context).ObjectContext.Refresh(System.Data.Objects.RefreshMode.StoreWins, ((IObjectContextAdapter)context).ObjectContext.ObjectStateManager.GetObjectStateEntries(EntityState.Added));

任何帮助都将不胜感激。请注意,应用程序处于生产环境中。

最好的问候,

润色

【问题讨论】:

  • 能否在后端的 transactionid 列上放置一个唯一索引?那会阻止它。
  • 谢谢。如果我在 sql 端设置“唯一索引”,我认为它可以防止重复的 transactionid。但问题是从前端我们必须知道 ipnDetails==null 与否,在此基础上我们可以执行其他操作。
  • 你的 id 列是自动递增的吗?因为这可以解释它......第一次调用成功,第二次将尝试插入 id 并并发失败。
  • 是的。“Id”列是自动增量字段。

标签: c# asp.net-mvc entity-framework


【解决方案1】:

如果您有 SQL Server 2014 或更高版本,merge 命令正是您所需要的。它允许您将 if 条件放在操作中的正确位置。

如果数据库中不存在新的 transactionId,则下面的示例将插入它。大多数替代方案涉及一个查询,然后是一个插入,留下一个窗口,另一个连接可以在您的插入完成之前潜入其中。

您可以在 Internet 上找到有关从实体框架调用存储过程的资源。

CREATE proc [dbo].[usp_InsertNewTransactionId](@transactionDate datetime2, @transactionId varchar(255))
as
begin
   ;with data as (select @transactionDate as transactionDate, @transactionId as transactionId)
   merge transactions t
   using data s
      on s.transactionId = t.transactionId
    when not matched by target
    then insert ([date],transactionId) values (s.transactionDate, s.transactionId);

end

【讨论】:

    【解决方案2】:

    将整个检查和插入逻辑封装在 TransactionScope 中。

    using (var scope = new TransactionScope(TransactionScopeOption.RequiresNew))
    {
        // read & write logic here     
    
        scope.Complete();        
    }
    

    RequiresNew 将导致使用新事务,它应该在数据库级别被阻止,因此您的另一个请求应该等待,直到第一个请求通过添加或不添加 Id 完成事务。

    【讨论】:

    • 请注意不同用户可以并行支付。此外,我们在 db 中有一个“id”主字段。我们认为该问题与上下文更新/刷新操作有关
    猜你喜欢
    • 2011-02-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多