对于受SqlCommand.ExecuteNonQuery影响的行,EF等效

Pet*_*inl 5 .net entity-framework-4

在审批工作流程中,我希望确保提醒电子邮件只发送一次.

使用SqlCommand.ExecuteNonQuery,我可以通过测试返回值来确保这一点.使用EF的推荐解决方案是什么?根据文档ObjectContext.SaveChanges不返回等效值.

SqlCommand示例:(在SendMail失败的情况下,TransactionScope用于回滚数据库更新.)


Dim sql = "UPDATE LeaveApprovalRequests SET State = 'Reminded'" &
          " WHERE ID=3 AND State <>'Reminded'"
Using scope As New TransactionScope
    Using cnx As New SqlConnection(My.Settings.connectionString)
        cnx.Open()
        Dim cmd As New SqlCommand(sql, cnx)
        If 1 = cmd.ExecuteNonQuery Then
             SendMail()
        End If
        scope.Complete()
    End Using
End Using

通过启用乐观并发(在RowVersion属性上使用ConcurrencyMode = Fixed)并捕获OptimisticConcurrencyException,我能够识别对象是否实际在商店中更新.现在,TransactionScope(用于在SendMail失败时回滚数据库更新)会引发死锁错误.为什么?


Using scope As New TransactionScope
  Using ctx As New ApprovalEntities
    Try
      Dim approval = ctx.LeaveApprovalRequests.
        Where(Function(r) r.ID = 3 And r.State = "Created"
        ).FirstOrDefault
      If approval Is Nothing Then
        Console.WriteLine("not found")
        Exit Sub
      End If
      Threading.Thread.Sleep(4000)
      approval.State = "Reminded"
      ctx.SaveChanges()
      SendMail()
    Catch ex As OptimisticConcurrencyException
      Exit Try
    End Try
  End Using
  scope.Complete()
End Using

Pet*_*inl 0

根据与 Morteza 讨论的结果,我回答如下。

SaveChanges 返回它打算更新的对象数量,而不是它在存储中更新的对象数量。因此它必须与 OptimisticConcurrencyException 一起使用来确定更改是否成功。必须考虑到除了要更改的属性之外的其他属性也可能导致 OptimisticConcurrencyException。

读取实体并在同一 TransactionScope 中更新它会导致死锁。

对于我的任务“仅当我能够将状态从创建更改为提醒时才发送电子邮件”,我使用以下解决方案:

通过 1:1 关联将 ApprovalRequest 实体一分为二,在 OptimisticConcurrencyException 上退出,使用 SaveChanges 在 TransactionScope 中发送邮件。


ApprovalRequests
  ID (PK)
  RequestedBy
  ...
  RowVersion (ConcurrencyMode=Fixed)

ApprovalRequestStates
  ApprovalRequest_ID (PK, FK)
  State (ConcurrencyMode=Fixed)


Using ctx As New ApprovalEntities
    Dim approval = cxt.ApprovalRequests.Where ...
    Dim state = ctx.ApprovalRequestStates.
        Where(Function(r) r.ApprovalRequest_ID = approval.ID And r.State = "Created"
        ).FirstOrDefault()
    If state Is Nothing Then Exit Sub
    state.State = "Reminded"
    Threading.Thread.Sleep(3000) 
    Using scope As New TransactionScope
        Try
            ctx.SaveChanges()
            SendMail()
            scope.Complete()
        Catch ex As OptimisticConcurrencyException
            Exit Try
        End Try
    End Using
End Using

谨防!通过父实体引用子实体时更新子实体也会导致父实体的数据库更新 - 在这种情况下会抛出不需要的 OptimisticConcurrencyException。因此我没有使用:ApprovalRequests.ApprovalRequestStates.State =“提醒”