-1

is anyone able to tell me why this is not working?

I am attempting to make a generic Interface to define a Polly RetryPolicy and CircuitBreaker policy in c# but am getting a compilation error:

CS0029: Cannot Implicitly Convert Type 'Polly.Retry.AsyncRetryPolicy' to 'T'

Type AsyncRetryPolicy derives from IAsyncPolicy so I do not know why this is not working.

public interface IPollyPolicy<T> where T : IAsyncPolicy
{
    T PolicyAsync { get; }

    Task ExecuteAsync(Func<Task> operation, CancellationToken cancellationToken);

    Task<TResult> ExecuteAsync<TResult>(Func<Task<TResult>> operation, CancellationToken cancellationToken);
}

public class MyRetryPolicy<T> : IPollyPolicy<T> where T : IAsyncPolicy
{

    public T PolicyAsync { get; }

    public MyRetryPolicy(int retryCount = 3, int initialWaitMs = 3000, double factor = 3, bool fastFirst = true)
    {
        var delay = Backoff.ExponentialBackoff(TimeSpan.FromMilliseconds(initialWaitMs), retryCount: retryCount,
            factor: factor, fastFirst: fastFirst).ToList();


             // this line Throws the error: 
             PolicyAsync = Policy
            .Handle<SqlException>()
            .WaitAndRetryAsync(delay);

    }


public class MyCircuitBreakerPolicy<T> : IPollyPolicy<T> where T : IAsyncPolicy
{

    public T PolicyAsync { get; }

    public MyCircuitBreakerPolicy(int retryCount = 3, int initialWaitMs = 3000, double factor = 3, bool fastFirst = true)
    {
        PolicyAsync = Policy
            .Handle<SqlException>()
            .CircuitBreakerAsync(exceptionsAllowedBeforeBreaking: exceptionsAllowedBeforeBreaking,
                durationOfBreak: TimeSpan.FromSeconds(durationofBreakSeconds));
    }

then to consume these, using a factory :

public class PolicyFactory : IPolicyFactory
{
    public IAsyncPolicy GetAsync(PolicyType policyType)
    {
        switch (policyType)
        {
            case PolicyType.RetryPolicy:
                return new MyRetryPolicy(3, 3000, 3, true);
            case PolicyType.CircuitBreakerPolicy:
                return new MyCircuitBreakerPolicy(2, 5);
        }

    }

}

Any help Would be much appreciated !

Thanks Andrew.

Peter Csala
  • 17,736
  • 16
  • 35
  • 75
  • Welcome to StackOverflow. What is the use case that you want to solve with this interface? – Peter Csala Mar 05 '21 at 07:50
  • thanks :) It's so I can implement a Factory class which Returns IPollyPolicy of the type requested, i.e AsyncRetryPolicy or AsyncCircuitBreakerPolicy (or in the future any policy that we need) – Andrew Humphries Mar 05 '21 at 09:22
  • I think [PolicyRegistry](https://github.com/App-vNext/Polly/wiki/PolicyRegistry) is the thing what you are looking for. – Peter Csala Mar 05 '21 at 09:30
  • Does `PolicyRegistry` solve your problem or are you still looking for other solution? – Peter Csala Mar 08 '21 at 07:31
  • sorry for the delay! I am looking into 'PolicyRegistry'. I'm not completely sure this is what I'm looking for.. I need to have a common Interface that I can use define the policies, so I can utilise it in Factories and SimpleInjector amongst others and could not work out why this interface did not allow this. – Andrew Humphries Mar 09 '21 at 09:47
  • I've left a post where I detailed what is the problem with your current solution, please check it. – Peter Csala Mar 09 '21 at 10:45
  • Thanks for your update Peter. I've added more context above as to what I want to acheive. I do not want to change the type, more that I don't know why I need to?! Both .WaitAndRetryAsync() and .CircuitBreakerAsync() return a type derived from IAsyncPolicy, so I do not know why there is a problem converting either. – Andrew Humphries Mar 10 '21 at 12:16
  • Then why don't you declare your `MyRetryLogic` class like this: `public class MyRetryPolicy: IPollyPolicy`? And `public class MyCircuitBreakerPolicy : IPollyPolicy` – Peter Csala Mar 10 '21 at 12:33
  • Did it work for you? – Peter Csala Mar 18 '21 at 07:11

1 Answers1

0

If you just want to get rid of the compile time error then all you need to do is this:

PolicyAsync = (T)Convert.ChangeType(Policy.Handle<Exception>().WaitAndRetryAsync(delay), typeof(T));

Here we have converted a concrete type to a generic type by utilizing the Convert.ChangeType.

But there is a problem: MyRetryPolicy can accept only a single type: AsyncRetryPolicy.

var policy = new MyRetryPolicy<AsyncRetryPolicy>();
Console.WriteLine(policy.PolicyAsync == null);

Compile time error

var policy = new MyRetryPolicy<AsyncRetryPolicy<HttpResponseMessage>>(); //cs3011

Severity Code Description Project File Line Suppression State Error CS0311 The type 'Polly.Retry.AsyncRetryPolicy<System.Net.Http.HttpResponseMessage>' cannot be used as type parameter 'T' in the generic type or method 'MyRetryPolicy'. There is no implicit reference conversion from 'Polly.Retry.AsyncRetryPolicy<System.Net.Http.HttpResponseMessage>' to 'Polly.IAsyncPolicy'. 2021_03_05 c:\Users\pcsala\source\repos\2021_03_05\Program.cs 20 Active

Runtime error

var policy = new MyRetryPolicy<AsyncTimeoutPolicy>(); //InvalidCastException

Unhandled exception. System.InvalidCastException: Object must implement IConvertible. at System.Convert.ChangeType(Object value, Type conversionType, IFormatProvider provider) at System.Convert.ChangeType(Object value, Type conversionType)


UPDATE: Alternative
Your concrete types should not receive a type parameter.

public class MyRetryPolicy: IPollyPolicy<AsyncRetryPolicy>
{
    public AsyncRetryPolicy PolicyAsync { get; }
    ...
}

public class MyCircuitBreakerPolicy : IPollyPolicy<AsyncCircuitBreakerPolicy>
{
    public AsyncCircuitBreakerPolicy PolicyAsync { get; }
    ...
}
Peter Csala
  • 17,736
  • 16
  • 35
  • 75