在这种情况下处理异常的最佳方法是什么

What is the best way to handle exceptions in this scenario

我正在编写一个 C# WEB API,它采用多部分形式的数据在数据库中创建记录并将文件上传到某个位置。作为创建的一部分,我还创建了一个 Jira Issue 并使用 JIRA 详细信息更新创建的记录。

  [Route("api/request/create")]
  [Consumes("multipart/form-data")]
  public async Task<HttpResponseMessage> CreateRequest()
  {
     try
     {
        var multipartMemoryStream = await Request.Content.ReadAsMultipartAsync();
        var newRequestData = await multipartMemoryStream.Contents[0].ReadAsStringAsync();

       CreateRequestCommand createRequestCommand = JsonConvert.DeserializeObject<CreateRequestCommand>(newRequestData);

       var requestId = CreateRequestInDB(createRequestCommand)
       var jira = await CreateJira(createContentRequestCommand);
       await WriteSourceFiles(multipartMemoryStream.Contents);
       await UpdateRequestWithJiraDetails(requestId, jira);
       return new SuccessResponse(requestId)
    }
   catch (Exception ex)
   {
      throw ex;
   }
}

这在大多数情况下工作正常。我怎样才能以最好的方式处理异常,以便如果任何方法失败,系统不应在数据库中保留记录并删除 JIRA 问题。 如果任何步骤失败,将系统保持在一致状态的最佳策略是什么。

如果您有多个操作并且希望它们全部成功或全部失败,那么您需要一个称为 Transaction 的概念。

在 .NET 中,这是在 System.Transactions 命名空间中实现的,class TransactionScope 可能是您最终会使用的。

抱歉,这含糊不清,没有代码示例,但由于您只发布了自己的方法名称,您将不得不自己弄清楚,这些方法中实际执行的代码是做什么的,以及它的效果如何使用 Microsoft classes。如果您的数据库是 Entity Framework 或普通的 SqlConnection,它可能开箱即用。但这取决于你自己想办法。

如果您在维基百科上查看 Distributed transaction 的定义,那么它是这样说的:

A distributed transaction is a database transaction in which two or more network hosts are involved. Usually, hosts provide transactional resources, while the transaction manager is responsible for creating and managing a global transaction that encompasses all operations against such resources. Distributed transactions, as any other transactions, must have all four ACID (atomicity, consistency, isolation, durability) properties, where atomicity guarantees all-or-nothing outcomes for the unit of work (operations bundle).

尽管定义侧重于数据库相关性,但即使您想将数据库和文件编写为工作单元,该示例也适用。如果它们中的任何一个失败,那么它们都应该回滚。

在那些日子里,.NET 只能 运行 只能在 Windows 操作系统上使用,.NET 可以利用 Transaction Manager. All you had to do is to implement the IEnlistmentNotification interface for Two-Phase-Commit. That interface exposes 4 methods:

  • Prepare 投票
  • Commit 幸福之路
  • Rollback 不愉快的道路
  • InDoubt第二阶段

最后,您必须通过以下调用将该实现注册到 TransactionManager:Transaction.Current.EnlistVolatile

.NET Core does not support分布式事务。也许是 .NET 5...

2 Phase Commit(和 3PC)协议存在阻塞问题(1, 2, 3) there might be situations when it stuck and can't move on. Martin Kleppmann's Designing Data-Intensive Applications 本书以非常容易理解的方式详细说明了该问题。


用微服务的话来说,推荐的方法是 Sagas pattern。你有本地交易,你有所有的 ACID 保证。需要的是参与者之间的某种协调。可以有指挥整个流程的协调者,参与者彼此不认识。或者参与者可以组成一个编排,每个参与者都知道如何与上一个和下一个参与者交流。

如果失败,您可以回滚或应用补偿操作来撤消先前操作的影响。有几个非常好的资源,其中对所有这些都进行了深入的详细介绍。我推荐 Chris Richardson's Microservices Patterns book. For online get started read this article.

如果您想了解真实世界的示例,那么我建议您阅读 Jimmy Bogard's excellent blog series