CHAPTER 4
One of the most important stateful patterns in computer science is the aggregator pattern. In the aggregator pattern, you want to implement something that receives multiple calls from the outside, aggregates all the data it receives in an internal status, and manages the status persistence.
If you want to implement this pattern using Azure Functions, you will run into some issues:
Imagine you want to implement an Azure function that allows you to store the telemetry data from an IoT field device. The following figure shows the pattern.

Figure 26: The aggregator pattern
We suppose that every device must have these features:
The Durable Functions platform provides us with special durable functions called durable entities, or entity functions. In the following paragraphs, we will see in detail the characteristics of these particular functions and how they can help us. Later in this chapter, we will see the full implementation for an IoT platform based on the durable entities.
Before starting with the implementation of our devices, it is important to clarify some fundamental concepts regarding durable entities.
A durable entity is similar to an orchestrator. It is an Azure function with a special trigger (the EntityTrigger). Like an orchestrator, a durable entity manages its state abstracting the persistence layer.
You can consider a durable entity like a little service that communicates using messages. It has a unique identity and an internal state, and it exposes operations. It can interact with other entities, orchestrators, or external services.
We saw in the previous chapters that you can access an orchestrator using its orchestrator ID.
You can access a durable entity using a unique identifier called entityID. This identifier is essentially composed of two strings:
In our example, suppose we implement one device with an entity function called TelemetryDeviceEntity, and we have two different devices in our environment called livingRoom and kitchen. The platform will uniquely identify the devices with the following entity ID: @TemperatureDeviceEntity@livingRoom and @TemperatureDeviceEntity@kitchen.
A durable entity can expose several operations, and you need the following information to call a specific operation:
You can call an operation from different sources (a client, another entity, or an orchestrator), and you have several ways to do that. We will cover those ways in the "Accessing the entities" section.
An entity operation can change the entity's inner status or call another entity and return a value.
You can define your durable entity in two different ways:
You define a function with a special trigger (EntityTrigger), and you manage your entity state inside that function.
In the following snippet of code, you can see an example of a simple counter implemented using this approach.
Code Listing 33: The counter entity implemented with the function-based syntax
[FunctionName("CounterFunctionBased")] 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; } } |
The function uses IDurableEntityContext to manage the input data from the caller, the data to return (if you need it), and the entity's state.
The context interface allows you to set and get the status, and it abstracts the persistence layer. You can store only one object in the state (the platform serializes and deserializes it for you before and after every persistence operation). If you need a complex state object, you must declare and use a class to store the state instead of using a base type like an integer.
If you look at Code Listing 33, you can see that the function implements a switch to manage every single operation supported by the entity. Every branch of the switch manages a single operation and uses the context to retrieve the input data, retrieve the state, and save the state.
The context interface also gives you other methods to call orchestrators or other entities and provides you with all the information about the EntityId invoked. Using the context, you can implement the behavior you prefer for your entity.
This approach works well when:
The limitations of this approach are:
You need to manage the retrieving and saving state operations using the context interface. It abstracts the actual persistence operations (you don't see the underlying storage layer), but you explicitly call the GetStatus and SetStatus methods.
You can use the function-based syntax for your durable entities if your entity has a simple state or exposes few methods.
In the real world, you often have entities with a complex state (composed of more than one complex object), you need to expose a lot of methods, or more simply, you want to take advantage of the compilation features of the language that you are using such as the compile-time syntax check or the IntelliSense.
In those scenarios, you can implement your durable entity using the class-based syntax. A class-based syntax entity is a POCO class (POCO stands for plain old CLR object) with properties, methods, and one entity function (similar to the entity function you see earlier in the function-based syntax).
This class doesn't require a special hierarchy (you can create your own class structure). It only requires that:
The platform automatically serializes and deserializes every single entity instance to store the status on the underlying storage layer. All its serializable properties represent the inner state (so you can design the entity state as a composition of complex objects).
You have some limitations on how you can implement the methods, and in particular:
The entity class exposes an entity function you can use to call the methods provided by the class. When you use your entity in a durable context, you don't call its methods directly. You call the entity function, and it invokes your particular method.
You can read or update the entity state by reading or changing the value in one or more properties in an entity method. The platform stores the state in the storage immediately after your method is finished. Moreover, in a method, you can perform I/O operations or computational operations: the limits are the common limits of Azure functions.
Finally, in an entity method, you can use the entity context (for example, to call another entity or create a new orchestration) through the Entity.Context object.
The following code listing shows you the implementation of the Counter entity.
Code Listing 34: The Counter entity implemented with the function-based syntax
[JsonObject(MemberSerialization.OptIn)] public class Counter { [JsonProperty("value")] public int CurrentValue { get; set; } public void Add(int amount) => this.CurrentValue += amount; public void Reset() => this.CurrentValue = 0; public int Get() => this.CurrentValue; [FunctionName(nameof(Counter))] public static Task Run([EntityTrigger] IDurableEntityContext ctx) => ctx.DispatchAsync<Counter>(); } |
As you can see, the device class consists of:
If you look at the entity function, you can see that this function uses the durable context to dispatch the calls received by the entity to the correct method without implementing any switch in the code. The DispatchAsync method uses the call context provided by the platform to try to invoke the method in the class. Here you don't have any string as happened in the function-based syntax, so the possibility for error is less.
The name of the entity function (declared in the FunctionName attribute) is also the entity's name. You can have different names for the class and the entity.
A durable entity can implement an interface. In that way, you can create a polymorphism in the durable entities platform.
The following code listing shows you the interface for the counter.
Code Listing 35: The Counter interface
public interface ICounter { void Add(int amount); Task AddAsync(int amount); Task ResetAsync(); Task<int> GetAsync(); } [JsonObject(MemberSerialization.OptIn)] public class Counter : ICounter { [JsonProperty("value")] public int CurrentValue { get; set; } public void Add(int amount) => this.CurrentValue += amount; public Task AddAsync(int amount) { this.Add(amount); return Task.CompletedTask; } public Task ResetAsync() { this.CurrentValue = 0; return Task.CompletedTask; } public Task<int> GetAsync() => Task.FromResult(this.CurrentValue); [FunctionName(nameof(Counter))] public static Task Run([EntityTrigger] IDurableEntityContext ctx) => ctx.DispatchAsync<Counter>(); } |
When you use an interface, you have some other limitations on the methods you can define:
If you define a void method, you can only call that method using a fire-and-forget pattern (a one-way call). A Task or Task<T> method can call both one-way and two-way patterns. You call those methods using the two-way pattern when you want to receive a result. We look at both patterns in the next section.
You can invoke an entity in two different ways:
An entity can be accessed by client functions, orchestrator functions, and other entities, and the way to call the entity depends on the caller.
A client function can call an entity using a one-way approach or can retrieve the entity state.
An orchestrator function can use both one-way and two-way patterns.
An entity can only signal another entity.
A client function can signal an entity or read its status.
The following snippet of code shows you how you can use the one-way approach inside a client function.
Code Listing 36: A client function signals an entity
[FunctionName("AddFromQueue")] public static Task AddFromQueue([QueueTrigger("durable-function-trigger")] string input, [DurableClient] IDurableEntityClient client) { // Entity operation input comes from the queue message content. var entityId = new EntityId(nameof(Counter), "myCounter"); int amount = int.Parse(input); return client.SignalEntityAsync(entityId, "Add", amount); } |
The client function is triggered by a message in the durable-function-trigger queue.
It creates the EntityId object for the entity to signal to and calls the SignalEntityAsync method provided by the IDurableEntityClient interface.
The entity invocation is completely asynchronous: you cannot know when the destination entity will process the request.
The IDurableEntityClient interface used by the client provides you with all the methods you need to interact with an entity inside a client.
In the following figure, you can see the methods exposed by the interface.

Figure 27: The IDurableEntityClient interface structure
It is important to know how this interface works.
The SignalEntityAsync allows you to create a one-way communication between your client and an entity, and you have six different overloads of this method. Two of those overloads (one is used in the previous snippet of code) allow you to call a method of the entity knowing the entityId and the method's name (a string). One of those also allows you to signal the entity delayed: you can specify a DateTime object, and the method will signal the entity at that time.
Both the overloads use a string to identify the method, which can create some issues when you refactor your code. If you change the name of an entity method, the compiler will not give you any information that the name you are using in the SignalEntityAsync is wrong. You can determine that only at runtime.
If your entity implements an interface (as mentioned earlier in this chapter), you can use one of the other signatures of the method.
Code Listing 37: A client function signals an entity through an interface
[FunctionName("AddFromQueueWithInterface")] public static Task AddFromQueueWithInterface([QueueTrigger("durable-function-trigger")] string input, [DurableClient] IDurableEntityClient client) { // Entity operation input comes from the queue message content. var entityId = new EntityId(nameof(Counter), "myCounter"); int amount = int.Parse(input); return client.SignalEntityAsync<ICounter>(entityId, c => c.Add(amount)); } |
In this case, you use a lambda expression to identify the entity method you want to signal, and the compiler will give you a compile-time error if you miss the method name. This is one reason why defining and using an interface for your entities is a best practice.
Using the IDurableEntityClient interface, you can also read the state of an entity.
Code Listing 38: A client function reads the entity state
[FunctionName("GetCounterState")] public static async Task<IActionResult> GetCounterState( [HttpTrigger(AuthorizationLevel.Function, Route = "counters/{counterName}")] HttpRequest req, string counterName, [DurableClient] IDurableEntityClient client) { var entityId = new EntityId(nameof(Counter), counterName); EntityStateResponse<JObject> stateResponse = await client.ReadEntityStateAsync<JObject>(entityId); return new OkObjectResult(stateResponse.EntityState); } |
The EntityStateResponse object contains the state of the entity. You can use your class as a generic parameter in the ReadEntityStateAsync method if you want to deserialize the status in your custom object. In the sample, we retrieve the status as JObject. The JObject class can contain complex objects serialized in JSON format.
If you look at Figure 27, you can see another two methods exposed by the IDurableEntityClient interface. The ListEntitiesAsync method allows you to retrieve the list of all the entities in the platform.
Code Listing 39: A client function to retrieve all the counter entities
public class CounterQueryState { public string Key { get; set; } public int Value { get; set; } } [FunctionName("CounterList")] public static async Task<IActionResult> CounterList( [HttpTrigger(AuthorizationLevel.Function, Route = "counters")] HttpRequest req, [DurableClient] IDurableEntityClient client) { var responseList = new List<CounterQueryState>(); var queryDefinition = new EntityQuery() { PageSize = 100, FetchState = true, EntityName = nameof(Counter) }; do { EntityQueryResult queryResult = await client.ListEntitiesAsync(queryDefinition, default); foreach (var item in queryResult.Entities) { var counterState = item.State.ToObject<CounterQueryState>(); counterState.Key = item.EntityId.EntityKey; responseList.Add(counterState); } queryDefinition.ContinuationToken = queryResult.ContinuationToken; } while (queryDefinition.ContinuationToken != null ); return new OkObjectResult(responseList); } |
You create an EntityQuery object that allows you to control how you execute the query against the platform. The EntityQuery object allows you to define the page size for each fetch operation (the query in the platform is always paginated), the name of the entity you want to search (if you want), if the query results contain the state of the entities, and so on.
Every time you call the ListEntitiesAsync method, you retrieve a page of the query, and you need to provide the continuation token returned by the previous invocation. You can know if you reach the last page by checking the continuation token. When it is null, you’ve retrieved the last page of the search.
The last method of the IDurableEntityClient interface is CleanEntityStorage. This method allows you to remove all the empty entities in your platform. An empty entity is an entity with no state, not used by other entities (with no active locks) and idle for a configurable amount of time in the durable function settings. This method allows you to clear your persistence storage safely.
When you use an orchestrator to interact with an entity, you have the most flexibility. You can use both the patterns one-way and two-way inside a workflow. An orchestrator can signal an entity without waiting for the response. Still, because the platform can manage the workflow state and restart the orchestrator, it can wait for a response from the entity. From this point of view, calling an entity is equal to calling an activity.
The orchestration context (provided by the platform through the OrchestrationTrigger) gives you all the methods you need to interact with entities.
In the following figure, you can see the structure of the IDurableOrchestrationContext interface.

Figure 28: The IDurableOrchestrationContext interface structure
In the following snippet of code, you can see an example of an orchestrator that calls an entity waiting for the response, and then interacts with the same entity with a one-way approach.
Code Listing 40: Interaction between an orchestrator and an entity
[FunctionName("CounterOrchestration")] public static async Task CounterOrchestration([OrchestrationTrigger] IDurableOrchestrationContext context) { var counterName = context.GetInput<string>(); var entityId = new EntityId(nameof(Counter), counterName); int currentValue = await context.CallEntityAsync<int>(entityId, "Get"); if (currentValue < 10) { context.SignalEntity(entityId, "Add", 1); } } |
The orchestrator executes these operations:
Code Listing 40 is easy to understand, but it has a big issue. The names of the operations you want to call or signal in the entity are strings, and you cannot leverage the compiler checking at compile time. If you make a mistake entering one of the names, you have an error at runtime.
To avoid this, you can use the entity interface and you can create a proxy for the entity you want to interact with, as shown in the following code.
Code Listing 41: Interaction between an orchestrator and an entity using the entity interface
[FunctionName("CounterOrchestrationWithProxy")] public static async Task CounterOrchestrationWithProxy([OrchestrationTrigger] IDurableOrchestrationContext context) { var counterName = context.GetInput<string>(); var entityId = new EntityId(nameof(Counter), counterName); var entityProxy = context.CreateEntityProxy<ICounter>(entityId); int currentValue = await entityProxy.GetAsync(); if (currentValue < 10) { entityProxy.Add(1); } } |
The CreateEntityProxy method enables you to create a proxy that exposes the entity interface you want (in the previous snippet of code, the ICounter interface). You can use this proxy to call the entity's methods directly.
As you can see in the snippet, the CallEntityAsync method of the orchestrator context is substituted with the Get method of the ICounter interface, and the SignalEntity method with the Add. If you save the result of the method (like in the Get method of the previous snippet), you are waiting for the response, so you are using a two-way approach. If you don't store the result of a method (like in the Add method invocation), you are using the one-way pattern.
The code is clearer and more maintainable, and you avoid having runtime errors by mistaking the names of the methods.
Another helpful scenario may be the coordination between entities. In some use cases, you must interact with more than one entity simultaneously, and you want other functions not to modify the entities involved in your task.
The orchestration context interface provides you with a lock implementation to implement those scenarios. At the time of writing this book, unfortunately, this feature is only present for durable functions written in .NET.
In the following snippet of code, you can see an example of a lock between two counter entities.
Code Listing 42: Use of the LockAsync method of the IDurableOrchestrationContext
[FunctionName("AddCounterValue")] public static async Task AddCounterValue([OrchestrationTrigger] IDurableOrchestrationContext context) { var counters = context.GetInput<string>(); if (string.IsNullOrWhiteSpace(counters)) return; var counterNames = counters.Split("|"); if (counterNames.Count() != 2) return; var sourceEntityId = new EntityId(nameof(Counter), counterNames[0]); var destEntityId = new EntityId(nameof(Counter), counterNames[1]); using (await context.LockAsync(sourceEntityId, destEntityId)) { ICounter sourceProxy = context.CreateEntityProxy<ICounter>(sourceEntityId); ICounter destProxy = context.CreateEntityProxy<ICounter>(destEntityId); var sourceValue = await sourceProxy.GetAsync(); await destProxy.AddAsync(sourceValue); await sourceProxy.AddAsync(-sourceValue); } } |
The orchestrator retrieves the value in the state of a source counter entity, adds that value to a destination counter entity, and subtracts it from the source entity. The platform will perform all the operations with a lock on the source and destination entities. This means that other orchestrations or entities cannot interact with the same entities while the lock is active.
The LockAsync method creates a critical section in the orchestrator and returns an IDisposable object. You can use the using construct or call the Dispose method explicitly to end the critical section and release all the locks on the entities involved in the operation. When you create a lock with the LockAsync method, the platform sends a "lock" message to all the entities and waits for the "lock acquired" confirmation.
When an entity is locked, every other operation it receives will be placed in the pending operation queue. When the platform releases the lock, the entity will process all the pending operations. The platform persists the lock in the entity state.
Remember that this isn't a transaction. The critical section doesn't automatically roll back the state of the entities involved in the section. It just prevents other functions from modifying the state while you are working on those entities. You must implement your rollback logic if you need it.
There are some limitations with critical sections:
If you violate one of the previous limitations, you receive a runtime error (a LockingRulesViolationException).
You can interact with an entity from another entity, but you can only use the one-way pattern: an entity can only signal another entity.
The entity context (both in a function-based syntax using the trigger of the function, and in the class-based syntax using the Entity.Current property) provides you with the SignalEntity method to achieve that.
To give an example, suppose you want to monitor how many times your counter is called by the ResetAsync method. Of course, you can add a new property in the state that takes care of that number, but this information is related to the counter without belonging to it. It is monitoring information, so the best approach may be to create another entity called MonitorEntity to take care of all the monitoring information you want to store.
In the following snippet of code, you can see a simple implementation of the MonitorEntity.
Code Listing 43: A simple MonitorEntity entity
public interface IMonitorEntity { void MethodCalled(string methodName); } [JsonObject(MemberSerialization.OptIn)] public class MonitorEntity : IMonitorEntity { [JsonProperty("methodsCounters")] public Dictionary<string, int> MethodsCounters { get; set; } public void MethodCalled(string methodName) { if (MethodsCounters == null) MethodsCounters = new Dictionary<string, int>(); if (MethodsCounters.ContainsKey(methodName)) MethodsCounters[methodName]++; else MethodsCounters[methodName] = 1; } [FunctionName(nameof(MonitorEntity))] public static Task Run([EntityTrigger] IDurableEntityContext ctx) => ctx.DispatchAsync<MonitorEntity>(); } |
Now we can modify the method ResetAsync of the Counter entity to signal the MethodCalled operation of the MonitorEntity every time the ResetAsync is executed.
Code Listing 44: Signaling an entity inside an entity
public Task ResetAsync() { this.CurrentValue = 0; // Monitoring all the reset operation signaling in a MonitorEntity. // The monitoring entity has the same key of the counter. var monitorEntityId = new EntityId("MonitorEntity", Entity.Current.EntityId.ToString()); Entity.Current.SignalEntity<IMonitorEntity>(monitorEntityId, m => m.MethodCalled("ResetAsync")); return Task.CompletedTask; } |
The platform abstracts our entities' state persistence, which means the platform saves and retrieves each entity's status when we address it by calling any method.
As we said earlier in this paragraph, our entity class must be serializable because the durable function runtime uses the Json.NET library to serialize the entire class in the underlying storage.
Note: The Json.NET package is available for download here, and you can read full documentation about it here.
To control how the runtime serializes our class, we can use the attributes present in the Json.NET library. If we look at Code Listing 35, we can see that in the Counter class, we use two different attributes:
You can use all the serialization attributes provided by Json.NET. For example, you can use the JsonIgnore attribute to prevent Json.NET from serializing a specific member.
The previous attributes aren't mandatory. Our entity is a standard class, and the Json.NET manages it even if you don't decorate the class member. If you like, you can use the DataContract and DataMember attributes, as shown in the following code.
Code Listing 45: The counter class decorated using DataContract
[DataContract] public class CounterDataMember { [DataMember(Name = "value")] public int CurrentValue { get; set; } ..... } |
The runtime manages the entity function executions in the same way it manages an orchestrator. It uses the TaskHubHistory table to store each step the entities did. So, for example, if we call the Add method of the Counter entity myCounter, the result is shown in the following figure.

Figure 29: myCounter history table for Add execution
The runtime uses the TaskHubInstances table to store the current status of every entity. In the following figure, you can see the myCounter status after the Add invocation.

Figure 30: The current status of myCounter in the TaskHubInstances
As you can see, the runtime uses the Input field to store the serialization of your class (in the state property of the complex object stored in the field).
In the real world, your project evolves, and you may need to modify the class that implements your entity. As the entity state is serialized in JSON format within the storage, you need to pay attention to some scenarios:
Because the runtime is using Json.NET, you can use all the options in Json.NET to control the serialization of the objects. You can write your own code to deserialize the JSON in the status as you prefer.