Registering Multiple Implementations of an Interface in ASP.NET Core with Autofac

Full source code here.

A few weeks ago I wrote a post about using dependency injection to pick between two implementations of an interface. It was a solution I was not very happy with because it meant I had to new up the implementations inside a factory or I had to use service collection to instantiate all implementations of the interface and then use a piece of code to return the one the was wanted.

As of this writing service collection does not natively support choosing between two implementations of a interface, but Autofac does and it can be used in place of service collection, or alongside it.

In this post I’m going to show its use alongside the service collection just for the dependencies that have multiple implementations, I’ll use service collection for dependencies that have a single collections. The examples are for ASP.NET Web Api Core 2.x and Core 3; there are small variations in how to use Autofac in those two versions of Core.

The Scenario
I have three controllers, the NegativeValuesController and PositiveValuesController take the same IValuesService and the third controller ProductsController takes an IProductsService.

The NegativeValuesController should get a NegativeValuesService and the PositiveValuesController should get a PositiveValuesService.

I’m going to use Autofac to provide the dependency injection for these controllers.

The ProductsController will use the built in ServiceCollection to fulfill its dependency.

Core 2.x
Start by adding the Autofac.Extensions.DependencyInjection package to the application.

Inside Program.cs add add .ConfigureServices(services => services.AddAutofac()) to the CreateWebHostBuilder statement.

public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
	WebHost.CreateDefaultBuilder(args)
	.ConfigureServices(services => services.AddAutofac())
	.UseStartup<Startup>();

In Startup.cs add this to the ConfigureServices(..) method –

services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1)
	 .AddControllersAsServices(); // these two lines have to come before  
services.AddScoped<IProductsService, ProductsService>(); // you populte the builder a below

var builder = new ContainerBuilder();
builder.Populate(services);

builder.RegisterType<NegativeValuesService>().As<IValuesService>().AsSelf();
builder.RegisterType<PositiveValuesService>().As<IValuesService>().AsSelf();
builder.Register(ctx => new NegativeValuesController(ctx.Resolve<NegativeValuesService>()));
builder.Register(ctx => new PositiveValuesController(ctx.Resolve<PositiveValuesService>()));

AutofacContainer = builder.Build();

return new AutofacServiceProvider(AutofacContainer);

The controllers work in the normal, familar way –

public NegativeValuesController(IValuesService valuesService)
{
    _valuesService = valuesService;
}
public PositiveValuesController(IValuesService valuesService)
{
    _valuesService = valuesService;
}

That’s it, you can now choose the implementation you want for a controller.

Core 3
If you want to the same in Core 3, there are few small differences.

As before, start by adding the Autofac.Extensions.DependencyInjection package to the application.

In Program.cs add .UseServiceProviderFactory(new AutofacServiceProviderFactory()) to the CreateWebHostBuilder statement.

Back in Startup.cs add this method –

public void ConfigureContainer(ContainerBuilder builder)
{
    builder.RegisterType<NegativeValuesService>().As<IValuesService>().AsSelf();
    builder.RegisterType<PositiveValuesService>().As<IValuesService>().AsSelf();

    builder.Register(ctx => new NegativeValuesController(ctx.Resolve<NegativeValuesService>()));
    builder.Register(ctx => new PositiveValuesController(ctx.Resolve<PositiveValuesService>()));
}

Edit the ConfigureServices method to include the following –

public void ConfigureServices(IServiceCollection services)
{
	//snip..
	services.AddScoped<IProductsService, ProductsService>();

	services.AddControllers();
	services.AddMvc().AddControllersAsServices();
}

The usage inside the controllers does not change.

There you go, injecting multiple implementations of a interface in ASP.NET Core 2 and 3.

Full source code here.

Simmy Chaos Engine for .NET – Part 4, Doing Some Real Damage, Dropping a Table

Full source code here.

Up until now the Simmy examples I’ve written have thrown exceptions, changed successes to failures or slowed down a request. None of these did any real damage to your system, and your application would probably have recovered when the next request came along.

