简体   繁体   English

使用带有 C# 的持久函数添加单行时,在 Azure blob 表中创建了两个条目

[英]Two entries made in Azure blob table when adding a single row, using Durable Functions with C#

I'm working on a proof of concept, but I'm new to both Azure and C#.我正在研究概念验证,但我对 Azure 和 C# 都是新手。 The following code works, sort of - it successfully stores incoming JSON data into a blob table.下面的代码可以正常工作 - 它成功地将传入的 JSON 数据存储到 blob 表中。 The problem is that it creates 2 different rows each time, each with its own unique RowKey (GUID I create), but, as you can see, I only add to the table once with await GrooveData.AddAsync (data);问题是它每次都会创建 2 个不同的行,每个行都有自己唯一的 RowKey(我创建的 GUID),但是,如您所见,我只使用await GrooveData.AddAsync (data); . .

What am I missing?我错过了什么? The two rows have slightly different Timestamps.这两行的时间戳略有不同。

using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.DurableTask;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.Azure.WebJobs.Host;
using Microsoft.Extensions.Logging;

namespace Company.Function
{
    public static class GrooveOrchestrationTest
    {
        [FunctionName("GrooveOrchestrationTest")]
        public static async Task<List<string>> RunOrchestrator(
            [OrchestrationTrigger] IDurableOrchestrationContext context,
            [Table ("GrooveData")] IAsyncCollector<GrooveItem> GrooveData)
        {
            var outputs = new List<string>();
            GrooveItem data = context.GetInput<GrooveItem>();      

                // Indicate we got the data:
                outputs.Add(await context.CallActivityAsync<string>("NowProcessingMessage", data));
                
                // RowKey and PartitionKey are required by Azure, but we don't get them from Groove and must provide them:
                data.RowKey = Guid.NewGuid ().ToString ();
                data.PartitionKey = data.grooveevent;

                // and this stores the data:
                await GrooveData.AddAsync (data);

                //If we get this far, it probably worked :D
                outputs.Add(await context.CallActivityAsync<string>("GrooveDataTest", data));

            return outputs;
        }

        [FunctionName("NowProcessingMessage")]
        public static string ProcessingMessage([ActivityTrigger] GrooveItem cust, ILogger log)
        {
            log.LogInformation($"Now processing transaction for {cust.firstname} for {cust.amount}");
            return $"Trying to store transaction for {cust.firstname} for {cust.amount}";
        }

        //This works.  To use it, call it above in CallActivityAsync:
        [FunctionName("GrooveDataTest")]
        public static string ProcessGrooveData([ActivityTrigger] GrooveItem cust, ILogger log)
        {
            log.LogInformation($"Received transaction for {cust.firstname} {cust.lastname}: {cust.email}");
            return $"{cust.firstname} had type {cust.grooveevent} for {cust.product_name} for ${cust.amount}";
        
        }

        [FunctionName("GrooveDataHandlerTest_HttpStart")]
        public static async Task<HttpResponseMessage> HttpStart(
            [HttpTrigger(AuthorizationLevel.Anonymous, "get", "post")] HttpRequestMessage req,
            [DurableClient] IDurableOrchestrationClient starter,
            ILogger log)
        {
                        
            var data = await req.Content.ReadAsAsync<GrooveItem>();
            
            // Function input comes from the request content.
            string instanceId = await starter.StartNewAsync("GrooveOrchestrationTest", data);

            log.LogInformation($"Started orchestration with ID = '{instanceId}'.");

            return starter.CreateCheckStatusResponse(req, instanceId);
        }
    }
}

I am testing using Postman, sending JSON data: {"event":"sales","buyer_first_name":"Testy","buyer_last_name":"2","buyer_email":"testy@bugmenot.com","product_name":"Great Product","amount":"99.99"}我正在使用 Postman 进行测试,发送 JSON 数据: {"event":"sales","buyer_first_name":"Testy","buyer_last_name":"2","buyer_email":"testy@bugmenot.com","product_name":"Great Product","amount":"99.99"}

Here is my activity log when running in Visual Studio Code, which, as near as I can tell, shows that it's only called once:这是我在 Visual Studio Code 中运行时的活动日志,据我所知,它只被调用一次:

