在本文中,我們會詳細描述使用 .NET 開發持久性實體的可用介面,包括範例和一般建議。
實體函式為無伺服器應用程式開發人員提供一種便利方式,將應用程式狀態組織為精細實體的集合。 如需基礎概念的詳細資料,請參閱持久性實體:概念一文。
我們目前提供兩個用於定義實體的 API:
類別型語法可將實體和作業表示為類別和方法。 此語法會產生易於讀取的程式碼,並允許透過介面以檢查類型的方式叫用作業。
函式型語法是較低層級的介面,可將實體表示為函式。 其可讓您精確控制實體作業的分派方式,以及實體狀態的管理方式。
本文主要著重於類別型語法,因為我們預期其更加適合大部分的應用程式。 不過,函式型語法可以適用於想要針對實體狀態和作業本身定義或管理其自己抽象的應用程式。 此外,其可能適用於實作需要泛型的程式庫,而類別型語法目前不支援此泛型。
注意
類別型語法只是函式型語法之上的一層,因此這兩個變體可以在相同的應用程式中交換使用。
定義實體類別
下列範例是 Counter 實體的實作,該實體會儲存整數類型的單一值,並提供四個作業 Add、Reset、Get 和 Delete。
[JsonObject(MemberSerialization.OptIn)]
public class Counter
{
[JsonProperty("value")]
public int Value { get; set; }
public void Add(int amount)
{
this.Value += amount;
}
public Task Reset()
{
this.Value = 0;
return Task.CompletedTask;
}
public Task<int> Get()
{
return Task.FromResult(this.Value);
}
public void Delete()
{
Entity.Current.DeleteState();
}
[FunctionName(nameof(Counter))]
public static Task Run([EntityTrigger] IDurableEntityContext ctx)
=> ctx.DispatchAsync<Counter>();
}
Run 函式包含使用類別型語法所需的樣板。 其必須是「靜態」Azure 函式。 其會針對實體所處理的每個作業訊息執行一次。 呼叫 DispatchAsync<T> 且實體尚未在記憶體中時,其會建構類型 T 的物件,並從儲存體中找到的最後一個保存 JSON 中填入其欄位 (若有的話)。 然後,其會使用相符的名稱叫用方法。
此範例中的 EntityTrigger 函式 Run 不需要位於實體類別本身之中。 它可以位於 Azure 函式的任何有效位置:在最上層命名空間內,或最上層類別內。 不過,如果巢狀結構更深 (例如,函式是在巢狀類別內宣告),則最新的執行階段無法辨識此函式。
注意
在類別型實體處理作業之前,其狀態會隱式建立,並且可以藉由呼叫 Entity.Current.DeleteState()。
注意
您需要 Azure Functions Core Tools4.0.5455 版或更新版本,才能在隔離模型中執行實體。
有兩種方式可將實體定義為 C# 隔離式背景工作角色模型中的類別。 它們會產生具有不同狀態序列化結構的實體。
使用下列方法,整個物件會在定義實體時序列化。
public class Counter
{
public int Value { get; set; }
public void Add(int amount)
{
this.Value += amount;
}
public Task Reset()
{
this.Value = 0;
return Task.CompletedTask;
}
public Task<int> Get()
{
return Task.FromResult(this.Value);
}
// Delete is implicitly defined when defining an entity this way
[Function(nameof(Counter))]
public static Task Run([EntityTrigger] TaskEntityDispatcher dispatcher)
=> dispatcher.DispatchAsync<Counter>();
}
TaskEntity<TState> 型實作,可讓您輕鬆使用相依性插入。 在此情況下,狀態會還原序列化為 State 屬性,且不會有任何其他屬性序列化/還原序列化。
public class Counter : TaskEntity<int>
{
readonly ILogger logger;
public Counter(ILogger<Counter> logger)
{
this.logger = logger;
}
public void Add(int amount)
{
this.State += amount;
}
public Task Reset()
{
this.State = 0;
return Task.CompletedTask;
}
public Task<int> Get()
{
return Task.FromResult(this.State);
}
// Delete is implicitly defined when defining an entity this way
[Function(nameof(Counter))]
public static Task Run([EntityTrigger] TaskEntityDispatcher dispatcher)
=> dispatcher.DispatchAsync<Counter>();
}
警告
撰寫衍生自 ITaskEntity 或 TaskEntity<TState>的實體時,請務必 不要 將實體觸發程式方法 RunAsync命名為 。 這會導致叫用實體時發生執行時錯誤,因為方法名稱「RunAsync」有模稜兩可的相符,原因是ITaskEntity已經定義了實例級的「RunAsync」。
刪除隔離模型中的實體
將實體狀態設定為 null來完成刪除隔離模型中的實體,而此程式取決於所使用的實體實作路徑:
- 從
ITaskEntity衍生或使用函式型語法時,可藉由呼叫TaskEntityOperation.State.SetState(null)來完成刪除。 - 從
TaskEntity<TState>衍生時,則已隱含定義刪除。 不過,您可以在實體上定義方法Delete來覆寫它。 您也可以透過this.State = null從任何作業中刪除狀態。- 若要藉由將狀態設定為 null 來刪除,
TState必須是可為 Null。 - 隱含定義的刪除作業會刪除不可為 Null 的
TState。
- 若要藉由將狀態設定為 null 來刪除,
- 使用 POCO 作為您的狀態時 (不是從
TaskEntity<TState>衍生),已隱含定義刪除。 您可以在 POCO 上定義方法Delete,以覆寫刪除作業。 不過,在 POCO 路由中沒有將狀態設定為null的方法,因此隱含定義的刪除作業是唯一真正的刪除。
類別需求
實體類別是不需要特殊超級類別、介面或屬性的 POCO(一般舊 CLR 物件)。 但是:
此外,任何叫用為作業的方法都必須滿足其他需求:
- 作業最多必須有一個自變數,但沒有任何多載或泛型類型自變數。
- 要使用介面從協調流程呼叫的作業必須傳回
Task或Task<T>。 - 引數和傳回值必須是可序列化的值或物件。
作業可以做什麼?
所有實體作業都可以讀取和更新實體狀態,而且狀態的變更會自動保存到儲存體。 此外,作業可以在所有 Azure Functions 通用的一般限制內執行外部 I/O 或其他計算。
作業也可以存取 Entity.Current 內容所提供的功能:
-
EntityName:目前執行中實體的名稱。 -
EntityKey:目前執行中實體的金鑰。 -
EntityId:目前執行中實體的識別碼 (包括名稱和金鑰)。 -
SignalEntity:將單向訊息傳送至實體。 -
CreateNewOrchestration:啟動新的協調流程。 -
DeleteState:刪除此實體的狀態。
例如,我們可以修改計數器實體,讓其在計數器達到 100 時啟動協調流程,並將實體識別碼當作輸入引數傳遞:
public void Add(int amount)
{
if (this.Value < 100 && this.Value + amount >= 100)
{
Entity.Current.StartNewOrchestration("MilestoneReached", Entity.Current.EntityId);
}
this.Value += amount;
}
直接存取實體
您可以直接存取類別型實體,方法是將明確字串名稱用於實體及其作業的。 本節提供範例。 如需基礎概念 (例如訊號與呼叫) 的更深入說明,請參閱存取實體中的討論。
注意
可能的話,您應透過介面存取實體,因為可提供更多類型檢查。
範例:用戶端訊號實體
下列 Azure Http 函式會使用 REST 慣例來實作 DELETE 作業。 其會將刪除訊號傳送至其金鑰在 URL 路徑中傳遞的計數器實體。
[FunctionName("DeleteCounter")]
public static async Task<HttpResponseMessage> DeleteCounter(
[HttpTrigger(AuthorizationLevel.Function, "delete", Route = "Counter/{entityKey}")] HttpRequestMessage req,
[DurableClient] IDurableEntityClient client,
string entityKey)
{
var entityId = new EntityId("Counter", entityKey);
await client.SignalEntityAsync(entityId, "Delete");
return req.CreateResponse(HttpStatusCode.Accepted);
}
範例:用戶端讀取實體狀態
下列 Azure HTTP 函式會使用 REST 慣例來實作 GET 作業。 此函式會讀取其金鑰在 URL 路徑中傳遞的計數器實體目前狀態。
[FunctionName("GetCounter")]
public static async Task<HttpResponseMessage> GetCounter(
[HttpTrigger(AuthorizationLevel.Function, "get", Route = "Counter/{entityKey}")] HttpRequestMessage req,
[DurableClient] IDurableEntityClient client,
string entityKey)
{
var entityId = new EntityId("Counter", entityKey);
var state = await client.ReadEntityStateAsync<Counter>(entityId);
return req.CreateResponse(state);
}
注意
ReadEntityStateAsync 傳回的物件只是本機複本,也就是來自某些先前時間點的實體狀態快照集。 特別要注意的是,它可能過時,而且修改此物件不會影響實際實體。
範例:協調流程會先發出訊號,然後呼叫實體
下列協調流程會對計數器實體發出訊號來將其遞增,然後呼叫相同的實體來讀取其最新值。
[FunctionName("IncrementThenGet")]
public static async Task<int> Run(
[OrchestrationTrigger] IDurableOrchestrationContext context)
{
var entityId = new EntityId("Counter", "myCounter");
// One-way signal to the entity - does not await a response
context.SignalEntity(entityId, "Add", 1);
// Two-way call to the entity which returns a value - awaits the response
int currentValue = await context.CallEntityAsync<int>(entityId, "Get");
return currentValue;
}
範例:用戶端訊號實體
下列 Azure HTTP 函式會使用 REST 慣例來實作 DELETE 作業。 其會將刪除訊號傳送至其金鑰在 URL 路徑中傳遞的計數器實體。
[Function("DeleteCounter")]
public static async Task<HttpResponseData> DeleteCounter(
[HttpTrigger(AuthorizationLevel.Function, "delete", Route = "Counter/{entityKey}")] HttpRequestData req,
[DurableClient] DurableTaskClient client, string entityKey)
{
var entityId = new EntityInstanceId("Counter", entityKey);
await client.Entities.SignalEntityAsync(entityId, "Delete");
return req.CreateResponse(HttpStatusCode.Accepted);
}
範例:用戶端讀取實體狀態
下列 Azure HTTP 函式會使用 REST 慣例來實作 GET 作業。 此函式會讀取其金鑰在 URL 路徑中傳遞的計數器實體目前狀態。
[Function("GetCounter")]
public static async Task<HttpResponseData> GetCounter(
[HttpTrigger(AuthorizationLevel.Function, "get", Route = "Counter/{entityKey}")] HttpRequestData req,
[DurableClient] DurableTaskClient client, string entityKey)
{
var entityId = new EntityInstanceId("Counter", entityKey);
EntityMetadata<int>? entity = await client.Entities.GetEntityAsync<int>(entityId);
HttpResponseData response = request.CreateResponse(HttpStatusCode.OK);
await response.WriteAsJsonAsync(entity.State);
return response;
}
範例:協調流程會先發出訊號,然後呼叫實體
下列協調流程會對計數器實體發出訊號來將其遞增,然後呼叫相同的實體來讀取其最新值。
[Function("IncrementThenGet")]
public static async Task<int> Run([OrchestrationTrigger] TaskOrchestrationContext context)
{
var entityId = new EntityInstanceId("Counter", "myCounter");
// One-way signal to the entity - does not await a response
await context.Entities.SignalEntityAsync(entityId, "Add", 1);
// Two-way call to the entity which returns a value - awaits the response
int currentValue = await context.Entities.CallEntityAsync<int>(entityId, "Get");
return currentValue;
}
透過介面存取實體
介面可以用來透過產生的 Proxy 物件存取實體。 此方法可確保作業的名稱和自變數類型符合實作的專案。 建議您盡可能使用介面來存取實體。
例如,我們可以修改計數器範例:
public interface ICounter
{
void Add(int amount);
Task Reset();
Task<int> Get();
void Delete();
}
public class Counter : ICounter
{
...
}
實體類別和實體介面類似於 Orleans 所推廣的粒紋和粒紋介面。 如需持久性實體與 Orlean 之間相似性和差異的詳細資訊,請參閱與虛擬執行者的比較。
除了提供類型檢查之外,介面也有助於更清楚地區隔應用程式內的考量。 例如,由於一個實體可以實作多個介面,因此單一實體可以提供多個角色。 此外,由於多個實體可以實作介面,因此一般通訊模式可以實作為可重複使用的連結庫。
範例:用戶端透過介面對實體發出訊號
用戶端程式碼可以使用 SignalEntityAsync<TEntityInterface> 將訊號傳送至實作 TEntityInterface 的實體。 例如:
[FunctionName("DeleteCounter")]
public static async Task<HttpResponseMessage> DeleteCounter(
[HttpTrigger(AuthorizationLevel.Function, "delete", Route = "Counter/{entityKey}")] HttpRequestMessage req,
[DurableClient] IDurableEntityClient client,
string entityKey)
{
var entityId = new EntityId("Counter", entityKey);
await client.SignalEntityAsync<ICounter>(entityId, proxy => proxy.Delete());
return req.CreateResponse(HttpStatusCode.Accepted);
}
在此範例中,proxy 參數是動態產生的 ICounter 執行個體,其會在內部將 Delete 的呼叫轉譯為訊號。
注意
SignalEntityAsync API 只能用於單向作業。 即使作業回傳 Task<T>,T 參數的值一律為 Null 或 default 而非實際結果。 例如,對 Get 作業發出訊號並不合理,因為它不會傳回值。 相反地,用戶端可以使用 ReadStateAsync 直接存取計數器狀態,或啟動呼叫作業的 Get 協調器函式。
範例:協調流程會先發出訊號,然後透過 Proxy 呼叫實體
若要從協調流程內呼叫實體或發出訊號,CreateEntityProxy 可與介面類型搭配使用來產生實體的 Proxy。 然後,此 Proxy 可以用來呼叫作業或發出訊號:
[FunctionName("IncrementThenGet")]
public static async Task<int> Run(
[OrchestrationTrigger] IDurableOrchestrationContext context)
{
var entityId = new EntityId("Counter", "myCounter");
var proxy = context.CreateEntityProxy<ICounter>(entityId);
// One-way signal to the entity - does not await a response
proxy.Add(1);
// Two-way call to the entity which returns a value - awaits the response
int currentValue = await proxy.Get();
return currentValue;
}
隱含地對任何傳回 void 的作業發出訊號,也會隱含地呼叫任何傳回 Task 或 Task<T> 的作業。 即使其明確使用 SignalEntity<IInterfaceType> 方法傳回工作,也可以變更此預設行為和訊號作業。
較短選項用於指定目標
使用介面呼叫實體或發出訊號時,第一個引數必須指定目標實體。 當只有一個實作實體的類別時,可以藉由定義實體標識碼或只指定實體索引鍵來指定目標:
context.SignalEntity<ICounter>(new EntityId(nameof(Counter), "myCounter"), ...);
context.SignalEntity<ICounter>("myCounter", ...);
如果僅指定實體金鑰,而且在執行階段找不到唯一的實作,則會擲回 InvalidOperationException。
實體介面的限制
一如往常,所有參數和傳回型別都必須是可 JSON 序列化。 否則,會在執行階段擲回序列化例外狀況。 下列規則也適用於:
- 實體介面必須與實體類別定義在相同的組件中。
- 實體介面必須只定義方法。
- 實體介面不得包含泛型參數。
- 實體介面方法不得具有多個參數。
- 實體介面方法必須傳回
voidTask或Task<T>。
如果違反上述任何規則,則在介面用作 InvalidOperationException、SignalEntity 或 SignalEntityAsync 的類型引數時,會在執行階段擲回 CreateEntityProxy。 例外狀況訊息說明違反了哪個規則。
注意
傳回 void 的介面方法只能發出訊號 (單向),而不能進行呼叫 (雙向)。 返回Task或Task<T>的介面方法可以被呼叫或發出訊號。 如果呼叫,這些函式會傳回作業的結果,或重新擲回由作業擲出的例外狀況。 如果收到訊號,它們會傳回預設值,而不是作業的實際結果或例外狀況。
.NET 隔離背景工作角色目前不支援此功能。
實體序列化
由於實體的狀態會永久保存,因此實體類別必須可序列化。 Durable Functions 執行階段會針對此目的使用 Json.NET 程式庫,這會支援原則和屬性來控制序列化和還原序列化流程。 最常使用的 C# 資料類型 (包括陣列和集合類型) 已可序列化,而且可以輕鬆地用於定義持久性實體的狀態。
例如,Json.NET 可以輕鬆地序列化和還原序列化下列類別:
[JsonObject(MemberSerialization = MemberSerialization.OptIn)]
public class User
{
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("yearOfBirth")]
public int YearOfBirth { get; set; }
[JsonProperty("timestamp")]
public DateTime Timestamp { get; set; }
[JsonProperty("contacts")]
public Dictionary<Guid, Contact> Contacts { get; set; } = new Dictionary<Guid, Contact>();
[JsonObject(MemberSerialization = MemberSerialization.OptOut)]
public struct Contact
{
public string Name;
public string Number;
}
...
}
序列化屬性
在上述範例中,我們包含數個屬性,讓基礎串行化更加可見:
- 我們會使用
[JsonObject(MemberSerialization.OptIn)]標註類別,以提醒我們類別必須可序列化,並且只保存明確標示為 JSON 屬性的成員。 - 我們會使用
[JsonProperty("name")]標註要被持久化的欄位,以提醒我們該欄位屬於被持久化的實體狀態的一部分,並指定在 JSON 表示法中使用的屬性名稱。
不過,這些屬性不是必要的;只要其他慣例或屬性使用 Json.NET,就允許它們。 例如,一個人可以使用 [DataContract] 屬性或完全沒有屬性:
[DataContract]
public class Counter
{
[DataMember]
public int Value { get; set; }
...
}
public class Counter
{
public int Value;
...
}
根據預設,類別的名稱不會*儲存為 JSON 表示法的一部分:亦即,我們會使用 TypeNameHandling.None 作為預設設定。 您可以使用 JsonObject 或 JsonProperty 屬性覆寫此預設行為。
對類別定義進行變更
當應用程式執行之後對類別定義進行變更時,有些需要小心,因為預存的 JSON 對象無法再符合新的類別定義。 只要了解JsonConvert.PopulateObject使用的反序列化過程,通常能夠正確處理已變更的數據格式。 以下是變更及其影響的範例:
- 新增未存在於預存 JSON 中的新屬性時,它會假設其預設值。
- 拿掉儲存 JSON 中的屬性時,會遺失先前的內容。
- 重新命名屬性時,效果就是移除舊的屬性並新增新的屬性。
- 當屬性類型變更,因此無法從儲存的 JSON 還原串行化時,就會擲回例外狀況。
- 當屬性類型變更後仍然能從預存的 JSON 進行反序列化時,便會進行反序列化。
有許多選項可用於自訂 Json.NET 的行為。 例如,若要在預存 JSON 包含類別中不存在的欄位時強制發生例外狀況,請指定屬性 JsonObject(MissingMemberHandling = MissingMemberHandling.Error)。 您也可以撰寫自訂程式碼進行還原序列化,讀取以任意格式儲存的 JSON。
序列化預設行為已從 Newtonsoft.Json 變更為 System.Text.Json。 如需詳細資訊,請參閱 自定義串行化和還原串行化。
實體建構
有時候我們想要對實體物件的建構方式施加更多控制。 我們現在描述數個選項,您可以在建構實體物件時用於變更預設行為。
第一次存取時進行自訂初始化
有時候,我們需要先執行一些特殊初始化,然後再將作業分派至從未存取或已刪除的實體。 若要指定此行為,可以在 DispatchAsync 之前新增條件:
[FunctionName(nameof(Counter))]
public static Task Run([EntityTrigger] IDurableEntityContext ctx)
{
if (!ctx.HasState)
{
ctx.SetState(...);
}
return ctx.DispatchAsync<Counter>();
}
實體類別中的繫結
不同於一般函式,實體類別方法無法直接存取輸入和輸出繫結。 相反地,其必須在進入點函式宣告中擷取繫結資料,然後再傳遞至 DispatchAsync<T> 方法。 傳遞至 DispatchAsync<T> 的任何物件都會自動傳遞至實體類別建構函式做為自變數。
下列範例會說明如何讓以類別為基礎的實體可以使用來自 CloudBlobContainer的 參考。
public class BlobBackedEntity
{
[JsonIgnore]
private readonly CloudBlobContainer container;
public BlobBackedEntity(CloudBlobContainer container)
{
this.container = container;
}
// ... entity methods can use this.container in their implementations ...
[FunctionName(nameof(BlobBackedEntity))]
public static Task Run(
[EntityTrigger] IDurableEntityContext context,
[Blob("my-container", FileAccess.Read)] CloudBlobContainer container)
{
// passing the binding object as a parameter makes it available to the
// entity class constructor
return context.DispatchAsync<BlobBackedEntity>(container);
}
}
如需 Azure Functions 中系結的詳細資訊,請參閱 Azure Functions 觸發程式和系結概念。
在實體類別中插入相依性
實體類別支援 Azure Functions 相依性插入。 下列範例會示範如何將 IHttpClientFactory 服務註冊到以類別為基礎的實體。
[assembly: FunctionsStartup(typeof(MyNamespace.Startup))]
namespace MyNamespace
{
public class Startup : FunctionsStartup
{
public override void Configure(IFunctionsHostBuilder builder)
{
builder.Services.AddHttpClient();
}
}
}
下列代碼段示範如何將插入的服務併入您的實體類別:
public class HttpEntity
{
[JsonIgnore]
private readonly HttpClient client;
public HttpEntity(IHttpClientFactory factory)
{
this.client = factory.CreateClient();
}
public Task<int> GetAsync(string url)
{
using (var response = await this.client.GetAsync(url))
{
return (int)response.StatusCode;
}
}
[FunctionName(nameof(HttpEntity))]
public static Task Run([EntityTrigger] IDurableEntityContext ctx)
=> ctx.DispatchAsync<HttpEntity>();
}
第一次存取時進行自訂初始化
public class Counter : TaskEntity<int>
{
protected override int InitializeState(TaskEntityOperation operation)
{
// This is called when state is null, giving a chance to customize first-access of entity.
return 10;
}
}
實體類別中的繫結
下列範例示範如何在類別型實體中使用 Blob 輸入繫結。
public class BlobBackedEntity : TaskEntity<object?>
{
private BlobContainerClient Container { get; set; }
[Function(nameof(BlobBackedEntity))]
public Task DispatchAsync(
[EntityTrigger] TaskEntityDispatcher dispatcher,
[BlobInput("my-container")] BlobContainerClient container)
{
this.Container = container;
return dispatcher.DispatchAsync(this);
}
}
如需 Azure Functions 中繫結的詳細資訊,請參閱 Azure Functions 觸發程序和繫結文件。
在實體類別中插入相依性
實體類別支援 Azure Functions 相依性插入。
下列範例示範如何在 HttpClient 檔案中設定 program.cs,以便稍後在實體類別中匯入。
public class Program
{
public static void Main()
{
IHost host = new HostBuilder()
.ConfigureFunctionsWorkerDefaults((IFunctionsWorkerApplicationBuilder workerApplication) =>
{
workerApplication.Services.AddHttpClient<HttpEntity>()
.ConfigureHttpClient(client => {/* configure http client here */});
})
.Build();
host.Run();
}
}
下列範例示範如何將插入的服務併入您的實體類別:
public class HttpEntity : TaskEntity<object?>
{
private readonly HttpClient client;
public HttpEntity(HttpClient client)
{
this.client = client;
}
public async Task<int> GetAsync(string url)
{
using var response = await this.client.GetAsync(url);
return (int)response.StatusCode;
}
[Function(nameof(HttpEntity))]
public static Task Run([EntityTrigger] TaskEntityDispatcher dispatcher)
=> dispatcher.DispatchAsync<HttpEntity>();
}
注意
若要避免串行化的問題,請務必排除從串行化中儲存插入值的字段。
注意
不同於在一般的 .NET Azure Functions 中使用建構函式插入時,以類別為基礎的實體「必須」將函式進入點方法宣告為 static。 宣告不是靜態的函式進入點可能會導致一般 Azure Functions 物件初始化運算式與 Durable Entities 物件初始化表達式之間的衝突。
函式型語法
到目前為止,本文主著重於類別型語法,因為我們預期其更加適合大部分的應用程式。 不過,函式型語法可以適用於想要針對實體狀態和作業本身定義或管理其自己抽象的應用程式。 此外,其可能適用於實作需要泛型的程式庫時,而類別型語法目前不支援此泛型。
使用函式型語法,實體函式會明確處理作業分派,並明確管理實體的狀態。 例如,下列程式碼會顯示使用函式型語法實作的「計數器」實體。
[FunctionName("Counter")]
public static void Counter([EntityTrigger] IDurableEntityContext ctx)
{
switch (ctx.OperationName.ToLowerInvariant())
{
case "add":
ctx.SetState(ctx.GetState<int>() + ctx.GetInput<int>());
break;
case "reset":
ctx.SetState(0);
break;
case "get":
ctx.Return(ctx.GetState<int>());
break;
case "delete":
ctx.DeleteState();
break;
}
}
實體內容物件
實體特定功能可以透過 IDurableEntityContext 類型的內容物件來存取。 此內容物件可用作實體函式的參數,以及透過 async-local 屬性 Entity.Current 取得。
下列成員提供目前作業的相關信息,並協助指定傳回值:
-
EntityName:目前執行中實體的名稱 -
EntityKey:目前執行實體的鍵 -
EntityId:目前執行實體的識別碼(包括名稱和金鑰) -
OperationName:目前作業的名稱 -
GetInput<TInput>():取得目前作業的輸入 -
Return(arg):將運算結果傳回至呼叫此作業的協調程序
下列成員會管理實體的狀態(建立、讀取、更新、刪除):
-
HasState:如果實體存在,則為 ;亦即,有一些狀態 -
GetState<TState>():取得實體的目前狀態,如果實體不存在,則會建立一個 -
SetState(arg):建立或更新實體的狀態 -
DeleteState():如果實體存在,則刪除實體的狀態
如果 傳 GetState 回的狀態是 物件,應用程式程式代碼就可以修改它。 不需要在結束時再次呼叫 SetState (但呼叫也不會造成任何損害)。 如果多次呼叫 GetState<TState>,則必須使用相同的類型。
最後,下列成員會向其他實體發出訊號,或啟動新的協調流程:
-
SignalEntity(EntityId, operation, input):將單向訊息傳送至實體 -
CreateNewOrchestration(orchestratorFunctionName, input):啟動新的編排流程
[Function(nameof(Counter))]
public static Task DispatchAsync([EntityTrigger] TaskEntityDispatcher dispatcher)
{
return dispatcher.DispatchAsync(operation =>
{
if (operation.State.GetState(typeof(int)) is null)
{
operation.State.SetState(0);
}
switch (operation.Name.ToLowerInvariant())
{
case "add":
int state = operation.State.GetState<int>();
state += operation.GetInput<int>();
operation.State.SetState(state);
return new(state);
case "reset":
operation.State.SetState(0);
break;
case "get":
return new(operation.State.GetState<int>());
case "delete":
operation.State.SetState(null);
break;
}
return default;
});
}