[英]Two entries made in Azure blob table when adding a single row, using Durable Functions with C#
我正在研究概念验证,但我对 Azure 和 C# 都是新手。 下面的代码可以正常工作 - 它成功地将传入的 JSON 数据存储到 blob 表中。 问题是它每次都会创建 2 个不同的行,每个行都有自己唯一的 RowKey(我创建的 GUID),但是,如您所见,我只使用await GrooveData.AddAsync (data);
.
我错过了什么? 这两行的时间戳略有不同。
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);
}
}
}
我正在使用 Postman 进行测试,发送 JSON 数据: {"event":"sales","buyer_first_name":"Testy","buyer_last_name":"2","buyer_email":"testy@bugmenot.com","product_name":"Great Product","amount":"99.99"}
这是我在 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)
更新我刚刚读到,Orchestrator 不应使用任何随机化,例如调用 GUID function,我正在这样做以创建 ROWKEY 值。 也许这是相关的? 我尝试将它移到 HTTPSTART 中,但这会产生一个它已经存在的实际错误。 也许还有另一种方法可以在不违反此限制的情况下将其放入项目中?
解决方案user1672994 提供的答案是正确的,但最初发布的方法#1 必须稍作调整才能正常工作。 这是固定的新代码:
[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}";
}
这里的问题是您正在通过 Orchestrator function 直接将数据添加到表中,这将被重放多次,直到完成。 您可以通过以下方法解决此问题。 我建议您使用方法1。
方法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);
}
方法 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.