But what about dropping a table? Poof, all the data is gone. Now what does your application do?

Doing this is very easy with the Behavior clause. Like the other clauses it takes a probability, an enabled flag, and action or func that executes any piece of code.

The Scenario
I have Web API application with a products controller that queries the database for a list of products.

During application start up the database is created and populated in Configure method of Startup.cs using the EnsureCreated() method and custom seed class.

Inside the controller the action method uses the SalesContext to query the database.

Inside the controller’s constructor I have the chaos policy, it is set to drop the Products table. The drop action of the behavior policy will execute 25% of the time the policy is run. If get an error when you run this saying that the table is absent, it means the chaos policy dropped the table, you’ll have to restart the application to run through the database seeding and creation again.

The policy looks like this –

public ProductsController(SalesContext salesContext)
{
	_behaviorPolicy = MonkeyPolicy.InjectBehaviourAsync(
		 async () =>
		 {
			 await _salesContext.Database.ExecuteSqlCommandAsync("DROP TABLE Products");
			 Console.WriteLine("Dropped the Products table.");
		 },
		 0.25, // 25% of the time this policy will execute
		async () => await Task.FromResult(true));
	_salesContext = salesContext;
}

The request to the _salesContext is made inside the policy, but the policy executes before the call the db is made and in 25% of such calls the table will be dropped.

[HttpGet]
public async Task<ActionResult> Get()
{
	List<Product> products =  await _behaviorPolicy.ExecuteAsync(async () => await _salesContext.Products.Take(10).ToListAsync());

	return Ok(products);
}

You can of course execute and code from inside the policy, wipe a cache, kill a service, delete a file, etc.

This example might be a little excessive in the damage it does, you can decide if it is unreasonable to expect you application to continue working even when the database is unavailable.

Full source code here.

Registering Multiple Implementations of an Interface with Service Collection in ASP.NET Core

Full source code here.

This is a simplistic approach to the problem and assumes that the dependencies created by the factory do not in turn have their own dependencies. This deficiency could be mediated by passing those dependencies into the factory. Having said that it will be of use in some scenarios. I will publish a post in the future providing a better solution.

In a later post I will show how to use Autofac to do this properly.

The out-of-the-box dependency injection container provided by Microsoft as part of ASP.NET Core is fine for most scenarios, only in a few complicated cases have I needed to switch to Castle or AutoFac.

But there is one somewhat common case where the ServiceCollection feels a little deficient. There are times when you might have more than one implementation of an interface and you would like to choose between them at runtime.

There is no obvious way to do this.

You could try something like –

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddScoped<IValuesService, PositiveValuesService>();
            services.AddScoped<IValuesService, NegativeValuesService>();
	//snip...

But when you request an implementation of IValuesService in the constructor of a controller, the service collection will return the last registered service, in this case it will always return the NegativeValuesService.

But what if you wanted the PositiveValuesService in some controllers and the NegativeValuesService in other controllers. What if you needed to choose the service implementation based on the address of the request that the controller is handling.

Something like this –
localhost:5000/NegativeValues – should use the NegativeValuesService.
localhost:5000/PositiveValues – should use the PositiveValuesService.

Using a Factory
Instead of registering two implementations of the IValuesService, I’m going to create a ValuesServiceFactory that returns the right service based on the path of the request.

The ValuesServiceFactory has a method that looks like this –

public IValuesService GetValuesService()
{
    string path = _httpContextAccessor.HttpContext.Request.Path.Value.ToString().Trim('/').ToLower();
    switch (path)
    {
        case "positivevalues":
            return new PositiveValuesService(); // as mentioned above this only works if the values services have no dependencies
        case "negativevalues":
            return new NegativeValuesService();
        default:
            return new PositiveValuesService();
    }
}

_httpContextAccessor is what lets me access the Request from inside the factory.

A few weeks ago I wrote about post about accessing the HttpRequest from inside the constructor of a controller or from the constructor of a service the controller depends on.

