c# - 正确使用Azure Durable Function - 序列化复杂对象

标签 c# azure azure-functions azure-durable-functions

因此,我正在对一些Azure Durable Functions进行原型(prototype)设计,尝试了解它们是否适合我们内部 API 系统的建议解决方案。

根据示例,我创建了一个Orchestrator 客户端 ( HelloOrchestratorClient.cs ),它响应 HttpTrigger 。该客户端从原始请求中提取一些信息,然后继续触发协调器功能 (HelloOrchestrator.cs),传入提取的一些信息:

复杂的HelloOrchestratorClient.cs:

[FunctionName("HttpSyncStart")]
public static async Task<HttpResponseMessage> Run(
    [HttpTrigger(AuthorizationLevel.Anonymous, methods: "get", Route = "orchestrators/{functionName}/wait")]
    HttpRequestMessage req,
    [OrchestrationClient] DurableOrchestrationClient starter,
    string functionName,
    ILogger log)
{       
    HttpReq originalRequest = new HttpReq() {
            DeveloperId = GetDevKey(req,apiHeaderKey),
            QueryString = req.RequestUri.Query,
            APIName = GetQueryStringValue(req,APIName),
            APIVersion = GetQueryStringValue(req,APIVersion)

    };
    string instanceId =   await starter.StartNewAsync(functionName, originalRequest);

    TimeSpan timeout = GetTimeSpan(req, Timeout) ?? TimeSpan.FromSeconds(30);
    TimeSpan retryInterval = GetTimeSpan(req, RetryInterval) ?? TimeSpan.FromSeconds(1);

    return  await starter.WaitForCompletionOrCreateCheckStatusResponseAsync(
        req,
        instanceId,
        timeout,
        retryInterval);

}

HelloOrchestrator.cs现在只是调用我们的内部 API 之一并返回 JsonProduct有效负载(简单的 POCO 描述,你猜对了,一个标题),使用 ActivityTigger命名HelloOrchestrator.APICall进行 API 调用本身。

复杂的HelloOrchestrator.cs:

  [FunctionName("E1_JsonProduct")]
        public static async Task<List<JsonProduct>> Run(
            [OrchestrationTrigger] DurableOrchestrationContextBase context,
            ILogger log)
        {
            List<JsonProduct> output = new List<JsonProduct>();
            HttpReq r = context.GetInput<HttpReq>();
            if(r != null)
            {
                if(r.DeveloperId == null)
                {
                    return output;
                }
                output.Add(await context.CallActivityAsync<JsonProduct>("E1_CallAPI",r));
                return output;
            }
            return output;
        } 

[FunctionName("E1_CallAPI")]
public async static Task<JsonProduct> APICall([ActivityTrigger] HttpReq req,
    ILogger log)
{

    JsonProduct products  = null;
    string u = $"{baseAddress}{req.APIVersion}/{req.APIName}{req.QueryString}";  

    var request = new HttpRequestMessage(HttpMethod.Get, u);
    request.Headers.Accept.Add(
        new MediaTypeWithQualityHeaderValue("application/json")
    );
    request.Headers.Add("x-apikey",req.DeveloperId);
     log.LogInformation($"URL calling = '{request.RequestUri.AbsoluteUri}'.");
    HttpResponseMessage response = await client.SendAsync(request);
    // return await response.Content.ReadAsStringAsync();
    if(response.IsSuccessStatusCode)
    {
        var formatter = new JsonMediaTypeFormatter
        {
            SerializerSettings = HelloProj.CosmosDB.Models.Products.Converter.Settings
        };

        products = await response.Content.ReadAsAsync<JsonProduct>(new [] {formatter});
    }
    return products;
}

旁注: 如果我能让这个工作正常,计划是将一堆进程扇出到不同的 API,然后再次扇入,合并 JSON 有效负载并将其返回到发起者。

我遇到的问题

