C# 异步工作流和用户交互:如何实现';背面';功能

C# 异步工作流和用户交互:如何实现';背面';功能,c#,async-await,C#,Async Await,我们正在尝试使用异步工作流来处理用户交互。我们的想法是,我们提供了一些基本的可能交互(上传文件、提示输入一些数据等)——以Web表单的形式实现——我们的系统用户可以编写在服务器上运行的工作流,并结合这些交互: var uploadResult = await DoUpload(); // . . .do stuff with uploadResult .. .. var formResult = await DoForm() // . . . do stuff with form

我们正在尝试使用异步工作流来处理用户交互。我们的想法是,我们提供了一些基本的可能交互(上传文件、提示输入一些数据等)——以Web表单的形式实现——我们的系统用户可以编写在服务器上运行的工作流,并结合这些交互:

  var uploadResult = await DoUpload();
  // . . .do stuff with uploadResult .. ..
  var formResult = await DoForm()
  // . . . do stuff with formResult
除了“返回”按钮的处理外,所有这些都可以正常工作:在上面的示例中,调用
DoForm()
会提示输入一些数据,因为它是工作流中的第二个表单,所以它还应该有一个“返回”按钮,当按下该按钮时,返回到初始上载表单

最明显的方法是在每次交互的结果中返回一个
BackClicked
标志,但是它应该由工作流代码进行测试和处理-我们更喜欢自动工作的东西

下面是一个简单控制台应用程序的代码,它复制了我们正在做的事情:

class Program {

  class Step {
    public string Prompt;
    public TaskCompletionSource<string> Tsc;
  }

  /// <summary>User interactions executed so far</summary>
  static List<Step> s_Steps = new List<Step>();
  static bool s_Continue = false;

  /// <summary>Example of a (very basic) user interaction</summary>
  static Task<string> ReadString(string prompt)
  {
    TaskCompletionSource<string> tsc = new TaskCompletionSource<string>();
    try {
      s_Steps.Add(
        new Step() { Prompt = prompt, Tsc = tsc }
      );
      s_Continue = true;
    } catch (Exception exception) {
      tsc.SetException(exception);
    }
    return tsc.Task;
  } // ReadString

  static async Task TheWorkflow()
  {
    var str1 = await ReadString("String 1");
    Console.WriteLine("String 1='{0}'", str1);
    var str2 = await ReadString("String 2");
    Console.WriteLine("String 2='{0}'", str2);
  } // TheWorkflow

  static void Main(string[] args)
  {
    // Start the workflow...
    TheWorkflow();
    // ..and then execute it
    while (s_Continue && s_Steps.Count > 0) {
      s_Continue = false;
    Back:
      var step = s_Steps[s_Steps.Count - 1];
      if (s_Steps.Count > 1) {
        Console.Write(step.Prompt + " [back]: ");
      } else {
        Console.Write(step.Prompt + ": ");
      }
      string result = Console.ReadLine();
      if (s_Steps.Count > 1 && result == "back") {
        s_Steps.RemoveAt(s_Steps.Count - 1);
        goto Back;
      }
      step.Tsc.SetResult(result);
    }
  }
}
类程序{
类步{
公共字符串提示;
公共任务完成源Tsc;
}
///迄今为止执行的用户交互
静态列表s_步骤=新列表();
静态布尔s_Continue=false;
///(非常基本的)用户交互示例
静态任务ReadString(字符串提示)
{
TaskCompletionSource tsc=新TaskCompletionSource();
试一试{
s_步骤添加(
新步骤(){Prompt=Prompt,Tsc=Tsc}
);
s_Continue=true;
}捕获(异常){
tsc.SetException(异常);
}
返回tsc任务;
}//读取字符串
静态异步任务工作流()
{
var str1=等待读取字符串(“字符串1”);
WriteLine(“字符串1='{0}',str1);
var str2=等待读取字符串(“字符串2”);
WriteLine(“字符串2='{0}',str2);
}//工作流
静态void Main(字符串[]参数)
{
//启动工作流。。。
工作流();
//…然后执行它
while(s_Continue&&s_Steps.Count>0){
s_Continue=false;
背面:
var step=s_Steps[s_Steps.Count-1];
如果(s_Steps.Count>1){
Console.Write(step.Prompt+“[back]:”);
}否则{
Console.Write(step.Prompt+“:”);
}
字符串结果=Console.ReadLine();
如果(s_Steps.Count>1&&result==“返回”){
s_Steps.RemoveAt(s_Steps.Count-1);
返回;
}
步骤Tsc设置结果(结果);
}
}
}
在第二个提示下键入
back
时,代码尝试重新执行第一个任务,但失败,因为它已经完成


有没有一种方法可以让它工作——即以某种方式调用以前执行的任务的延续?

简言之,没有。您正在对核心类型进行一次相当大的重新设计,我认为您无法使用
异步
方法将其全部绑定在一起。注意内部平台效应。谢谢@StephenCleary,我想是的,但我希望在放弃这个想法之前得到一些确认。