首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >在C#中为同步/异步任务添加重试/回滚机制的最佳方法是什么?

在C#中为同步/异步任务添加重试/回滚机制的最佳方法是什么?
EN

Stack Overflow用户
提问于 2016-05-23 14:33:37
回答 5查看 3.4K关注 0票数 22

假设有一个WebForms应用程序,其中有一个名为CreateAll()的主要方法。我可以一步一步地描述方法任务的过程:

1)数据库存储(更新/创建数据库项3至4次)

2)启动新线程

3) Result1 =调用soap服务,通过使用超时阈值检查状态,在x minutes.The继续运行之后(状态现在已确定,并不意味着失败)

4)数据库存储(更新/创建数据库项3至4次)

5) result2 =调用soap服务(以火灾和遗忘的方式)

6)更新配置文件(实际上是从result1获取的)

7)通过使用回调请求,它检查前面部分的每个checks、result2的状态和UI显示一个进度bar.If,进程完成了(100%),这意味着成功。

我认为所有这些任务都可以按照它们的type.Basically进行分组,几种类型的操作是:

  • Type1: DB事务
  • Type2:服务通信/事务
  • Type3:配置文件I/O事务

我希望在现有实现中添加回滚/重试机制,并使用面向任务的体系结构和重构现有的遗留代码。

我发现,类似于Memento设计模式或C#中的命令模式可以帮助实现这一目的,我还发现msdn 重试模式描述很有趣。我真的不知道,我希望有人带我找到最安全和最好的决定.

您能建议我最好的方法来保持现有的实现和流程,但将其包装在一个通用的抽象重试/回滚/任务列表实现中吗?

最后的实现必须能够在每种情况下重新尝试(无论在一般的createAll过程中,无论是什么任务或一般故障,例如超时等等),还有一个回滚决策列表,其中应用程序必须能够回滚所有已完成的任务。

我想要一些例子,如何打破这个耦合的代码。

PseudoCode可能会有帮助:

代码语言:javascript
复制
class something
{  
    static result CreateAll(object1 obj1, object2 obj2 ...)
    {
        //Save to database obj1
        //...
        //Update to database obj1 
        //
        //NEW THREAD
       //Start a new thread with obj1, obj2 ...CreateAll
       //...          
     } 

    void CreateAllAsync()
    {
        //Type1 Save to database obj1
        //...
        //Type1 Update to database obj2

        //Type2 Call Web Service to create obj1 on the service (not async)

        while (state != null && now < times)
        {
            if (status == "OK")
            break;      
            else
            //Wait for X seconds
        }

        //Check status continue or general failure
        //Type1 Update to database obj2 and obj1

        //Type2 Call Web Service to create obj2 on the service (fire and forget)

        //Type3 Update Configuration File
        //Type1 Update to database obj2 and obj1
        //..   

    return;
}

//Then the UI takes the responsibility to check the status of result2
EN

回答 5

Stack Overflow用户

回答已采纳

发布于 2016-05-23 14:55:12

查看使用波莉进行重试的场景,这些场景似乎与伪代码很好地一致。在这个答案的末尾是文档中的一个示例。您可以执行各种重试方案、重试和等待等。例如,您可以多次重试一个完整的事务,或者多次重试一组幂等操作,然后在重试策略最后失败时写入补偿逻辑。

memento模式更适合于在字处理器(Ctrl和Ctrl)中找到的撤销-重做逻辑。

其他有用的模式是一个简单的队列,一个持久的队列,甚至一个服务总线,以使您最终一致,而不必让用户等待一切顺利完成。

代码语言:javascript
复制
// Retry three times, calling an action on each retry 
// with the current exception and retry count
Policy
    .Handle<DivideByZeroException>()
    .Retry(3, (exception, retryCount) =>
    {
        // do something 
    });

基于伪代码的示例如下所示:

代码语言:javascript
复制
static bool CreateAll(object1 obj1, object2 obj2)
{
     // Policy to retry 3 times, waiting 5 seconds between retries.
     var policy =
         Policy
              .Handle<SqlException>()
              .WaitAndRetry(3, count =>
              {
                 return TimeSpan.FromSeconds(5); 
              });

       policy.Execute(() => UpdateDatabase1(obj1));
       policy.Execute(() => UpdateDatabase2(obj2));
  }
票数 13
EN

Stack Overflow用户

发布于 2016-05-29 21:06:55

您可以选择命令模式,其中每个命令都包含所有必要的信息,如连接字符串、服务url、重试计数etc.On,可以考虑使用rx、数据流块来完成管道操作。

高层视图

更新:意图是分离关注。重试逻辑仅限于一个类,它是现有命令的包装器。您可以进行更多的分析,并提出适当的命令、调用程序和接收对象,并添加回滚功能。

代码语言:javascript
复制
public abstract class BaseCommand
{
    public abstract RxObservables Execute();
}

public class DBCommand : BaseCommand
{
    public override RxObservables Execute()
    {
        return new RxObservables();
    }
}

public class WebServiceCommand : BaseCommand
{
    public override RxObservables Execute()
    {
        return new RxObservables();
    }
}

public class ReTryCommand : BaseCommand // Decorator to existing db/web command
{
    private readonly BaseCommand _baseCommand;
    public RetryCommand(BaseCommand baseCommand)
    {
         _baseCommand = baseCommand
    }
    public override RxObservables Execute()
    {
        try
        {
            //retry using Polly or Custom
            return _baseCommand.Execute();
        }
        catch (Exception)
        {
            throw;
        }
    }
}

public class TaskDispatcher
{
    private readonly BaseCommand _baseCommand;
    public TaskDispatcher(BaseCommand baseCommand)
    {
        _baseCommand = baseCommand;
    }

    public RxObservables ExecuteTask()
    {
        return _baseCommand.Execute();
    }
}

public class Orchestrator
{
    public void Orchestrate()
    {
        var taskDispatcherForDb = new TaskDispatcher(new ReTryCommand(new DBCommand));
        var taskDispatcherForWeb = new TaskDispatcher(new ReTryCommand(new WebCommand));
        var dbResultStream = taskDispatcherForDb.ExecuteTask();
        var WebResultStream = taskDispatcherForDb.ExecuteTask();
    }
}
票数 5
EN

Stack Overflow用户

发布于 2016-05-31 08:25:00

对我来说,这听起来像是“分布式事务”,因为您有不同的资源(数据库、服务通信、文件i/o),并且希望能够处理可能涉及所有资源的事务。

在C#中,您可以使用Microsoft分布式事务协调器解决这个问题。对于每个资源,您都需要一个资源管理器。对于数据库,如sql server和文件i/o,据我所知,它已经可用。对于其他人来说,你可以发展自己的。

例如,要执行这些事务,可以使用TransactionScope类,如下所示:

代码语言:javascript
复制
using (TransactionScope ts = new TransactionScope())
{
    //all db code here

    // if an error occurs jump out of the using block and it will dispose and rollback

    ts.Complete();
}

(取自这里的例子)

要开发您自己的资源管理器,您必须实现IEnlistmentNotification,这可能是一个相当复杂的任务。这里是一个简短的示例

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/37393828

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档