如何将逻辑从 Azure Durable Functions Orchestrator 移到另一个 class?

How to move logic out of Azure Durable Functions Orchestrator into another class?

作为一般做法,我们一直在将我们自己的 "service" 类 注入我们所有的函数应用程序,我们希望为 Orchestrator 做同样的事情。

示例:

  public async Task<string> Run(
            [OrchestrationTrigger] IDurableOrchestrationContext context,
            ILogger log)
        {
            try
            {
                // get input
                var input = context.GetInput<MyInputType>();

                // do some stuff #1

                var input1 = new BlahBlahOne();
                await context.CallActivityWithRetryAsync<string>("activityfn1", retryOptions, input1);

                // do some stuff #2

                var input1 = new BlahBlahTwo();
                await context.CallActivityWithRetryAsync<string>("activityfn3", retryOptions, input1);

                // do some stuff #3

                var input1 = new BlahBlahThree();
                await context.CallActivityWithRetryAsync<string>("activityfn3", retryOptions, input1);

                // do some stuff #4

                return "I'm done";
            }
            catch (Exception ex)
            {
                log.LogError(ex, "unexpected error");
                throw;
            }
        }

我们想做这样的事情:

  public async Task<string> Run(
            [OrchestrationTrigger] IDurableOrchestrationContext context,
            ILogger log)
        {
            try
            {
                string output = await _myOrchestratorService.RunAsync(context); // NOT allowed! 
                return output
            }
            catch (Exception ex)
            {
                log.LogError(ex, "unexpected error");
                throw;
            }
        }

但是,请注意,根据 Durable Functions 代码对多线程的限制,我们不能使用 'await'。所以我在下面尝试过,但是我该如何编码呢?调用 .Result 在 Activity 函数上生成代码 'hang'。我做错了什么?

public string Run(IDurableOrchestrationContext context)
{
       // code like before, but then how do I call this?
       // await context.CallActivityWithRetryAsync<string>("activityfn1", retryOptions, input1);

       // I tried this, doesn't work, will hang on the first activity function
       context.CallActivityWithRetryAsync<string>("activityfn1", retryOptions, input1).Result; 
}

澄清一下,在 Orchestrator 函数中使用 await 的限制仅适用于不是由 IDurableOrchestrationContext API 生成的任务。引用 Durable Functions 编排 code constraint documentation:

Orchestrator code must never start any async operation except by using the IDurableOrchestrationContext API or the context.df object's API. For example, you can't use Task.Run, Task.Delay, and HttpClient.SendAsync in .NET or setTimeout and setInterval in JavaScript. The Durable Task Framework runs orchestrator code on a single thread. It can't interact with any other threads that might be called by other async APIs.

一样,仅在 IDurableOrchestrationContext 创建的 Task 对象上调用 await 的异步辅助方法在技术上对 await 也是安全的。这意味着您对 await _myOrchestratorService.RunAsync(context); 的调用可能没问题,只要该异步方法遵循所有正常的编排代码约束即可。

综上所述,我不完全确定通过注入一个似乎只有一个方法的服务会获得什么,该方法包含通常存在于您的编排方法中的所有逻辑。该抽象似乎并没有提高代码的可测试性,额外的抽象层可能会混淆我们的持久函数分析器,该分析器有助于诊断您的代码何时违反编排约束。

就这样吧async Task<string>。它应该工作

public async Task<string> RunAsync(IDurableOrchestrationContext context)
{    
    var result = await context.CallActivityWithRetryAsync<string>("activityfn1", retryOptions, input1);
    return result;
}