Send parameters to fallback Action in Polly

2.1k Views Asked by At

I am trying to execute a request and return a response with 2 executers, using the Polly library with the Fallback policy. Here is my code so far:

private IExecuter primary;
private IExecuter secondary;
private readonly Policy _policy;
public Client()
{
    primary = new Executer("primary");
    secondary = new Executer("secondary");

    _policy = Policy
        .Handle<Exception>()
        .Fallback((request) => GetResponseFallback(request));
}

public Response Process(Request request)
{
    return _policy.Execute( GetResponse(request));
}

private Response GetResponse(Request request)
{
    return this.primary.Execute(request);
}

private Response GetResponseFallback(Request request)
{
    return secondary.Execute(request);
}

The problem with this code is that I can't put a Request parameter on this line: .Fallback((request) => GetResponseFallback(request));

And the code won't compile, because of the error:

"cannot convert from 'System.Threading.CancellationToken' to 'PollyPOC.Request'"

How to make the code compile and tell the fallback policy that the action will receive a Request parameter to the GetResponseFallback action?

1

There are 1 best solutions below

3
On BEST ANSWER

In order to pass data to your Fallback method you need to take advantage of the Context

For the sake of simplicity let me simplify your implementation a bit to show you how to use the Context:

private int GetResponse(int request)
{
    return request + 1;
}

private int GetResponseFallback(int request)
{
    return request + 2;
}

So I've just get rid of the IExecuter, Request and Response concepts to keep the sample code as simple as possible.


To pass data to your GetResponseFallback we need to change the parameter like this:

private const string requestKey = "request"; 
private int GetResponseFallback(Context ctx)
{
    var request = (int)ctx[requestKey];
    return request + 2;
}

So, we have retrieved the request data from the Context. Now we need to somehow pass that information to the GetResponseFallback call.

The good thing is that we don't need to modify the GetResponse. On the other hand we need to adjust the Process method like this:

private int Process(int request)
{
    var contextData = new Dictionary<string, object> { { requestKey, request } };

    _policy = Policy<int>
        .Handle<Exception>()
        .Fallback((ctx) => GetResponseFallback(ctx), (_, __) => { });

    return _policy.Execute((_) => GetResponse(request), contextData);
}
  • We have created a contextData dictionary to store the input parameter in it
  • We have used a special overload of the Fallback where we have to define two delegates:
    • Unfortunately there is no overload where we have to provide only a Action<Context> fallbackAction parameter
public static FallbackPolicy Fallback(this PolicyBuilder policyBuilder, Action<Context> fallbackAction, Action<Exception, Context> onFallback)
  • Because we don't want to do anything inside the onFallback that's why I defined that like this (_,__) => { }
  • We have called a special overload of Execute, which has the following syntax:
public void Execute(Action<Context> action, IDictionary<string, object> contextData)
  • Because we are not passing the Context parameter to the GetResponse directly that's why we can use here the discard operator (_) => GetResponse(...)

One last important thing: I had to change the _policy variable type to Policy<int> to indicate the return type.


For the sake of completeness here is the full source code:

private static Policy<int> _policy;
static void Main(string[] args)
{
    Console.WriteLine(Process(1));
}

private const string requestKey = "request";
static int Process(int request)
{
    var contextData = new Dictionary<string, object> { { requestKey, request } };

    _policy = Policy<int>
        .Handle<Exception>()
        .Fallback((ctx) => GetResponseFallback(ctx), (_, __) => { });

    return _policy.Execute((_) => GetResponse(request), contextData);
}

static int GetResponse(int request)
{
    throw new Exception();
    return request + 1;
}

static int GetResponseFallback(Context ctx)
{
    var request = (int)ctx[requestKey];
    return request + 2;
}