【问题标题】:Make sure that call api success and save myself success确保调用api成功并保存自己成功
【发布时间】:2019-06-18 00:55:54
【问题描述】:

您好,当我将 ASP.NET MVC 与 EF 一起使用并从其他网站(也使用实体框架)调用 Web API 时,我发现了问题 问题是

我想确保 MVC SaveChanges() 和 Web API SaveChanges() 同时成功。

这是我梦寐以求的伪代码

public ActionResult Operation()
{
    Code Insert Update Delete....

    bool testMvcSaveSuccess = db.TempSaveChanges();  //it does not have this command.

    if(testMvcSaveSuccess == true)
    {
       bool isApiSuccess = CallApi(); //insert data to Other Web App


       if(isApiSuccess == true)
       {
          db.SaveChanges(); //Real Save
       }
    }
}

从上面的代码来看,如果没有db.TempSaveChanges(),Web API 可能会成功,但 MVC SaveChanges() 可能会失败。

【问题讨论】:

  • 那为什么不先CallAPI,然后根据Api成功与否做mvc savechanges?
  • @beibeizhu : 先调用api成功,然后你确定mvc savechange() 会成功吗? mvc 可能会保存失败
  • 所以你想在一个事务中进行两个操作..数据一致性问题也发生在微服务中,对不起我不熟悉这部分。

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


【解决方案1】:

是的,你可以。

您需要在上下文类中重载.Savechanges,在该类中将首先检查它,然后再调用常规。

或者在上下文类中创建您自己的TempSaveChanges() 调用它,然后如果成功从它调用SaveChanges

【讨论】:

    【解决方案2】:

    所以没有像TempSaveChanges 这样的东西,因为有更好的东西:Transactions

    Transaction 是一个 IDisposable(可以在 using 块中使用)并具有 CommitRollback 等方法。

    小例子:

    private void TestTransaction()
    {
        var context = new MyContext(connectionString);
    
        using (var transaction = context.Database.BeginTransaction())
        {
            // do CRUD stuff here 
    
            // here is your 'TempSaveChanges' execution
            int changesCount = context.SaveChanges();
    
            if (changesCount > 0)
            // changes were made
            {
                // this will do the real db changes
                transaction.Commit();
            }
            else
            {
                // no changes detected -> so do nothing
                // could use 'transaction.Rollback();' since there are no changes, this should not be necessary
                // using block will dispose transaction and with it all changes as well
            }
        }
    }
    

    我从我的 GitHub Exercise.EntityFramework 存储库中提取了这个示例。随意 Star/Clone/Fork...

    【讨论】:

    • 所以如果 changesCount > 0 则 100% 确定 transaction.Commit() 不会失败,对吧?
    • 到目前为止我知道:SaveChanged 确实以某种“锁定”方式更新了您的数据库(因为我们处于事务中)。这意味着要么一切都会成功,要么什么都不会改变。 Commit 调用将退出此锁定状态。 Rollback 将撤消所有更改并退出锁定状态。请注意,此事务执行取决于您当前使用的存储技术。有关更多信息,您可以查看SQL transactions 或启动 SQL 分析器并检查所有查询
    【解决方案3】:

    您所指的是原子性:您希望多个操作要么全部成功,要么都不成功。在数据库的上下文中,您可以通过 transactions(如果数据库支持它)获得它。但是,在您的情况下,您需要一个跨越两个不相交系统的事务。这种事务的通用(一些特殊情况有更简单的解决方案)健壮实现将对两个系统有一定的要求,并且还需要额外的持久性。

    基本上,您需要能够在序列中的任何时候从突然停止中优雅地恢复。您使用的每个数据库很可能都符合ACID,因此您可以依靠每个数据库事务来满足原子性要求(它们要么成功,要么失败)。因此,您只需要担心两个数据库事务的顺序。您对这两个系统的要求是一种后验确定是否执行某些操作的方法。

    流程示例:

    1. 操作开始
    2. 生成唯一的事务 ID 并持久化(使用请求数据)
    3. 更改本地数据库并提交
    4. 调用外部 Web API
    5. 将交易标记为已完成(或将其删除)
    6. 操作结束

    恢复:

    1. 从存储中获取所有待处理(未完成)的交易
    2. 检查是否对本地数据库进行了预期的更改
    3. 询问 Web API 是否进行了预期的更改
    4. 如果未进行任何更改或同时进行了两项更改,则事务完成:删除/标记它。
    5. 如果进行了其中一项更改但未进行另一项更改,则要么恢复所做的更改(恢复事务),要么执行未进行的更改(恢复事务)=> 然后删除/标记它。

    现在,您可以看到它很快变得复杂,特别是如果“确定是否进行了更改”是一项不平凡的操作。对此的常见解决方案是使用该唯一事务 ID 作为确定需要注意哪些数据的方法。但在这一点上,它变得非常特定于应用程序,完全取决于具体操作是什么。对于某些应用程序,您可以在恢复步骤中重新运行整个操作(因为您将整个请求数据存储在事务中)。一些特殊情况不需要持久化事务,因为还有其他方法可以实现相同的事情等。

    【讨论】:

      【解决方案4】:

      好的,让我们稍微澄清一下。

      你有一个 MVC 应用 A1,有自己的数据库 D1 然后你就有了一个 API,我们称它为 A2 并有自己的数据库 D2

      你想要在 A1 中的一些代码在 D1 中进行临时保存,然后触发对 A2 的调用,如果响应成功然后它这次将 D1 的临时数据保存在正确的位置。

      根据您的伪代码,我建议您创建第二个表,将您的“临时”数据保存在 D1 中。所以你的数据库有一个额外的表,流程是这样的:

      首先将A1数据保存在该表中,然后调用A2,数据保存在D2A1 收到确认并调用一个方法,将数据从第二个表移动到它应该在的位置。

      需要考虑的场景:

      1. D1 中保存临时数据有效,但对 A2 的调用失败。现在,您可以使用批处理作业清除孤立数据,或者在调用 A2 失败时调用删除它的方法。

      2. A2 调用成功,D1 调用失败,所以现在 D1 中有临时数据失败移动到右边的桌子。您可以针对每一行向第二个表添加一个标志,这表明对 A2 的第二次调用成功,因此该数据需要尽可能移动到正确的位置。您可以在这里有一个定期运行的服务,如果它找到任何标志设置为 true 的数据,那么它将数据移动到正确的位置。

      还有其他方法可以处理此类情况。您可以使用队列系统来管理它。每行数据都成为一条消息,您为其分配一个唯一的 ID,一个 GUID,它基本上是一个 CorrelationID,并且在两个系统中都是相同的。即使一个系统出现故障,当它恢复时,数据将被保存,世界上一切都很好,并且由于通用 ID,您始终可以正确链接。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-01-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多