Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/331.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 在与多个系统交互时做出类似事务的行为_C#_Design Patterns_Sequence_Monads - Fatal编程技术网

C# 在与多个系统交互时做出类似事务的行为

C# 在与多个系统交互时做出类似事务的行为,c#,design-patterns,sequence,monads,C#,Design Patterns,Sequence,Monads,免责声明 在以下职位: action=action/Func 我有一个执行多个操作的长方法。每个操作都包装在一个try-catch中。如果特定操作失败,在catch中,我必须对所有以前的操作和当前操作执行一个清除操作。 我不知道如何停止在catch中复制代码,并使用设计模式或其他方式来聚合它们 我目前拥有的 在上面的方法中,我不想在所有catch块中写入所有清除操作,直到该点。 我希望每个成功的操作都设置一个类似于检查点的东西,然后当检查点失败时,检查状态并执行所有必需的清除,直到该点 我想要什

免责声明 在以下职位: action=action/Func

我有一个执行多个操作的长方法。每个操作都包装在一个try-catch中。如果特定操作失败,在catch中,我必须对所有以前的操作和当前操作执行一个清除操作。 我不知道如何停止在catch中复制代码,并使用设计模式或其他方式来聚合它们

我目前拥有的

在上面的方法中,我不想在所有catch块中写入所有清除操作,直到该点。 我希望每个成功的操作都设置一个类似于检查点的东西,然后当检查点失败时,检查状态并执行所有必需的清除,直到该点

我想要什么

我在想,是否有一种设计模式来包装这些可能具有不同返回类型的操作,并将其管道化。无论哪个操作失败,它都会触发Clearaction1…ClearactionN,其中N是失败的操作

另外,它可能有点像单子

并购->并购->并购->并购->并购->并购->并购->并购->并购->并购->并购->并购->并购->并购 其中a、b、c、d是类型,区别在于我需要汇总所有失效处理

更新

在这个论坛上回答之后,我觉得我需要做一些补充:

这是ASP网络控制器内的端点。我从多个系统检索数据,并使用获取的数据设置其他系统。 我希望它看起来像一个分布式系统事务:

获取输入系统[A,B] 到输出系统[X,Y]

序列示例

从数据库中获取数据 使用数据在X上设置数据并获得响应Z 从B获取数据 使用从B和Z获取的数据在Y上设置数据 塞塞纳里奥 现在假设从B获取数据失败,我想: -仅从X清除数据

我不想试图清除Y上的数据,因为它会产生不可撤销的损害


我只关心设置数据的I/O操作。

如果您的代码对性能/分配不是很关键,您可以创建一个反向操作列表和布尔变量来跟踪成功:

public void LongMethod()
{
   var reverseActions = new List<Action>();
   var success = false;
   try
   {
      int checkpoint=0;
      Action1();
      reverseActions.Add(ClearAction1);
      Action2();
      reverseActions.Add(ClearAction2);
      ...
      success = true;
   }
   finally // or can be catch if you can/want to handle/swallow exception
   {   
         if(!success)
         {
            foreach(var a in reverseActions)
            {
             a();
            }
         }
    }
}

如果您的代码对性能/分配不是很关键,您可以创建一个反向操作列表和布尔变量来跟踪成功:

public void LongMethod()
{
   var reverseActions = new List<Action>();
   var success = false;
   try
   {
      int checkpoint=0;
      Action1();
      reverseActions.Add(ClearAction1);
      Action2();
      reverseActions.Add(ClearAction2);
      ...
      success = true;
   }
   finally // or can be catch if you can/want to handle/swallow exception
   {   
         if(!success)
         {
            foreach(var a in reverseActions)
            {
             a();
            }
         }
    }
}

似乎您可以简单地构建一个动作对列表和相应的补偿动作。然后从1迭代到N,应用每个操作。如果在步骤I中捕获到异常,则通过补偿操作列表从I向后迭代到1


此外,还可以使用单子进行补偿,例如Scala CAT库。似乎您可以简单地构建一个动作对列表和相应的补偿动作。然后从1迭代到N,应用每个操作。如果在步骤I中捕获到异常,则通过补偿操作列表从I向后迭代到1


此外,还可以使用单子进行补偿,例如Scala猫的库

如果您能够拥有一个状态对象,该对象具有所有副作用,您可以执行以下操作:

public State LongMethod( State originalState ) // assuming, `State` is your state object type
{
    //                     vv Copy-CTOR == "Begin Transaction"
    State localState = new State(originalState); 
    try{
       // mutate _the local copy_
       action1(localState);
       var intermediateResult = func2(localState);
       action3(localState, intermediateResult);
       // ...
       return localState; // return mutated state == "Commit"
    }
    catch(Exception ex)
    {
        // return unchanged state == "Rollback"
        return originalState;
    }
}
为什么我要费心在一个不同的答案被接受后再加上这个

关于Martin的评论,我想提出以下备选方案:


也许与你的问题无关,但是你是否考虑过如果你的流程在这个交易的中间终止会发生什么?

如果进程在上述代码中终止,则会有一个一致的状态:未更改的状态

缺点是:只有当您能够隔离状态并且不依赖于过程中触发的事件时,它才真正起作用


更清楚地说:如果在这个过程中,假设action5对API XYZ执行HTTP PUT,那么这个解决方案是不够的,因为您必须主动地反转该PUT。

如果您能够有一个状态对象,它会产生所有副作用,那么您可以这样做:

public State LongMethod( State originalState ) // assuming, `State` is your state object type
{
    //                     vv Copy-CTOR == "Begin Transaction"
    State localState = new State(originalState); 
    try{
       // mutate _the local copy_
       action1(localState);
       var intermediateResult = func2(localState);
       action3(localState, intermediateResult);
       // ...
       return localState; // return mutated state == "Commit"
    }
    catch(Exception ex)
    {
        // return unchanged state == "Rollback"
        return originalState;
    }
}
为什么我要费心在一个不同的答案被接受后再加上这个

