diff --git a/nuget.config b/nuget.config
index 6b515b73..1dfe90ff 100644
--- a/nuget.config
+++ b/nuget.config
@@ -5,6 +5,5 @@
-
\ No newline at end of file
diff --git a/samples/AzureFunctionsApp/AzureFunctionsApp.csproj b/samples/AzureFunctionsApp/AzureFunctionsApp.csproj
index a3fb81be..efe42b22 100644
--- a/samples/AzureFunctionsApp/AzureFunctionsApp.csproj
+++ b/samples/AzureFunctionsApp/AzureFunctionsApp.csproj
@@ -9,9 +9,9 @@
-
+
-
+
diff --git a/samples/AzureFunctionsApp/Entities/Counter.Entity.cs b/samples/AzureFunctionsApp/Entities/Counter.Entity.cs
deleted file mode 100644
index e6a0cea5..00000000
--- a/samples/AzureFunctionsApp/Entities/Counter.Entity.cs
+++ /dev/null
@@ -1,89 +0,0 @@
-// Copyright (c) Microsoft Corporation.
-// Licensed under the MIT License.
-
-using Microsoft.Azure.Functions.Worker;
-using Microsoft.Azure.Functions.Worker.Http;
-using Microsoft.DurableTask;
-using Microsoft.DurableTask.Client;
-using Microsoft.DurableTask.Entities;
-using Microsoft.Extensions.Logging;
-
-namespace AzureFunctionsApp.Entities.Entity;
-
-///
-/// Example on how to dispatch to an entity which directly implements TaskEntity. Using TaskEntity gives
-/// the added benefit of being able to use DI. When using TaskEntity, state is deserialized to the "State"
-/// property. No other properties on this type will be serialized/deserialized.
-///
-public class Counter : TaskEntity
-{
- readonly ILogger logger;
-
- public Counter(ILogger logger)
- {
- this.logger = logger;
- }
-
- public int Add(int input)
- {
- this.logger.LogInformation("Adding {Input} to {State}", input, this.State);
- return this.State += input;
- }
-
- public int OperationWithContext(int input, TaskEntityContext context)
- {
- // Get access to TaskEntityContext by adding it as a parameter. Can be with or without an input parameter.
- // Order does not matter.
- context.StartOrchestration("SomeOrchestration", "SomeInput");
-
- // When using TaskEntity, the TaskEntityContext can also be accessed via this.Context.
- this.Context.StartOrchestration("SomeOrchestration", "SomeInput");
- return this.Add(input);
- }
-
- public int Get() => this.State;
-
- [Function("Counter2")]
- public Task RunEntityAsync([EntityTrigger] TaskEntityDispatcher dispatcher)
- {
- // Can dispatch to a TaskEntity by passing a instance.
- return dispatcher.DispatchAsync(this);
- }
-
- [Function("Counter3")]
- public static Task RunEntityStaticAsync([EntityTrigger] TaskEntityDispatcher dispatcher)
- {
- // Can also dispatch to a TaskEntity by using a static method.
- return dispatcher.DispatchAsync();
- }
-
- [Function("StartCounter2")]
- public static async Task StartAsync(
- [HttpTrigger(AuthorizationLevel.Anonymous, "post")] HttpRequestData request,
- [DurableClient] DurableTaskClient client)
- {
- Payload? payload = await request.ReadFromJsonAsync();
- string id = await client.ScheduleNewOrchestrationInstanceAsync("CounterOrchestration2", payload);
- return client.CreateCheckStatusResponse(request, id);
- }
-
- [Function("CounterOrchestration2")]
- public static async Task RunOrchestrationAsync(
- [OrchestrationTrigger] TaskOrchestrationContext context, Payload input)
- {
- ILogger logger = context.CreateReplaySafeLogger();
- int result = await context.Entities.CallEntityAsync(
- new EntityInstanceId("Counter2", input.Key), "add", input.Add);
-
- logger.LogInformation("Counter value: {Value}", result);
- return result;
- }
-
- protected override int InitializeState()
- {
- // Optional method to override to customize initialization of state for a new instance.
- return base.InitializeState();
- }
-
- public record Payload(string Key, int Add);
-}
diff --git a/samples/AzureFunctionsApp/Entities/Counter.State.cs b/samples/AzureFunctionsApp/Entities/Counter.State.cs
deleted file mode 100644
index d286ac84..00000000
--- a/samples/AzureFunctionsApp/Entities/Counter.State.cs
+++ /dev/null
@@ -1,65 +0,0 @@
-// Copyright (c) Microsoft Corporation.
-// Licensed under the MIT License.
-
-using Microsoft.Azure.Functions.Worker;
-using Microsoft.Azure.Functions.Worker.Http;
-using Microsoft.DurableTask;
-using Microsoft.DurableTask.Client;
-using Microsoft.DurableTask.Entities;
-using Microsoft.Extensions.Logging;
-
-namespace AzureFunctionsApp.Entities.State;
-
-///
-/// Example on how to dispatch to a POCO as the entity implementation. When using POCO, the entire object is serialized
-/// and deserialized.
-///
-public class Counter
-{
- public int Value { get; set; }
-
- public int Add(int input) => this.Value += input;
-
- public int OperationWithContext(int input, TaskEntityContext context)
- {
- // Get access to TaskEntityContext by adding it as a parameter. Can be with or without an input parameter.
- // Order does not matter.
- context.StartOrchestration("SomeOrchestration", "SomeInput");
- return this.Add(input);
- }
-
- public int Get() => this.Value;
-
- [Function("Counter1")]
- public static Task RunEntityAsync([EntityTrigger] TaskEntityDispatcher dispatcher)
- {
- // Using the dispatch to a state object will deserialize the state directly to that instance and dispatch to an
- // appropriate method.
- // Can only dispatch to a state object via generic argument.
- return dispatcher.DispatchAsync();
- }
-
- [Function("StartCounter1")]
- public static async Task StartAsync(
- [HttpTrigger(AuthorizationLevel.Anonymous, "post")] HttpRequestData request,
- [DurableClient] DurableTaskClient client)
- {
- Payload? payload = await request.ReadFromJsonAsync();
- string id = await client.ScheduleNewOrchestrationInstanceAsync("CounterOrchestration1", payload);
- return client.CreateCheckStatusResponse(request, id);
- }
-
- [Function("CounterOrchestration1")]
- public static async Task RunOrchestrationAsync(
- [OrchestrationTrigger] TaskOrchestrationContext context, Payload input)
- {
- ILogger logger = context.CreateReplaySafeLogger();
- int result = await context.Entities.CallEntityAsync(
- new EntityInstanceId("Counter1", input.Key), "add", input.Add);
-
- logger.LogInformation("Counter value: {Value}", result);
- return result;
- }
-
- public record Payload(string Key, int Add);
-}
diff --git a/samples/AzureFunctionsApp/Entities/Counter.cs b/samples/AzureFunctionsApp/Entities/Counter.cs
new file mode 100644
index 00000000..97584977
--- /dev/null
+++ b/samples/AzureFunctionsApp/Entities/Counter.cs
@@ -0,0 +1,157 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+using Microsoft.Azure.Functions.Worker;
+using Microsoft.Azure.Functions.Worker.Http;
+using Microsoft.DurableTask;
+using Microsoft.DurableTask.Client;
+using Microsoft.DurableTask.Entities;
+using Microsoft.Extensions.Logging;
+
+namespace AzureFunctionsApp.Entities;
+
+///
+/// Example on how to dispatch to an entity which directly implements TaskEntity. Using TaskEntity gives
+/// the added benefit of being able to use DI. When using TaskEntity, state is deserialized to the "State"
+/// property. No other properties on this type will be serialized/deserialized.
+///
+public class Counter : TaskEntity
+{
+ readonly ILogger logger;
+
+ public Counter(ILogger logger)
+ {
+ this.logger = logger;
+ }
+
+ public int Add(int input)
+ {
+ this.logger.LogInformation("Adding {Input} to {State}", input, this.State);
+ return this.State += input;
+ }
+
+ public int OperationWithContext(int input, TaskEntityContext context)
+ {
+ // Get access to TaskEntityContext by adding it as a parameter. Can be with or without an input parameter.
+ // Order does not matter.
+ context.ScheduleNewOrchestration("SomeOrchestration", "SomeInput");
+
+ // When using TaskEntity, the TaskEntityContext can also be accessed via this.Context.
+ this.Context.ScheduleNewOrchestration("SomeOrchestration", "SomeInput");
+ return this.Add(input);
+ }
+
+ public int Get() => this.State;
+
+ [Function("Counter")]
+ public Task RunEntityAsync([EntityTrigger] TaskEntityDispatcher dispatcher)
+ {
+ // Can dispatch to a TaskEntity by passing a instance.
+ return dispatcher.DispatchAsync(this);
+ }
+
+ [Function("Counter_Alt")]
+ public static Task RunEntityStaticAsync([EntityTrigger] TaskEntityDispatcher dispatcher)
+ {
+ // Can also dispatch to a TaskEntity by using a static method.
+ return dispatcher.DispatchAsync();
+ }
+
+ protected override int InitializeState(TaskEntityOperation operation)
+ {
+ // Optional method to override to customize initialization of state for a new instance.
+ return base.InitializeState(operation);
+ }
+}
+
+///
+/// Example on how to dispatch to a POCO as the entity implementation. When using POCO, the entire object is serialized
+/// and deserialized.
+///
+///
+/// Note there is a structural difference between and . In the former,
+/// the state is declared as . In the later, state is the type itself ().
+/// This means they have a different state serialization structure. The former is just "int", while the later is
+/// "{ \"Value\": int }".
+///
+public class StateCounter
+{
+ public int Value { get; set; }
+
+ public int Add(int input) => this.Value += input;
+
+ public int OperationWithContext(int input, TaskEntityContext context)
+ {
+ // Get access to TaskEntityContext by adding it as a parameter. Can be with or without an input parameter.
+ // Order does not matter.
+ context.ScheduleNewOrchestration("SomeOrchestration", "SomeInput");
+ return this.Add(input);
+ }
+
+ public int Get() => this.Value;
+
+ [Function("StateCounter")]
+ public static Task RunEntityAsync([EntityTrigger] TaskEntityDispatcher dispatcher)
+ {
+ // Using the dispatch to a state object will deserialize the state directly to that instance and dispatch to an
+ // appropriate method.
+ // Can only dispatch to a state object via generic argument.
+ return dispatcher.DispatchAsync();
+ }
+}
+
+public static class CounterHelpers
+{
+ ///
+ /// Usage:
+ /// Add to :
+ /// POST /api/counter/{id}?value={value-to-add}
+ /// POST /api/counter/{id}?value={value-to-add}&mode=0
+ /// POST /api/counter/{id}?value={value-to-add}&mode=entity
+ ///
+ /// Add to
+ /// POST /api/counter/{id}?value={value-to-add}&mode=1
+ /// POST /api/counter/{id}?value={value-to-add}&mode=state
+ ///
+ ///
+ ///
+ ///
+ ///
+ [Function("StartCounter")]
+ public static async Task StartAsync(
+ [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "counter/{id}")] HttpRequestData request,
+ [DurableClient] DurableTaskClient client,
+ string id)
+ {
+ _ = int.TryParse(request.Query["value"], out int value);
+
+ string name = "counter";
+
+ // switch to StateCounter if ?mode=1 or ?mode=state is supplied.
+ string? mode = request.Query["mode"];
+ if (int.TryParse(mode, out int m) && m == 1)
+ {
+ name = "state";
+ }
+ else if (mode == "state")
+ {
+ name = "statecounter";
+ }
+
+ string instanceId = await client.ScheduleNewOrchestrationInstanceAsync(
+ "CounterOrchestration", new Payload(new EntityInstanceId(name, id), value));
+ return client.CreateCheckStatusResponse(request, instanceId);
+ }
+
+ [Function("CounterOrchestration")]
+ public static async Task RunOrchestrationAsync(
+ [OrchestrationTrigger] TaskOrchestrationContext context, Payload input)
+ {
+ ILogger logger = context.CreateReplaySafeLogger();
+ int result = await context.Entities.CallEntityAsync(input.Id, "add", input.Add);
+ logger.LogInformation("Counter value: {Value}", result);
+ return result;
+ }
+
+ public record Payload(EntityInstanceId Id, int Add);
+}
diff --git a/samples/AzureFunctionsApp/Entities/StateManagement.cs b/samples/AzureFunctionsApp/Entities/StateManagement.cs
index b590af4d..86871469 100644
--- a/samples/AzureFunctionsApp/Entities/StateManagement.cs
+++ b/samples/AzureFunctionsApp/Entities/StateManagement.cs
@@ -43,7 +43,7 @@ public void Delete()
this.State = null!;
}
- protected override MyState InitializeState()
+ protected override MyState InitializeState(TaskEntityOperation operation)
{
// This method allows for customizing the default state value for a new entity.
return new("Default", 10);
diff --git a/src/Worker/Core/DurableTaskWorkerOptions.cs b/src/Worker/Core/DurableTaskWorkerOptions.cs
index bc1e7fc4..2fa44d78 100644
--- a/src/Worker/Core/DurableTaskWorkerOptions.cs
+++ b/src/Worker/Core/DurableTaskWorkerOptions.cs
@@ -45,8 +45,8 @@ public DataConverter DataConverter
}
///
- /// Gets or sets a value indicating whether this client should support entities. If true, all instance ids starting with '@' are reserved for entities,
- /// and validation checks are performed where appropriate.
+ /// Gets or sets a value indicating whether this client should support entities. If true, all instance ids starting
+ /// with '@' are reserved for entities, and validation checks are performed where appropriate.
///
public bool EnableEntitySupport { get; set; }