question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Applying policies by attributes

See original GitHub issue

This 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:closed
  • Created 4 years ago
  • Reactions:6
  • Comments:8 (2 by maintainers)

github_iconTop GitHub Comments

2reactions
jjxtracommented, Dec 22, 2019

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 😃

1reaction
djecheloncommented, Dec 10, 2020

The biggest potential drawback I can see - if the attributes configure the policies themselves - is that attributes require parameters to be compile-time constants (eg [Retry(3)]). This tends either to preclude or not play well with various other policy features - driving policies from external config; using any policy-callback features like onRetry; using anything dynamic at runtime like enumerables for delay-between-retries, eg for jitter; and DI/separation-of-concerns and unit-testing.

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

Read more comments on GitHub >

github_iconTop Results From Across the Web

Apply a policy to Projects
A policy can be applied to one or multiple Project attributes; but a set of attributes can only be assigned to one policy....
Read more >
Using RADIUS Attributes to Apply Group Policies - Cisco Meraki
RADIUS attributes used with Group policies can apply custom network policies to wireless users. This can be accomplished using a RADIUS  ...
Read more >
Policy-based authorization in ASP.NET Core
Apply policies to Razor Pages. Apply policies to Razor Pages by using the [Authorize] attribute with the policy name. For example: C#
Read more >
What Is Attribute-Based Access Control (ABAC)?
With ABAC, an organization's access policies enforce access decisions based on the attributes of the subject, resource, action, and environment ...
Read more >
Implementing and Managing Policy Rules in Attribute Based ...
Metaattributes apply to subjects, objects, and environment conditions as extended attribute information useful for enforcing more detailed policy that ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found