[8/10/2020 2:19:08 PM] Executing 'GrooveOrchestrationTest' (Reason='', Id=064dd243-fccc-4d29-9c1e-b803d0aaf87f)
[8/10/2020 2:19:08 PM] 6ea660aef5924d0ebfe70d6dbfd52742: Function 'GrooveOrchestrationTest (Orchestrator)' started. IsReplay: False. Input: (2920 bytes). State: Started. HubName: TestHubName. AppName: . SlotName: . ExtensionVersion: 2.1.1. SequenceNumber: 3.
[8/10/2020 2:19:08 PM] 6ea660aef5924d0ebfe70d6dbfd52742: Function 'NowProcessingMessage (Activity)' scheduled. Reason: GrooveOrchestrationTest. IsReplay: False. State: Scheduled. HubName: TestHubName. AppName: . SlotName: . ExtensionVersion: 2.1.1. SequenceNumber: 4.
[8/10/2020 2:19:08 PM] Executed 'GrooveOrchestrationTest' (Succeeded, Id=064dd243-fccc-4d29-9c1e-b803d0aaf87f)
[8/10/2020 2:19:08 PM] 6ea660aef5924d0ebfe70d6dbfd52742: Function 'GrooveOrchestrationTest (Orchestrator)' awaited. IsReplay: False. State: Awaited. HubName: TestHubName. AppName: . SlotName: . ExtensionVersion: 2.1.1. SequenceNumber: 5.
[8/10/2020 2:19:08 PM] 6ea660aef5924d0ebfe70d6dbfd52742: Function 'NowProcessingMessage (Activity)' started. IsReplay: False. Input: (3088 bytes). State: Started. HubName: TestHubName. AppName: . SlotName: . ExtensionVersion: 2.1.1. SequenceNumber: 6.
[8/10/2020 2:19:08 PM] Executing 'NowProcessingMessage' (Reason='', Id=057e5131-f212-4234-a3ae-e2f504814146)
[8/10/2020 2:19:08 PM] Now processing transaction for Testy for 99.99
[8/10/2020 2:19:08 PM] Executed 'NowProcessingMessage' (Succeeded, Id=057e5131-f212-4234-a3ae-e2f504814146)
[8/10/2020 2:19:08 PM] 6ea660aef5924d0ebfe70d6dbfd52742: Function 'NowProcessingMessage (Activity)' completed. ContinuedAsNew: False. IsReplay: False. Output: (196 bytes). State: Completed. HubName: TestHubName. AppName: . SlotName: . ExtensionVersion: 2.1.1. SequenceNumber: 7.
[8/10/2020 2:19:08 PM] Executing 'GrooveOrchestrationTest' (Reason='', Id=9c7ae8df-8f48-451c-a021-7f54167f5be9)
[8/10/2020 2:19:08 PM] 6ea660aef5924d0ebfe70d6dbfd52742: Function 'GrooveDataTest (Activity)' scheduled. Reason: GrooveOrchestrationTest. IsReplay: False. State: Scheduled. HubName: TestHubName. AppName: . SlotName: . ExtensionVersion: 2.1.1. SequenceNumber: 8.
[8/10/2020 2:19:08 PM] Executed 'GrooveOrchestrationTest' (Succeeded, Id=9c7ae8df-8f48-451c-a021-7f54167f5be9)
[8/10/2020 2:19:09 PM] 6ea660aef5924d0ebfe70d6dbfd52742: Function 'GrooveOrchestrationTest (Orchestrator)' awaited. IsReplay: False. State: Awaited. HubName: TestHubName. AppName: . SlotName: . ExtensionVersion: 2.1.1. SequenceNumber: 9.
[8/10/2020 2:19:09 PM] 6ea660aef5924d0ebfe70d6dbfd52742: Function 'GrooveDataTest (Activity)' started. IsReplay: False. Input: (3236 bytes). State: Started. HubName: TestHubName. AppName: . SlotName: . ExtensionVersion: 2.1.1. SequenceNumber: 10.
[8/10/2020 2:19:09 PM] Executing 'GrooveDataTest' (Reason='', Id=8f70954c-64ac-48f5-83ce-ca3b89d11bcf)
[8/10/2020 2:19:09 PM] Received transaction for Testy 1: testy@bugmenot.com
[8/10/2020 2:19:09 PM] Executed 'GrooveDataTest' (Succeeded, Id=8f70954c-64ac-48f5-83ce-ca3b89d11bcf)
[8/10/2020 2:19:09 PM] 6ea660aef5924d0ebfe70d6dbfd52742: Function 'GrooveDataTest (Activity)' completed. ContinuedAsNew: False. IsReplay: False. Output: (204 bytes). State: Completed. HubName: TestHubName. AppName: . SlotName: . ExtensionVersion: 2.1.1. SequenceNumber: 11.
[8/10/2020 2:19:09 PM] Executing 'GrooveOrchestrationTest' (Reason='', Id=8656fff8-6b49-4fdd-93ef-38959e7af01f)
[8/10/2020 2:19:09 PM] 6ea660aef5924d0ebfe70d6dbfd52742: Function 'GrooveOrchestrationTest (Orchestrator)' completed. ContinuedAsNew: False. IsReplay: False. Output: (412 bytes). State: Completed. HubName: TestHubName. AppName: . SlotName: . ExtensionVersion: 2.1.1. SequenceNumber: 12.
[8/10/2020 2:19:09 PM] Executed 'GrooveOrchestrationTest' (Succeeded, Id=8656fff8-6b49-4fdd-93ef-38959e7af01f)

