gpt4 book ai didi

.net - EF 等效于 SqlCommand.ExecuteNonQuery 影响的行

转载 作者:行者123 更新时间:2023-12-04 21:25:40 26 4
gpt4 key购买 nike

在审批工作流中,我想确保提醒电子邮件只发送一次。

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

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

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

最佳答案

嗯,事实上,可以从对 ObjectContext.SaveChanges() 的调用推断出受影响的确切行数。

如果你看看 ObjectContext.SaveChanges documentation 你会看到的:

public int SaveChanges()
  • 返回值:调用 SaveChanges 时处于已添加、已修改或已删除状态的对象数。
  • “SaveChanges 在事务中运行。如果任何脏 ObjectStateEntry 对象无法持久化,SaveChanges 将回滚该事务并抛出异常。”

  • (1) 和 (2) 基本上意味着如果您对 SaveChanges() 的调用已成功完成并且您没有收到任何异常,则 EF 保证返回值准确反射(reflect)已修改的对象数。

    因此,您需要做的就是:
    try {
    // Try to save changes, which may cause a conflict.
    int num = context.SaveChanges();
    SendMail();
    }
    catch (OptimisticConcurrencyException) {
    //Refresh the entity, using ClientWins
    context.Refresh(RefreshMode.ClientWins, yourEntityObject);
    //SaveChanges again;
    context.SaveChanges();
    }
    调用 Refresh with ClientWins 时,它执行查询以检索当前
    该实体在数据库中的值,包括新的时间戳。因此,所有原始字段值都已更新以反射(reflect)最新的数据库值,因此我们可以安全地再次尝试 SaveChanges()。

    更新了您的问题:

    任务是:仅当我能够将状态从已创建更改为已提醒时才发送电子邮件。因此,在处理 OptimisticConcurrencyException 时强制通过 SaveChanges 是没有意义的。如果更改状态导致异常,则错误处理程序应退出,否则重试整个任务(读取和保存)。如果通过 RowVersion 列而不是仅通过状态启用乐观并发,我该怎么做?

    好吧,每次修改行时,rowversion 字段都会自动更新,因此在您的情况下,最好关闭 rowversion 上的并发模式并将其打开,对于 State 属性,这样您的代码就会如此简单作为:
    try {
    context.SaveChanges();
    SendMail();
    }
    catch (OptimisticConcurrencyException) {
    // RowVersion Concurrency Mode = Fixed
    // State Concurrency Mode = None
    // If it reaches here it means that the State has been changed;
    // so you do nothing except than throwing the exception
    throw;
    }

    但是,如果您只想为 rowversion 属性设置 Concurrency Mode = Fixed(就像您提到的那样),那么这意味着您可能会在包括 State 在内的任何字段上获得 OptimisticConcurrencyException 以进行更改,因此需要做更多的工作工作:
    try {
    ctx.SaveChanges();
    SendMail;
    }
    catch (OptimisticConcurrencyException) {
    // RowVersion Concurrency Mode = Fixed
    // State Concurrency Mode = None
    // If it reches here it means that ANY/All field(s) has changed
    // So we need to see if it was State:
    ctx.Refresh(RefreshMode.ClientWins, approval);
    ObjectStateEntry ose = ctx.ObjectStateManager.GetObjectStateEntry(approval);
    string stateValue = ose.OriginalValues["State"].ToString();
    // If the value is still "Created" then something else should have changed,
    // And caused the exception, so we can proceed with the Save:
    if (stateValue == "Created") {
    ctx.SaveChanges();
    SendMail;
    }
    else {
    // Nope, it was state, so we throw the exception to the caller:
    throw;
    }

    关于.net - EF 等效于 SqlCommand.ExecuteNonQuery 影响的行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3695780/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com