关于Martin的评论,我想提出以下备选方案:


也许与你的问题无关,但是你是否考虑过如果你的流程在这个交易的中间终止会发生什么?

如果进程在上述代码中终止,则会有一个一致的状态:未更改的状态

缺点是:只有当您能够隔离状态并且不依赖于过程中触发的事件时,它才真正起作用

更清楚地说:如果在这个过程中,假设action5对API XYZ执行HTTP PUT,那么这个解决方案是不够的,因为您必须主动反转该PUT。

一个操作总是返回void类型。否则它将是一个函数。我在考虑做一些实际上是交易性的事情。但这将取决于这些行动的作用。你有状态对象或类似的东西吗?这些行动有副作用吗

在改变该状态之外?请原谅,我对它们的命名不正确,你是对的。我所说的操作是指Func和Action。它们中的一些确实有返回类型,而另一些则没有。它们中的一些相互依赖。序列可以是Func>>Action>>Func>>Action。这些方法确实有副作用。也许与你的问题无关,但是你是否考虑过如果你的进程在这个交易的中间终止会发生什么?我知道它不是很好,但是它们都需要发生,所以我现在有了一个大的捕捉,我正在运行所有清晰的动作。其中包含检查是否需要执行的逻辑。通过相互依赖,您的意思是,例如,在没有调用Action1之前,不能调用Action2?一个操作总是返回void类型。否则它将是一个函数。我在考虑做一些实际上是交易性的事情。但这将取决于这些行动的作用。你有状态对象或类似的东西吗?这些操作在改变该状态之外是否有副作用?请原谅,我对它们的命名不正确,你是对的。我所说的操作是指Func和Action。它们中的一些确实有返回类型,而另一些则没有。它们中的一些相互依赖。一个序列可能是Func>>Action>>Func>>Action。这些方法确实有副作用。也许与你的问题无关,但是你是否考虑过如果你的进程在这个交易的中间终止会发生什么?我知道它不是很好,但是它们都需要发生,所以我现在有了一个大的捕捉,我正在运行所有清晰的动作。它们内部有逻辑,可以检查是否需要执行。通过相互依赖,您的意思是,例如,在没有调用Action1之前,不能调用Action2?这就是我在最后将所有这些操作包含在monad中的意思。因此,操作的结果可以是一个对象,它可以处于两种状态之一:异常/结果可能是结果。所以我可以有一个类:class Monad{Exception ex;T result;}由一个具有所有副作用的状态对象生成,你是说一个可以触发所有i/O调用的对象,对吗?我可能有,是的,你的实现实际上就是我想要在一个对象上应用计算,而这个对象在每一步都会失败。我的问题是,你会把每次计算的ClearAction逻辑放在哪里?我不想仅仅抛出一个异常。异常处理也会引发副作用。@Bercovicadrian在这种情况下,状态对象仅仅是一个数据库,一个属性集合,如果您愿意的话。它在计算过程中按特定时间点保存数据。上述解决方案的要点是:您不需要ClearActions,因为一旦发生错误,您只需一次性放弃所有更改。但正如在回答中已经指出的那样:只有当你不改变其他地方的状态时,这才能起作用,也必须改变回去。你说的其他地方是什么意思?创建更多上下文:这是ASP NET Web API控制器中的一个方法。这是一种初始化方法,我尝试从一些系统A、B获取数据i/O调用,并将其他数据设置到一些其他系统X、Y。序列可以是get A->set X,get B->set Y。然而,我确实关心序列到达了哪里。我不想尝试清除尚未查询的系统中的数据。示例:get A->set X,get B在get B时失败。在这种情况下,我不想尝试清除Y。好的,在这种情况下,您不能使用它。其他地方可能意味着系统x和y。只读IO并不重要。但在SystemX中设置某些内容是一种外部状态更改。因此,如果您需要在出现错误的情况下逆转这一点,那么这个解决方案是不够的。这就是我在最后将所有这些操作包含在monad中的意思。因此,操作的结果可以是一个对象,它可能处于两种状态之一:异常/结果可能结果。所以我可以有一个类:class Monad{Exception ex;T result;}由一个具有所有副作用的状态对象生成,你是说一个可以触发所有i/O调用的对象,对吗?我可能有,是的,你的实现实际上就是我想要在一个对象上应用计算,而这个对象在每一步都会失败。我的问题是,你会把每次计算的ClearAction逻辑放在哪里?我不想仅仅抛出一个异常。异常处理也会引发副作用。@Bercovicadrian在这种情况下,状态对象仅仅是一个数据库,一个属性集合,如果您愿意的话。它在计算过程中按特定时间点保存数据。上述解决方案的要点是:您不需要ClearActions,因为一旦发生错误,您只需一次性放弃所有更改。但正如在回答中已经指出的那样:只有当你不改变其他地方的状态时,这才能起作用,也必须改变回去。你说的其他地方是什么意思?创建更多上下文:这是ASP NET Web API控制器中的一个方法。这是一种初始化方法,我尝试在其中获取数据i/O调用
从一些系统A、B和set其他数据到一些其他系统X、Y。序列可以是get A->set X,get B->set Y。然而,我确实关心序列到达了哪里。我不想尝试清除尚未查询的系统中的数据。示例:get A->set X,get B在get B时失败。在这种情况下,我不想尝试清除Y。好的,在这种情况下,您不能使用它。其他地方可能意味着系统x和y。只读IO并不重要。但在SystemX中设置某些内容是一种外部状态更改。因此,如果您需要在发生错误的情况下反转,那么这个解决方案是不够的。