UPDATE I just read that the Orchestrator should not use any randomization such as calling the GUID function, which I am doing to create a ROWKEY value.更新我刚刚读到,Orchestrator 不应使用任何随机化,例如调用 GUID function,我正在这样做以创建 ROWKEY 值。 Perhaps this is related?也许这是相关的? I tried moving it into the HTTPSTART, but that generated an actual error that it already exists.我尝试将它移到 HTTPSTART 中,但这会产生一个它已经存在的实际错误。 Perhaps there is another way I can get that into the item without running afoul of this limitation?也许还有另一种方法可以在不违反此限制的情况下将其放入项目中?

SOLUTION The answer provided by user1672994 is right, but Method #1 as originally posted had to be adjusted slightly so it would work.解决方案user1672994 提供的答案是正确的,但最初发布的方法#1 必须稍作调整才能正常工作。 This is the fixed new code:这是固定的新代码:

        [FunctionName("AddGrooveDataAsync2")]
        public static async Task<String> Run([ActivityTrigger] GrooveItem trans, [Table ("GrooveData")] IAsyncCollector<GrooveItem> GrooveData, ILogger log)
        {
        // RowKey and PartitionKey are required by Azure, but we don't get them from Groove and must provide them:
            trans.RowKey = Guid.NewGuid ().ToString ();
            trans.PartitionKey = trans.grooveevent;

            // and this stores the data:
            await GrooveData.AddAsync (trans);
            return $"Added {trans.firstname} {trans.lastname}: {trans.email}";
        }

The problem here is that you are adding the data into table directly by Orchestrator function which will be replayed multiple times until finishes.这里的问题是您正在通过 Orchestrator function 直接将数据添加到表中,这将被重放多次,直到完成。 You can fix this issue by following approach.您可以通过以下方法解决此问题。 I would recommend you to for approach 1.我建议您使用方法1。

Approach 1: Convert the logic to activity function which won't be called again if message is replaying.方法1:将逻辑转换为活动function,如果消息正在重播,则不会再次调用该活动。

    [FunctionName("AddGrooveDataAsync")]
    public static async Task<string> AddGrooveDataAsync([ActivityTrigger] GrooveItem cust, [Table ("GrooveData")] IAsyncCollector<GrooveItem> GrooveData, ILogger log)
    {
        // RowKey and PartitionKey are required by Azure, but we don't get them from Groove and must provide them:
            data.RowKey = Guid.NewGuid ().ToString ();
            data.PartitionKey = data.grooveevent;

            // and this stores the data:
            await GrooveData.AddAsync (data);
    }

Approach 2: Add the context.IsReplaying check.方法 2:添加context.IsReplaying检查。

   [FunctionName("GrooveOrchestrationTest")]
    public static async Task<List<string>> RunOrchestrator(
        [OrchestrationTrigger] IDurableOrchestrationContext context,
        [Table ("GrooveData")] IAsyncCollector<GrooveItem> GrooveData)
    {
        var outputs = new List<string>();
        GrooveItem data = context.GetInput<GrooveItem>();      

            // Indicate we got the data:
            outputs.Add(await context.CallActivityAsync<string>("NowProcessingMessage", data));
             
            if (!context.IsReplaying)
            {
                  // RowKey and PartitionKey are required by Azure, but we don't get them from Groove and must provide them:
                  data.RowKey = Guid.NewGuid ().ToString ();
                  data.PartitionKey = data.grooveevent;
            }

            // and this stores the data:
            await GrooveData.AddAsync (data);

            //If we get this far, it probably worked :D
            outputs.Add(await context.CallActivityAsync<string>("GrooveDataTest", data));

        return outputs;
    }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM