耐用实体——反例



Total Azure Functions新手,但我觉得我已经花了好几天时间自己研究了,只是缺少了一些东西。我正在创建一个简单的计数器实体,可以用于生成订单跟踪号码:

入口点:

public static class Counter
{
[FunctionName("GetTrackingNumber")]
public static async Task<IActionResult> Get(
[HttpTrigger(AuthorizationLevel.Function, "get", Route = "GetTrackingNumber")] HttpRequest req,
[DurableClient] IDurableEntityClient client,
ILogger log
)
{
var entityId = new EntityId(nameof(CounterEntity), "myCounter");
await client.SignalEntityAsync<ICounterEntity>(entityId, proxy => proxy.Add(1));
var stateResponse = await client.ReadEntityStateAsync<CounterEntity>(entityId);
string trackingNumber = "";
if(stateResponse.EntityExists)
{
trackingNumber = await stateResponse.EntityState.GetNextTrackingNumber();
}
return new OkObjectResult(trackingNumber);            
}
}

计数器实体:

public interface ICounterEntity
{
[Deterministic]
public void Add(int amount);
[Deterministic]
public Task<string> GetNextTrackingNumber();
[Deterministic]
public Task Reset();
}
[JsonObject(MemberSerialization.OptIn)]
public class CounterEntity : ICounterEntity
{
private readonly Random _random = new Random();
[JsonProperty("value")]
public int Value { get; set; }
[JsonProperty("prefix")]
public string Prefix { get; set; }
[JsonProperty("prefixList")]
public List<String> PrefixList { get; set; }
public CounterEntity()
{
PrefixList = new List<string>();
Prefix = RandomString(3);
PrefixList.Add(Prefix);
}
public void Add(int amount)
{
Value += amount;
}
public Task<string> GetNextTrackingNumber()
{
var thisTrackingNumber = String.Concat(Prefix, "-", string.Format("{0:00000000}", Value));
return Task.FromResult(thisTrackingNumber);
}
public Task Reset()
{
Value = 0;
Prefix = RandomString(3);
PrefixList.Add(Prefix);
return Task.CompletedTask;
}

public string RandomString(int size, bool lowerCase = false)
{
var builder = new StringBuilder(size);
for (var i = 0; i < size; i++)
{
var @char = (char)_random.Next(offset, offset + lettersOffset);
builder.Append(@char);
}
return lowerCase ? builder.ToString().ToLower() : builder.ToString();
}
[FunctionName(nameof(CounterEntity))]
public static Task Run([EntityTrigger] IDurableEntityContext ctx) => ctx.DispatchAsync<CounterEntity>();

}

我在Azure上发布了这个函数,看起来它是有效的(有点(,但我一点也不相信它是正确的。我第一次打电话给它时,得到的却是一片空白。第二天我运行它的前几次也发生了类似的事情——功能应用程序可能必须启动,这很好,但我在发送请求时收到的前两三个响应显示了昨晚输出的最后一个数字,然后它开始按预期增加。

有谁能在持久实体方面有一点经验,看看这件事,并提出我可能做错了什么吗?对此进行了近一周的搜索,但几乎一无所获。

谢谢你的帮助!!!

您可能会得到一个空响应,这是合理的。这里的问题是,您只是向实体发出信号,但不能保证在使用函数ReadEntityStateAsync检查实体状态时,它已经成功创建(它可能仍在内存中(。

ReadEntityStateAsync函数只返回某个较早时间点的实体状态的快照,但这种状态可能已过时。中持久实体的开发人员指南。NET页面上有一条注释:

ReadEntityStateAsync返回的对象只是一个本地副本是来自某个较早时间点的实体状态的快照。在里面特别是,它可能已经过时,修改此对象没有任何效果在实际实体上。

只有业务流程才能准确读取实体的状态。我以前使用的粗略解决方法(在一个HTTP调用中完成所有这些(是启动一个编排(使用IDurableOrchestrationClientStartNewAsync函数(,然后在该编排中调用实体(而不是发出信号(。您将在该业务流程中等待,以确保已创建实体。

StartNewAsync将返回一个instanceId,因此您可以在尝试中使用IDurableOrchestrationClientWaitForCompletionOrCreateCheckStatusResponseAsync函数来等待编排完成。只有这样,您才应该使用ReadEntityStateAsync读取实体状态

耐用函数扩展公开了内置的HTTP API。它还提供了用于与HTTP触发函数中的业务流程和实体交互的API。如果您不介意在客户端进行多次HTTP调用,那么您肯定可以想出更好的解决方案。

最新更新