所以,当我的 List<JsonProduct>HelloOrchestrator.Run 返回,我收到以下 NullReferenceException在此找到Gist (大堆栈跟踪),我收到来自 Orchestrator 客户端500 响应

下面证明output返回的实际上在运行时有一个对象:

Runtime Screen Grab of output

是否是因为 JsonProduct 的复杂性(再次找到模型类 here )?我问,因为当我将我的Orchestrator Function换成更简单的模型结构时,我没有收到 500,我收到了 JSON有效负载。

此示例显示了简单的 Orchestrator 功能 HelloOrchestrator.cs ,返回一个简单的TestToDo.cs ( Gist for model ) 不会出错的平面对象:

简单的HelloOrchestrator.cs:

   [FunctionName("E1_Todo")]
    public static async Task<TestToDo> RunToDo(
    [OrchestrationTrigger] DurableOrchestrationContextBase context,
        ILogger log)
    {
        HttpReq r = context.GetInput<HttpReq>();
        TestToDo todo = new TestToDo();
        if(r != null)
        {
            todo = await context.CallActivityAsync<TestToDo>("E1_CallAPITodo",r);
        }
        return todo;
    }

[FunctionName("E1_CallAPITodo")]
public async static Task<TestToDo> APITodoCall([ActivityTrigger] HttpReq req,
    ILogger log)
{

    var request = new HttpRequestMessage(HttpMethod.Get, "https://jsonplaceholder.typicode.com/todos/1");
    request.Headers.Accept.Add(
        new MediaTypeWithQualityHeaderValue("application/json")
    );
     log.LogInformation($"URL calling = '{request.RequestUri.AbsoluteUri}'. for {req.QueryString}");
    HttpResponseMessage response = await client.SendAsync(request);
    return await response.Content.ReadAsAsync<TestToDo>();
} 

更多信息

如果您需要我的完整原型(prototype)项目,可以在这里找到它们:

当你运行它时,在 Postman 之类的东西中使用以下内容(F5 后):

http://localhost:7071/api/orchestrators/E1_JsonProduct/wait?timeout=20&retryInterval=0.25&api=products&apiVersion=v1&filterByImprints=W%26N&N

当你运行它时,在 Postman 之类的东西中使用以下内容(F5 后):

http://localhost:7071/api/orchestrators/E1_Todo/wait?timeout=20&retryInterval=0.25

最佳答案

查看您发布的调用堆栈,NullReferenceException 似乎是 DurableOrchestrationClient 类中的错误。查看代码(您可以找到 here )似乎有可能,如果您使用的查询字符串无法正确解析,则可能存在空引用。

您提到您正在使用以下 URL 进行测试:

http://localhost:7071/api/orchestrators/E1_JsonProduct/wait?timeout=20&retryInterval=0.25&api=products&apiVersion=v1&filterByImprints=W%26N&N

我想知道最后两个字符(&N)是否是问题的根源。是否可以对 & 进行编码或将其完全删除以隔离问题?

无论哪种方式,如果您可以在此处记录问题,那就太好了:https://github.com/Azure/azure-functions-durable-extension/issues

关于c# - 正确使用Azure Durable Function - 序列化复杂对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52738284/

相关文章:

javascript - azure ARM 消耗: get consumption of a resource group

c# - 在 function.json 中参数化 runOnStartup

Azure Functions 应用程序在发布后为只读

c# - 使用 TeamCity 设置构建机器

azure - ARM模板: how to use the exported template. json和parameters.json文件?

c# - 当我 Assembly.Load 肯定存在的程序集时,为什么会出现 FileNotFoundException?

c# - 如何在 Visual Studio 中使用 .NET 5(独立进程)调试 Azure Functions?

C# ASP X-CONTENT-TYPE-OPTIONS - Internet Explorer 不显示 PNG 文件

c# - 使用 Windows 10 的 crystalreportviewer

c# - 为什么 HttpRequestValidationException 有 500 个 http 错误代码而不是 400?