Applying policies by attributes
See original GitHub issueThis feature request is related to policies.
I would like to be able to annotate my class and interface methods with attributes that represent Polly policies, such as retry, circuit breaker, caching, etc. Something like this:
[Cache(...)] // cache ALL the methods of this interface unless overridden
[Retry(...)] // retry ALL the methods on this interface unless overridden
public interface IWebInterface
{
[Cache(...)]
[Retry(...)]
[CircuitBreaker(...)]
[Bulkhead(...)]
Task<string> FetchJson1Async(string param1);
[PollyIgnore] // ignore all polly attributes for just this method
Task<string> FetchJson2Async(string param1);
// this method inherits cache and retry from the interface definition
Task<string> FetchJson3Async(string param1);
}
These attributes would inherit from a PollyBaseAttribute
class. This saves having to type the policies everywhere the function is called. You could also provide a “uber” attribute that allowed specifying all the policies in a single attribute with named parameters. I could see people making some attribute sub-classes that setup all the policies they care about and then applying those attributes as needed.
The attributes would provide all the necessary properties to create a policy automatically. For example, the Cache
attribute could provider a Key string property. If left null, an automatic key could be created using the full type name plus method name.
If a class implements an interface or overrides base class methods with Polly attributes, the class implementation would always get priority for Polly attributes.
The attributes would be applied in order. With the attributes, the interface call could work one of two ways:
1] Have an expression tree based utility method that allows calling the interface method through a Polly helper method, like Policy.Execute already does. The helper method would take care of finding the attributes and applying the appropriate Polly policies. There is still some repetition of code, since a Polly helper method would be needed anywhere you want to call a method with Polly attributes.
Example:
IWebInterface baseInterface = new WebInterface(); // WebInterface class implements IWebInterface
string result = await Polly.Execute(obj => obj.FetchJson1Async(param1)); // finds Polly attributes and adds appropriate policies to the method call
The Polly.Wrap
call would take care of finding the attributes and applying policies. You could cache the type and method name for fast attribute lookup.
2] Use TypeBuilder and allow generating a Polly wrapper on an interface. For example:
IWebInterface baseInterface = new WebInterface(); // WebInterface class implements IWebInterface
IWebInterface wrappedInterface = Polly.Wrap(baseInterface); // MOQ style interface wrapper builder magic
string result = await wrappedInterface.FetchJson1("param1"); // wrappedInterface provides Polly policies using TypeBuilder magic.
Once that is done, you could call wrappedInterface and the Polly policies would be called automatically. This requires either building up an interface at runtime that wraps the existing interface, or having some sort of code generator that could generate a C# class that wraps the interface. Frameworks like MOQ do this already. This eliminates all the redundant calls to Polly and helper methods, and would be my ideal solution.
TypeBuilder
documentation: https://docs.microsoft.com/en-us/dotnet/api/system.reflection.emit.typebuilder?view=netcore-3.1
I’d love any thoughts or feedback on this proposal.
Issue Analytics
- State:
- Created 4 years ago
- Reactions:6
- Comments:8 (2 by maintainers)
Top GitHub Comments
PollyNator does wrap an interface using Roslyn so that’s interesting. It does not use attributes but rather a class-wide policy. With a little work it could probably be tweaked to do attributes instead. That would cover the second suggestion.
For the first suggestion, your idea of using a policy key seems like it would work well, allowing the policy to be configured and injected at runtime.
Thanks for your well thought out response, Polly is an amazing library, I have only stumbled upon it recently and have been using it for https://ipban.com back-end 😃
We could use the best of both worlds. My preferred item for comparison is Java’s Spring Retry annotation, where you can’t set a configurable policy but are restrained to compile-time retry policy.
So the new Polly-Contrib project could allow both a compile-time and registry policy giving priority to any if both are chosen (or error).
As for the very implementation in DI framework, as a very experienced Spring contributor, I’d investigate how can .NET Core DI handle AOP (Aspect Oriented Programming) via interception, something like
PollyNator does wrap an interface
statement.We should make plans, I’m interested in this topic for my company.
See also