That post had a contrived example, but this post has a more practical use of the technique.

The constructor of the ValuesServiceFactory needs to take an IHttpContextAccessor.

public ValuesServiceFactory(IHttpContextAccessor httpContextAccessor)
{
    _httpContextAccessor = httpContextAccessor;
}

Back in Startup.cs, remove the code registering the two services and replace with a line to register the factory and the HttpContextAccessor.

public void ConfigureServices(IServiceCollection services)
{
    services.AddScoped<IValuesServiceFactory, ValuesServiceFactory>();
    services.AddHttpContextAccessor();
    //snip…
}

The controllers now take the implementation of IValuesServiceFactory and request the an IValuesService from the factory.

private readonly IValuesService _valuesService;

public NegativeValuesController(IValuesServiceFactory valuesServiceFactory)
{
    _valuesService = valuesServiceFactory.GetValuesService();
}

Do the same in the other controller –

private readonly IValuesService _valuesService;

public PositiveValuesController(IValuesServiceFactory valuesServiceFactory)
{
    _valuesService = valuesServiceFactory.GetValuesService();
}

That’s it, now you can choose between implementation of an interface. As I was working on this post I came across a post by Steve Gordon on this topic, he solves the same problem in a different way.

Full source code here.

Accessing the HttpContext from the Constructor of a Controller or a Dependency

Full source code here.

There are times when you may need to access the HttpRequest from places that it is not normally available such as the constructor of a controller or the constructor of a service that is built by dependency injection.

Take the example of a ValuesController that relies on a NumberGeneratorService, where both need to know the Host used in the request.

But you can’t just access the Host via the Request in either case. It is not yet available in the lifetime of the controller and it will not be available at all in the life of the service. (By the time you reach the action method of the controller you will have access to it.)

Fortunately there is a way to get at it using the HttpContextAccessor.

In Startup.cs add the following –

public void ConfigureServices(IServiceCollection services)
{
    services.AddScoped<INumberGeneratorService, NumberGeneratorService>();
    services.AddHttpContextAccessor();
    //snip..
}

Pass IHttpContextAccessor to the constructor of the ValuesController

public ValuesController(IHttpContextAccessor httpContextAccessor, INumberGeneratorService numberGeneratorService)
{
    _numberGeneratorService = numberGeneratorService;
    Console.WriteLine($"Request.Host inside constructor of controller : {httpContextAccessor.HttpContext.Request.Host.Value}");
}

Do the same to the constructor of the the NumberGeneratorService

public NumberGeneratorService(IHttpContextAccessor httpContextAccessor)
{
    _httpContextAccessor = httpContextAccessor;
    Console.WriteLine($"Request.Host inside service : {httpContextAccessor.HttpContext.Request.Host.Value}");
}

Now you have full access to everything about the request in controller’s constructor and the services you depend on.

In another post I’ll show a more practical use of this when I want to use the dependency injection with two implementations of an interface, HttpContextAccessor will help me choose between them based on the request.

Full source code here.

Simmy Chaos Engine for .NET – Part 3, Adding Latency

Full source code here.

This is the third in my series on chaos engineering with Simmy. The first two posts dealt with the fault policy throwing exceptions and returning failed responses rather than calling the remote service.

In this I will show how to use the latency policy to inject delays into requests. It works in a similar way to the fault policy – a delay is chosen, the percentage of the time the delay should apply is chosen, and finally the policy is enabled/disabled.

When the policy is active, it delays the request, not the response.

var latencyPolicy = MonkeyPolicy.InjectLatencyAsync<HttpResponseMessage>(
    TimeSpan.FromSeconds(5), // inject a 5 second delay
    0.5, // in 50% of requests
    () => true); // policy is enabled

To see the effect of the latency policy I am going to add Polly timeout policy that throws an TimeoutRejectedException if no response is received from the OpenBreweryDb in 2 seconds.

I have also added a Polly retry policy to retry request that fail or timeout.

