johni
johni

Reputation: 5568

Wrapping a method with Attributes C#

I'm using Polly .NET for wrapping my methods with retry behavior. Polly makes it quite easy and elegant but I'm trying to take it to the next level.

Please see this Python example (it might have few mistakes, but that's not the point here):

@retry(wait_exponential_multiplier=250,
           wait_exponential_max=4500,
           stop_max_attempt_number=8,
           retry_on_result=lambda failures_count: failures_count > 0)
def put():
    global non_delivered_tweets

    logger.info("Executing Firehose put_batch command on {} tweets".format(len(non_delivered_tweets)))
    response = firehose.put_record_batch(DeliveryStreamName=firehose_stream_name, Records=non_delivered_tweets)

    failures_count = response["FailedPutCount"]
    failures_list = []
    if failures_count > 0:
        for index, request_response in enumerate(response["RequestResponses"]):
            if "ErrorCode" in request_response:
                failures_list.append(non_delivered_tweets[index])

    non_delivered_tweets = failures_list
    return failures_count

The benefits in writing code like the above:

Since the two are not mixed - it makes the code much more readable in my opinion.

I would like to achieve this syntax with Polly on C#, using attributes. I have minimum knowledge in C# attributes, and for what I've read, it seems that this is not possible.

I would be happy to have something like this:

class Program
{
    static void Main(string[] args)
    {
        var someClassInstance = new SomeClass();
        someClassInstance.DoSomething();
    }
}

class Retry : Attribute
{
    private static readonly Policy DefaultRetryPolicy = Policy
        .Handle<Exception>()
        .WaitAndRetry(3, retryAttempt => TimeSpan.FromSeconds(5));

    public void Wrapper(Action action)
    {
        DefaultRetryPolicy.Execute(action);
    }
}

class SomeClass
{
    [Retry]
    public void DoSomething()
    {
        // core logic
    }
}

As you can see, in my example - the [Retry] attribute wraps the DoSomething method with retry logic. If that is possible, I would be very happy to learn how to implement it.

Thanks a lot for help !

Upvotes: 1

Views: 1243

Answers (1)

Georg
Georg

Reputation: 5781

Of course, that is possible. However, it is way more complicated than in Python. Unlike Python where decorators are executable code that may exchange the decorated object, attributes in C# are pure metadata. .NET attributes do not have access to the object they are decorating but rather stand for themselves.

Therefore, you have to connect the attribute and the method yourself and especially replace the method yourself (i.e. replace the function with the core logic with the function that also includes retries etc.). The latter is not possible implicitly in C#, you have to do that explicitly.

It should work similar to this:

class RetryExecutor
{
    public static void Call(Action action)
    {
        var attribute = action.Method.GetCustomAttribute(typeof(Retry));
        if (attribute != null)
        {
             ((Retry)attribute).Wrap(action);
        }
        else
        {
            action();
        }
    }
}

Upvotes: 2

Related Questions