In order to achieve that you need to register your HttpClient and your PolicyRegistry separately:
services.AddHttpClient("someClient");
services.AddPolicyRegistry(registry);
Then you can ask the DI to provide that registry for you:
public class SomeService
{
private readonly IHttpClientFactory factory;
private readonly IReadOnlyPolicyRegistry<string> policyRegistry;
public SomeService(IHttpClientFactory factory, IReadOnlyPolicyRegistry<string> policyRegistry)
{
this.factory = factory;
this.policyRegistry = policyRegistry;
}
...
}
Finally inside your SomeCall
you can retrieve the policy:
public async Task SomeCall(string url)
{
var client = factory.GetClient("someClient");
var policy = policyRegistry.Get<IAsyncPolicy>("type1");
await policy.ExecuteAsync(async ct => await client.SendAsync(..., ct), CancellationToken.None);
}
UPDATE #1: Create new policy per url
In functional programming there is a well-known technique, called Memoization. It basically memorizes / caches the output of a method based on the received parameters. In other words if the method receives the same input then it emits the result from a cache rather than re-executing it.
This technique works fine for pure functions. They return the same value if they receive the exact same input in a side effect free way.
So, if we can have a solution which
- creates a new policy if the url is new
- or returns a cached policy if the url is an "already seen" one
then we are good to go.
Here is a simple implementation, but you can find a lots of variations like 1, 2, 3, etc.
public interface IMemoizer<K, V> where K : IComparable
{
Func<K, V> Memoize(Func<K, V> toBeMemoized);
}
public sealed class Memoizer<K, V> : IMemoizer<K, V> where K: IComparable
{
public static readonly ConcurrentDictionary<K, Lazy<V>> memoizedValues;
static Memoizer()
{
memoizedValues = new ConcurrentDictionary<K, Lazy<V>>();
}
public Func<K, V> Memoize(Func<K, V> toBeMemoized)
=> (K input) => memoizedValues.GetOrAdd(input, (K param) => new Lazy<V>(() => toBeMemoized(param))).Value;
}
Here is a sample for usage:
static void Main()
{
var memoizer = new Memoizer<string, IAsyncPolicy<HttpResponseMessage>>();
var policyMemoizer = memoizer.Memoize(GetSamplePolicy);
var gPolicy1 = policyMemoizer("https://google.com");
var soPolicy = policyMemoizer("https://stackoverflow.com");
var gPolicy2 = policyMemoizer("https://google.com");
Console.WriteLine(gPolicy1 == gPolicy2); //true
Console.WriteLine(gPolicy1 == soPolicy); //false
var policyMemoizer2 = memoizer.Memoize(GetSamplePolicy);
var gPolicy3 = policyMemoizer2("https://google.com");
Console.WriteLine(gPolicy1 == gPolicy3); //true
}
static IAsyncPolicy<HttpResponseMessage> GetSamplePolicy(string _)
=> Policy<HttpResponseMessage>
.Handle<HttpRequestException>()
.WaitAndRetryAsync(3, _ => TimeSpan.FromSeconds(2));
You can register the memoizer as a singleton and you are ready to use it. :D
I hope it helps you to achieve the desired behaviour.