Using Polly for retrial policies with Autofac
Image from Pexels by Magda Ehlers

Using Polly for retrial policies with Autofac

Confguring and using Polly with with pretty much anything

Some time ago I wrote an article which explains how to Increase service resilience using Polly and retry pattern in ASP.NET Core. This is a great way how to easily implement retrials when using .NET Core dependency injection, but in case of using Autofac with .NET Framework 4.x you do not have many out of the box solutions. 

However, Polly as a library is not specifically built for .NET Core and using it with other dependecy injection packages such as Autofac is fairly easy. The whole concept of Polly is around policies which will execute your code.

To demonstrate this I will first create a simple example which does not rely on any dependency injection, just simple code with HttpClient and policy that will execute GetAsync method on of the HttpClient instance.

using Polly;
using Polly.Extensions.Http;
using Polly.Retry;
using System;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
namespace Retry.Sample
{
class Program
{
static async Task Main(string[] args)
{
AsyncRetryPolicy<HttpResponseMessage> _retryPolicy = HttpPolicyExtensions
.HandleTransientHttpError()
.OrResult(msg => msg.StatusCode == HttpStatusCode.Unauthorized || msg.StatusCode == HttpStatusCode.NotFound)
.WaitAndRetryAsync(3, attempt => TimeSpan.FromSeconds(2));
HttpClient client = new HttpClient()
{
BaseAddress = new Uri("https://example.com/")
            };

            var response = await _retryPolicy.ExecuteAsync(async () =>
            {
                return await client.GetAsync(new Uri("/notfound"));
            });

            response.EnsureSuccessStatusCode();
        }
    }
}


    

If you put a breakpoint inside on line 23, you will see that it will execute GetAsync method 4 times (once initially and 3 times for each retrial) after which it will throw an exception. It is pretty easy this way, but let's see how can we utilize Autofac to inject the policy.

I will use ASP.NET project for an example and will use Autofac to inject both HttpClient instance and the policy that I can use in the code for retrials. If you navigate to definition of AsyncRetryPolicy<HttpResponseMessage> you will see that it implements IAsyncPolicy<TResult> which has definition of ExecuteAsync method in its signature, which is the method we are using to execute GetAsync of the HttpClient instance. This will make unite testing a lot easier as we can easily mock the interface.

For this reason we are going to setup and access our retry policy as IAsyncPolicy<TResult> and than just use ExecuteAsync.

As for the HttpClient instance, it will not be thet easy to write unite tests, but there is a way to mock HttpClient same way I managed to mock HttpClient behavior when unit testing code where IHttpClientFactory. You can checkout the details in article Mocking HttpClient in unit tests with Moq and Xunit when using IHttpClientFactory

using Autofac;
using Autofac.Integration.WebApi;
using Polly.Extensions.Http;
using Polly.Retry;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Reflection;
using System.Web;
using System.Web.Http;
using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing;

namespace Polly.Sample
{
    public class WebApiApplication : System.Web.HttpApplication
    {
        protected void Application_Start()
        {
            var builder = new ContainerBuilder();
            var config = GlobalConfiguration.Configuration;
            builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
            builder.RegisterWebApiFilterProvider(config);
            builder.RegisterWebApiModelBinderProvider();

            var httpClient = new HttpClient()
            {
                BaseAddress = new Uri("https://example.com/")
            };
            httpClient.DefaultRequestHeaders.Add("User-Agent", "HttpClient-Service");

            AsyncRetryPolicy<HttpResponseMessage> retryPolicy = HttpPolicyExtensions
                       .HandleTransientHttpError()
                       .OrResult(msg => msg.StatusCode == HttpStatusCode.Unauthorized || msg.StatusCode == HttpStatusCode.NotFound)
                       .WaitAndRetryAsync(3, attempt => TimeSpan.FromSeconds(2));

            builder.RegisterInstance<HttpClient>(httpClient).SingleInstance();
            builder.RegisterInstance(retryPolicy).As<IAsyncPolicy<HttpResponseMessage>>();

            var container = builder.Build();
            config.DependencyResolver = new AutofacWebApiDependencyResolver(container);

            GlobalConfiguration.Configure(WebApiConfig.Register);
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
            RouteConfig.RegisterRoutes(RouteTable.Routes);

        }
    }
}

    
Note

Since there is no implementation of IHttpClientFactory in .NET Framework I am creating a singleton instance to avoid port exhaustion issue (https://aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong/) which again has it's downsides like no DNS update checks since single connection is reused. If possible, switch to .NET Core and use IHttpClientFactory

Now to access injected instances in controller constructor we just need to add them

using System.Net.Http;
using System.Threading.Tasks;
using System.Web.Http;

namespace Polly.Sample.Controllers
{
    public class ValuesController : ApiController
    {
        readonly HttpClient _httpClient;
        readonly IAsyncPolicy<HttpResponseMessage> _retryPolicy;
        public ValuesController(HttpClient httpClient, IAsyncPolicy<HttpResponseMessage> retryPolicy)
        {
            _httpClient = httpClient;
            _retryPolicy = retryPolicy;
        }

        // GET api/values
        public async Task<IHttpActionResult> Get()
        {
            var response = await _retryPolicy.ExecuteAsync(async () =>
            {
               return await _httpClient.GetAsync("/notfound");
            });
            response.EnsureSuccessStatusCode();
            return Ok();
        }

    }
}

    

Same way as in the first code snippet, if you put the breakpoint to line 19 and run the debug you will get it hit 4 times (initial attempt plus 3 retries as defined in the policy). 

References

Disclaimer

Purpose of the code contained in snippets or available for download in this article is solely for learning and demo purposes. Author will not be held responsible for any failure or damages caused due to any other usage.


About the author

DEJAN STOJANOVIC

Dejan is a passionate Software Architect/Developer. He is highly experienced in .NET programming platform including ASP.NET MVC and WebApi. He likes working on new technologies and exciting challenging projects

CONNECT WITH DEJAN  Loginlinkedin Logintwitter Logingoogleplus Logingoogleplus

JavaScript

read more

SQL/T-SQL

read more

Umbraco CMS

read more

PowerShell

read more

Comments for this article