95 lines
3.7 KiB
Plaintext
95 lines
3.7 KiB
Plaintext
== Why is this an issue?
|
|
|
|
The recommended way to access Azure Durable Entities is through generated proxy objects with the help of interfaces.
|
|
|
|
The following restrictions, during interface design, are enforced:
|
|
|
|
* Entity interfaces must be defined in the same assembly as the entity class. This is not detected by the rule.
|
|
* Entity interfaces must only define methods.
|
|
* Entity interfaces must not contain generic parameters.
|
|
* Entity interface methods must not have more than one parameter.
|
|
* Entity interface methods must return void, Task, or Task<T>.
|
|
|
|
If any of these rules are violated, an `InvalidOperationException` is thrown at runtime when the interface is used as a type argument to `IDurableEntityContext.SignalEntity<TEntityInterface>`, `IDurableEntityClient.SignalEntityAsync<TEntityInterface>` or `IDurableOrchestrationContext.CreateEntityProxy<TEntityInterface>`. The exception message explains which rule was broken.
|
|
|
|
This rule raises an issue in case any of the restrictions above is not respected.
|
|
|
|
=== Noncompliant code example
|
|
|
|
[source,csharp,diff-id=1,diff-type=noncompliant]
|
|
----
|
|
namespace Foo // Noncompliant, must be defined in the same assembly as the entity class that implements it
|
|
{
|
|
public interface ICounter<T> // Noncompliant, interfaces cannot contain generic parameters
|
|
{
|
|
string Name { get; set; } // Noncompliant, interface must only define methods
|
|
void Add(int amount, int secondParameter); // Noncompliant, methods must not have more than one parameter
|
|
int Get(); // Noncompliant, methods must return void, Task, or Task<T>
|
|
}
|
|
}
|
|
|
|
namespace Bar
|
|
{
|
|
public class Counter : ICounter
|
|
{
|
|
// do stuff
|
|
}
|
|
|
|
public static class AddToCounterFromQueue
|
|
{
|
|
[FunctionName("AddToCounterFromQueue")]
|
|
public static Task Run(
|
|
[QueueTrigger("durable-function-trigger")] string input,
|
|
[DurableClient] IDurableEntityClient client)
|
|
{
|
|
var entityId = new EntityId("Counter", "myCounter");
|
|
int amount = int.Parse(input);
|
|
return client.SignalEntityAsync<ICounter>(entityId, proxy => proxy.Add(amount, 10));
|
|
}
|
|
}
|
|
}
|
|
----
|
|
|
|
=== Compliant solution
|
|
|
|
[source,csharp,diff-id=1,diff-type=compliant]
|
|
----
|
|
namespace Bar
|
|
{
|
|
public interface ICounter
|
|
{
|
|
void Add(int amount);
|
|
Task<int> Get();
|
|
}
|
|
}
|
|
|
|
namespace Bar
|
|
{
|
|
public class Counter : ICounter
|
|
{
|
|
// do stuff
|
|
}
|
|
|
|
public static class AddToCounterFromQueue
|
|
{
|
|
[FunctionName("AddToCounterFromQueue")]
|
|
public static Task Run(
|
|
[QueueTrigger("durable-function-trigger")] string input,
|
|
[DurableClient] IDurableEntityClient client)
|
|
{
|
|
var entityId = new EntityId("Counter", "myCounter");
|
|
int amount = int.Parse(input);
|
|
return client.SignalEntityAsync<ICounter>(entityId, proxy => proxy.Add(amount));
|
|
}
|
|
}
|
|
}
|
|
----
|
|
|
|
== Resources
|
|
|
|
* https://docs.microsoft.com/en-us/azure/azure-functions/durable/durable-functions-dotnet-entities#restrictions-on-entity-interfaces[Restrictions on Entity Interfaces]
|
|
* https://docs.microsoft.com/en-us/azure/azure-functions/durable/durable-functions-entities?tabs=csharp[Durable Entities]
|
|
* https://learn.microsoft.com/en-us/dotnet/api/microsoft.azure.webjobs.extensions.durabletask.idurableentitycontext.signalentity?view=azure-dotnet[IDurableEntityContext.SignalEntity]
|
|
* https://learn.microsoft.com/en-us/dotnet/api/microsoft.azure.webjobs.extensions.durabletask.idurableentityclient.signalentityasync?view=azure-dotnet[IDurableEntityClient.SignalEntityAsync]
|
|
* https://learn.microsoft.com/en-us/dotnet/api/microsoft.azure.webjobs.extensions.durabletask.idurableorchestrationcontext.createentityproxy?view=azure-dotnet[IDurableOrchestrationContext.CreateEntity]
|