IAsyncPolicy<HttpResponseMessage> timeoutPolicy = Policy.TimeoutAsync<HttpResponseMessage>(2); 

The retry policy is set to retry up to three times.

IAsyncPolicy<HttpResponseMessage> retryPolicy = Policy
                .HandleResult<HttpResponseMessage>(r => !r.IsSuccessStatusCode)
                .Or<Exception>()
                .RetryAsync(3)

I have a policy wrap with the latency policy at the center, the timeout around it and finally the retry around that.

IAsyncPolicy<HttpResponseMessage> latencyTimeoutAndRetryPolicy = Policy.WrapAsync(
    retryPolicy, timeoutPolicy, latencyPolicy);

What happens is –
In 50% of the requests to the OpenBreweryDb, the latency policy delays the request by 5 seconds.
If the latency is active, the timeout policy throws a TimeoutRejectedException.
Then retry policy retries the request again.

If you run the application you will see the timeout and retry policies logging to the console.

That’s it, the latency policy is very easy to use.

Full source code here.

Simmy Chaos Engine for .NET – Part 2, Resilience and Injected Faults

Full source code here.

I wrote a blog on using a Simmy Fault policy a few days ago, it is very simple to use by itself, but it is far more useful when used in combination with a resilience policy.

Take the scenario where I have added retry logic to my requests, but the requests never fail, how do I know the retry logic works the way I expect. This is where the fault policy shines.

I’m going to build on the previous post where I injected a fault 50 percent of the time when making http requests. In the previous post I had Simmy throw a HttpRequestException but this time I’m going to have Simmy return a HttpResponseMessage containing an InternalServerError.

Along with the fault policy, I am going add a simple retry policy, retrying up to three times.

Here’s how it will work –

The fault policy executes –

If the fault policy is active and a fault is to be returned (remember this happens 50% of the time) a InternalServerError is returned. The HttpClient does NOT make a request to the remote service

The retry policy executes. The handles clause sees the InternalServerError and the behavior clause of the retry kicks in and performs the retry

The fault policy executes again

The preceding two steps repeat until a request succeeds or the retry policy reaches is limit of 3 retries

 

If the fault policy is not active, or a fault is NOT to be returned

the HTTP client executes the request to the remote service

the handles clause of the retry determines whether a retry should be performed

 

This is what it looks like –

At the center is the HttpReqeust, around it is the fault policy and around everything is the retry policy.

To achieve all this in code is very simple, in ConfigureServices add the fault policy.

public void ConfigureServices(IServiceCollection services)
{
	HttpResponseMessage faultHttpResponseMessage = new HttpResponseMessage(HttpStatusCode.InternalServerError)
	{
		Content = new StringContent("Simmy swapped the Ok for an Internal Server Error")
	};
	AsyncInjectOutcomePolicy<HttpResponseMessage> faultPolicy = MonkeyPolicy.InjectFaultAsync(
		faultHttpResponseMessage,
		injectionRate: .5,
		enabled: () => true 
	);

When the fault policy executes, 50% of the time it will return an InternalServerError response, preempting the real request to the remote service.

Next is the retry policy, its behavior clause triggers if the HttpResponseMessage is not a success.

IAsyncPolicy<HttpResponseMessage> retryPolicy = Policy
	.HandleResult<HttpResponseMessage>(r => !r.IsSuccessStatusCode)
	.RetryAsync(3, onRetry: (message, retryCount) =>
	{
		Console.WriteLine($"Retry: {retryCount}");
	});

Wrap the retry policy around the fault policy.

AsyncPolicyWrap<HttpResponseMessage> faultAndRetryWrap = Policy.WrapAsync(retryPolicy, faultPolicy);

Finally, create the HttpClientFactory, passing in the policy wrap. If you are not familiar with how the HttpClientFactory works read Steve Gordon’s series or my blog posts on it. If you want know how it works with Polly, take a look at my posts on that topic.

services.AddHttpClient("OpenBreweryDb", client =>
{
	client.BaseAddress = new Uri("https://api.openbrewerydb.org/breweries/");
	client.DefaultRequestHeaders.Add("Accept", "application/json");
}).AddPolicyHandler(faultAndRetryWrap);

To fire of the HttpClient request inside the BreweryController remains the same –

string requestEndpoint = $"?by_state={state}&by_name={name}";
var httpClient = _httpClientFactory.CreateClient("OpenBreweryDb");
var response = await httpClient.GetAsync(requestEndpoint);

Full source code here.

Simmy Chaos Engine for .NET – Part 1, Injecting Faults

Full source code here.

For quite a while I have been writing, presenting and making videos about using Polly to make applications more stable.

With this post I’m starting a series on how break your applications with a chaos engine, the kind you might have heard about from the likes of NetFlix.

The team behind Polly released a chaos engineering module in June 2019. It allows you to inject faults, latency and arbitrary behavior into your application.

There are two general ways of using these policies –
1. Within your own business logic (if you have access to it).
2. Within the calls to business logic (if you can’t or don’t want to change it).

I’m going to deal with the latter case in this post. I want to see how my code responds when failures occur in third party dependencies, since I have no control over those dependencies I have to introduce the errors myself.

Making a good service go bad
In the provided code I have dependency on remote service and I want make it seem that that service suffers from a high percentage of faults.
This example will make GET requests to an API that looks up breweries, from what I can tell it is a highly reliable API, but I will make look as though 50% of requests to it fail. The point of doing this is to see how my application reacts to these errors – does it do so gracefully, or does it fall on its face.

If you made requests to the API without interfering with them you should see a 100% success rate. But I want to see failures, so I’ll use the Fault Policy to inject exceptions.

First, I added two NuGet packages to my project – Polly.Contrib.Simmy and Microsoft.Extensions.Http.Polly to the project.

In Startup.cs I add the fault policy.

AsyncInjectOutcomePolicy<HttpResponseMessage> faultPolicy = MonkeyPolicy.InjectFaultAsync<HttpResponseMessage>(
	new HttpRequestException("Simmy threw an exception"), 
		injectionRate: .5,
		enabled: () => true
	);

The parameters specify the exception to throw, the percentage of requests that throw the exception, and whether the policy is enabled. You can use set the injection rate and whether the policy is enabled via configuration, but I’m not going to show that here.
In this case, I am throwing an HttpRequestException 50% of the time, and the policy is enabled.

Then create a HttpClientFactory for the OpenBreweryDb

services.AddHttpClient("OpenBreweryDb", client =>
{
	client.BaseAddress = new Uri("https://api.openbrewerydb.org/breweries/");
	client.DefaultRequestHeaders.Add("Accept", "application/json");
}).AddPolicyHandler(faultPolicy);

If you are not yet using the HttpClientFactory, the source code has an example for you too, check the BreweryNoHttpClientFactoryController.

Start up the application, it should open on port 5000 and make a request to look up Night Shift Brewing in Massachusetts. There is a 50% chance of an exception occurring with each request.

Try it out. You’ll see that my application does not handle these failures very well, it simply returns the error to original caller.

That’s it, you are now starting down the road of mixing in some chaos into your application.

What then?
Great, my application fails, but we always knew it could. There’s not much point introducing failures unless you plan to handle them in some way, and this is where you might add a retry policy to improve you likelihood of a successful request.

Of course, the Polly retry, or wait and retry policies would be ideal. I’ll show how to do just that in the next post.

Full source code here.

Streaming Results from Entity Framework Core and Web API Core

Full source code here.
The code provided will not compile until you make a change in seeder.cs, the way it’s written it generates 500,000 rows in a local db. Set this to whatever value you want.

In this post I’m going to show you how to return an unlimited number of results from a database via a Web API application while keeping your memory usage low and constant. In effect, you are going to stream results from the database with Entity Framework.

To achieve this you need to do two things, disable tracking and return a not materialize your data inside the action method.

Turn off tracking
By default Entity Framework tracks entities that you read from a database. Tracking allows EF to determine what, if any, properties have changed since being loaded, then EF can save just the relevant changes. But tracking takes up memory and CPU.

If you have no intention of changing these entities there is no point in tracking them.

There are two ways of doing this, at point where you make the request to the database, or globally for the whole context.

To use AsNoTracking for a single request it looks like this –
_salesContext.Products.AsNoTracking().Where(..)

To use it for all requests to that context set AsNoTracking in the constructor of the context.

public SalesContext(DbContextOptions<SalesContext> options) : base(options)
{
	ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
}

That’s the first step taken care of, now we make sure not to turn our data into objects inside the controller.

Do not materialize
If you follow tutorials on Entity Framework Core and Web API you will see examples like this –

[HttpGet]
public async Task<ActionResult> Get()
{
    List<Product> products = await _salesContext.Products.ToListAsync();

    return Ok(products);
}

In this example, all the products in the database are read and put into a list of products, the return does not execute until after all the data has been read, so you are waiting for this to complete before getting any results. You are also storing the whole list of products in memory (you are probably also tracking the entities).

Instead of that you can do the following –

[HttpGet]
public ActionResult GetStreaming()
{
	IQueryable<Product> products = _salesContext.Products.AsNoTracking();

	return Ok(products);
}

Now, the products do not materialize, there is no list to store in memory, tracking is turned off and the action method begins returning data almost immediately.

Your memory profile will be almost same whether you are returning a few hundred or a few million rows of data.

Example of Memory Usage
Below are screenshots from Visual Studio 2017 of the application running showing the amount of memory consumed for a variety of requests. The first shows the memory usage when I requested a single row from the database

The following images show the memory usage when streaming and not streaming results sets of 100,000, 200,000, 300,000 and 500,000.

106 MB used for a single row

 

100,000 rows returned. On the left is with streaming, on the right without.

 

200,000 rows returned. On the left is with streaming, on the right without.

 

300,000 rows returned. On the left is with streaming, on the right without.

 

500,000 rows returned. On the left is with streaming, on the right without.

 

Summary of Results

It’s obvious that streaming maintains a uniform memory footprint while the memory consumed for non-streaming grows with the number of rows returned.

What you don’t see here is that streaming requests complete more quickly. If you download the attached code and use a local mdf file, you probably won’t see much of a difference in speed, but if your application connects to remote database you will see approximately a 20% improvement in speed.

Summary
For streaming to work you need to turn of tracking of entities and return an IQueryable from the action method.

Full source code here.
The code provided will not compile until you make a change in seeder.cs, the way it’s written it generates 500,000 rows in a local db. Set this to whatever value you want.

Getting Error Messages and Status Codes from Typed HttpClients

Full source code here.

I have been using HttpClientFactory for a while and have generally preferred using named clients over typed clients. With a named client you get the HttpClient just before you are going to use it, and you have full access to the HttpResponse at the point you make the call to the remote service.

var httpClient = _httpClientFactory.CreateClient("InvoiceApi");
HttpResponseMessage response = await httpClient.GetAsync(requestEndpoint);

This is great when things go wrong because you can check the HttpStatus code and the ReasonPhrase, any message, and then take appropriate action.

If you are using a typed client, it might look something like this –

public class InvoiceClient
{
	private readonly HttpClient _client;

	public InvoiceClient(HttpClient client)
	{
		_client = client;
	}
	
	public async Task<Invoice> GetInvoice(int orderId)
	{
		var response = await _client.GetAsync($"invoice/{orderId}");
		Invoice invoice = await response.Content.ReadAsAsync<Invoice>();
		return invoice;
	}
}

And then you use it –

var invoice = await invoiceClient.GetOrder(id);

If the request worked, you are fine, but if it didn’t you and got an error, then the deseriaization will fail and an exception will be thrown, but that exception won’t tell you very much about the cause of the original failure.

Returning a typed response
Here’s the alternative. I use a typed response that includes the HttpStatusCode, the ReasonPhrase and the content I actually want, in this case the Invoice.

public class TypedResponse<T>
{
	public T Content { get; set; }
	public HttpStatusCode HttpStatusCode { get; set; }
	public string ReasonPhrase { get; set; }
	public string Message { get; set; }
}

Here’s how to change the Get method in the Invoice.

[HttpGet("{orderId}")]
public async Task<ActionResult> Get(int orderId)
{
	// Pretend to load the order from the db. 
	Order order = new Order { OrderId = orderId, OrderDate = new DateTime(2018, 6, 14), CustomerId = 1001 };

	var invoiceResponse = await _invoiceClient.GetInvoice(orderId);

	if (invoiceResponse.Content != null)
	{
		order.Cost = invoiceResponse.Content.Cost;
		return Ok(order);
	}
	return StatusCode((int)invoiceResponse.HttpStatusCode, invoiceResponse.Message);
}

And finally, how to use it in the controller.

var invoiceAndStatus = await _invoiceClient.GetInvoice(orderId);

if (invoiceAndStatus.Content != null)
{
    order.Cost = invoiceAndStatus.Content.Cost;
    return Ok(order);
}
return StatusCode((int)invoiceAndStatus.HttpStatusCode, invoiceAndStatus.Message);

Run the code, it will open your browser to http://localhost:5000/api/order/2. The OrderController will call the InvoiceController, the first request will fail, but you will see the error message from the InvoiceController.

Hit F5 in the browser to make another request to the OrderController, again this calls the InvoiceController, but this time the response is a success.

Full source code here.

Selectively Caching Values Inside HttpResponseMessage with Polly – caching series 3/3

Full source code here.

This is the last of three posts on caching with Polly. The first showed how to selectively cache HttpResponseMessages based on the status code of the response.

The second showed how to cache values inside the HttpResponseMessage rather than the whole of the response.

This final post will show how to selectively cache values inside the response based on the status of the response.

If you have read the previous posts the content of this will feel familiar.

Changes to Startup
Like the previous post, add a memory cache, a policy registry and a HttpClientFactory to the service collection.

public void ConfigureServices(IServiceCollection services)
{
    services.AddMemoryCache();
    services.AddSingleton<IAsyncCacheProvider, MemoryCacheProvider>();

    IPolicyRegistry<string> registry = services.AddPolicyRegistry();

    services.AddHttpClient("RemoteServer", client =>
    {
        client.BaseAddress = new Uri("http://localhost:5000/api/");
        client.DefaultRequestHeaders.Add("Accept", "application/json");
    });

    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}

What’s new is in the Configure method, fist step is to add the registry to the method parameters, DI will take care of this for us.

public void Configure(IApplicationBuilder app, IHostingEnvironment env,
    IAsyncCacheProvider cacheProvider, IPolicyRegistry<string> registry)
{

Next is the cache filter, this one examines the tuple of int and HttpStatusCode and, if the HttpStatusCode is an OK the tuple will be cached for the duration specified. If the respones is not OK the filter will set the cache time to 0 which is interpreted as do not cache by the cache policy.

Func<Context, (int value, HttpStatusCode StatusCode), Ttl> cacheOnly200OkFilter =
	(context, result) => new Ttl(
		timeSpan: result.StatusCode == HttpStatusCode.OK ? TimeSpan.FromSeconds(5) : TimeSpan.Zero,
		slidingExpiration: true
	);

And this is the cache policy that uses the filter above to store the tuple.

IAsyncPolicy<(int, HttpStatusCode)> cachePolicy =
	Policy.CacheAsync<(int, HttpStatusCode)>(
		cacheProvider.AsyncFor<(int, HttpStatusCode)>(), 
		new ResultTtl<(int, HttpStatusCode)>(cacheOnly200OkFilter),
		onCacheError: null);

Lastly, add the policy to the registry.

registry.Add("cacheOnly200OkFilter", cachePolicy);

Changes to the Controller
Over in the CatalogController there many changes.

At the top I take in the HttpClientFactory and policy registry.

private readonly IHttpClientFactory _httpClientFactory;
private readonly IPolicyRegistry<string> _policyRegistry;

public CatalogController(IHttpClientFactory httpClientFactory, IPolicyRegistry<string> policyRegistry)
{
	_httpClientFactory = httpClientFactory;
	_policyRegistry = policyRegistry;
}

Inside the Get method, I setup grab the HttpClient from the HttpClientFactory, set the endpoint of the remote service, take the cache policy from the registry and setup the context for the cache policy to cache based on the incoming request. All of this is similar to normal usage of the Polly cache.

[HttpGet("{id}")]
public async Task<IActionResult> Get(int id)
{
    string requestEndpoint = $"inventory/{id}";
    var httpClient = _httpClientFactory.CreateClient("RemoteServer");

    var cachePolicy = _policyRegistry.Get<CachePolicy<(int, HttpStatusCode)>>("cacheOnly200Ok");
    Context policyExecutionContext = new Context($"GetInventoryById-{id}");	

As in the previous post I don’t call the HttpClient from inside the cache policy, instead I need to call something that returns the int and the HttpStatusCode. I use a local function that makes the request to the remote service, checks the response, deserializes the value and returns a tuple.

(int ItemsInStock, HttpStatusCode StatusCode) itemsInStockAndStatusCode =
    await cachePolicy.ExecuteAsync(context => MakeRequest(), policyExecutionContext);

// local function
async Task<(int count, HttpStatusCode statusCode)> MakeRequest()
{
    HttpResponseMessage response = await httpClient.GetAsync(requestEndpoint);

    if (response.IsSuccessStatusCode)
    {
        int itemsInStock = await response.Content.ReadAsAsync<int>();
        return (itemsInStock, response.StatusCode);
    }

    return (0, response.StatusCode);
}

When the cachePolicy is executed it first checks if there is value already in the cache, if there is, that will be returned and the local function will not be called. If there is no value in the cache, the local function is called, if the response of the local function is in the 200 range, the value is stored in the cache.

At this point, the action method has not returned anything to its caller.

Below the local function I check the tuple returned from the call to the cache policy and return a response.

if (itemsInStockAndStatusCode.StatusCode != HttpStatusCode.OK)
{
     return StatusCode((int)itemsInStockAndStatusCode.StatusCode);
}
return Ok(itemsInStockAndStatusCode.ItemsInStock);

For clarity, here is the full Get method.

[HttpGet("{id}")]
public async Task<IActionResult> Get(int id)
{
    string requestEndpoint = $"inventory/{id}";
    var httpClient = _httpClientFactory.CreateClient("RemoteServer");

    var cachePolicy = _policyRegistry.Get<CachePolicy<(int, HttpStatusCode)>>("cacheOnly200Ok");
    Context policyExecutionContext = new Context($"GetInventoryById-{id}");

    (int ItemsInStock, HttpStatusCode StatusCode) itemsInStockAndStatusCode =
        await cachePolicy.ExecuteAsync(context => MakeRequest(), policyExecutionContext);

    async Task<(int count, HttpStatusCode statusCode)> MakeRequest()
    {
        HttpResponseMessage response = await httpClient.GetAsync(requestEndpoint);

        if (response.IsSuccessStatusCode)
        {
            int itemsInStock = JsonConvert.DeserializeObject<int>(await response.Content.ReadAsSt
            return (itemsInStock, response.StatusCode);
        }

        // it doesn't matter what int you use here, it won't be cached as the StatusCode is not a
        return (0, response.StatusCode);
    }

    if (itemsInStockAndStatusCode.StatusCode != HttpStatusCode.OK)
    {
         return StatusCode((int)itemsInStockAndStatusCode.StatusCode);
    }
    return Ok(itemsInStockAndStatusCode.ItemsInStock);
}

Here endeth the series on caching.

Full source code here.