Streaming Results from Entity Framework Core and Web API Core – Part 2

Full source code available here.

Some time ago I wrote a post showing how to stream results from Entity Framework over Web API. This approach a few benefits – the results would not be materialized in the API code, a small amount of memory would be used irrespective of the size of the data returned, the results would being steaming as soon as possible and the speed of the request was faster than doing something like .ToList() or .ToListAsync().

In the example code I directly accessed the DbContext from the API controller, a reader of the blog got in touch to ask the database access code could be kept away from the API controller. This blog post shows a simple way of achieving this.

In the related post I had a method like this –

[HttpGet("streaming/")]
public IActionResult GetStreaming()
{
    IQueryable<Product> products = _salesContext.Products.AsNoTracking();
    return Ok(products);
}

The _salesContext was passed into the constructor of the ProductsController class using dependency injection and access directly.

In this post I’m going to pass a ProductsService into the ProductsController and use it to make the request to the database.

Here is how the controller now looks –

    [Route("api/[controller]")]
    [ApiController]
    public class ProductsController : ControllerBase
    {
        private readonly ProductsService _productsService;

        public ProductsController(ProductsService productsService)
        {
            _productsService = productsService;
        }

        [HttpGet("streamingFromService/{count}")]
        public ActionResult GetStreamingFromService(int count)
        {
            return Ok(_productsService.GetStreaming(count));
        }

        [HttpGet("streamingFromServiceWithProjection/{count}")]
        public ActionResult GetStreamingFromServiceWithProjection(int count)
        {
            return Ok(_productsService.GetStreamingWithProjection(count));
        }

        [HttpGet("nonStreamingFromService/{count}")]
        public async Task<ActionResult> GetNonStreamingFromService(int count)
        {
            return Ok(await _productsService.GetNonStreaming(count));
        }
    }

The ProductService is very simple, it makes the relevant calls to the database and returns the Products or ProductModels. For good measure I’ve added Automapper to project the Product to ProductModel, separating the underlying data type from the one presented to the caller.

    public class ProductsService
    {
        private readonly SalesContext _salesContext;
        private readonly IMapper _mapper;

        public ProductsService(SalesContext salesContext, IMapper mapper)
        {
            _salesContext = salesContext;
            _mapper = mapper;
        }

        public IQueryable<Product> GetStreaming(int count)
        {
            IQueryable<Product> products = _salesContext.Products.OrderBy(o => o.ProductId).Take(count).AsNoTracking();
            return products;
        }

        public IQueryable<ProductModel> GetStreamingWithProjection(int count)
        {
            var productModels = _mapper.ProjectTo<ProductModel>(_salesContext.Products.OrderBy(o => o.ProductId).Take(count).AsNoTracking());
            return productModels;
        }

        public async Task<IList<Product>> GetNonStreaming(int count)
        {
            IList<Product> products = await _salesContext.Products.OrderBy(o => o.ProductId).Take(count).ToListAsync();
            return products;
        }
    }

Now, some people might be concerned about returning an IQueryable, if you are see this post by Mark Seemann https://blog.ploeh.dk/2012/03/26/IQueryableTisTightCoupling/ to start digging into the topic.

While writing this I came across what seems like a severe performance bug in Entity Framework Core 3.x, I’m working on another post which will cover this is detail.

Full source code available here.

Fluent Validation in ASP.NET Core 3.1

Full source code available here.

This is an update to a post I wrote in 2017 talking about Fluent Validation in ASP.NET Core.

The example is the same but there has been few updates. The first is how you setup the FluentValidation in Startup.cs, and the second is that you don’t need a ActionFilterAttribute anymore. I have included an example of how to call the action method using Fiddler.

Step 1

Firstly add the package to your Web Api project.
Install-Package FluentValidation.AspNetCore.
If you are going to keep your validators in the Web Api project that is all you need to add.

But if you put the validators in a different project you need to add the FluentValidation package to that project
Install-Package FluentValidation.

Step 2

In startup.cs add –

public void ConfigureServices(IServiceCollection services)
{
     services.AddControllers()
        .AddFluentValidation(fv => fv.RegisterValidatorsFromAssemblyContaining<PersonModelValidator>());}
Step 3

Add a model and a validator.

public class PersonModel
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

public class PersonModelValidator : AbstractValidator<PersonModel>
{
    public PersonModelValidator()
    {
        RuleFor(p => p.FirstName).NotEmpty();
        RuleFor(p => p.LastName).Length(5);
    }
}

Example Usage

In Fiddler, Postman or any other tool you POST to localhost:5000/person with the following header –

Content-Type: application/json

And body –

{
    "firstName": "",
    "lastName": "This is too long"
}

Here is how the request looks in Fiddler –

The response will look like this –

{
    "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
    "title": "One or more validation errors occurred.",
    "status": 400,
    "traceId": "|84df05e2-41e0d4841bb61293.",
    "errors": {
        "LastName": [
            "'Last Name' must be 5 characters in length. You entered 16 characters."
        ],
        "FirstName": [
            "'First Name' must not be empty."
        ]
    }
}

Easy to read that there are two errors.

Full source code available here.

Passing Configuration Options Into Middleware, Services and Controllers in ASP.NET Core 3.1

Full source code here.

I recently hit a problem where I needed to reload configuration settings as they changed, fortunately this is relatively straightforward when using the IOptionsMonitor, in .NET Core.

Add a few lines to ConfigureServices, pass the configuration options as a scoped service to the controller or into another service and everything works.

Here is all you need in the ConfigureServices method –

public void ConfigureServices(IServiceCollection services)
{
	services.AddOptions();
	services.Configure<WeatherOptions>(Configuration.GetSection("WeatherOptions"));
	services.AddScoped<ISomeService, SomeService>();

Here’s the service that takes the IOptionsMonitor

public class SomeService : ISomeService
{
	private readonly WeatherOptions _weatherOptions;

	public SomeService(IOptionsMonitor<WeatherOptions> weatherOptions)
	{
		_weatherOptions = weatherOptions.CurrentValue;
	}
// snip..

And here is the controller taking the IOptionsMonitor and the service that also uses the options, this is of course contrived and you almost certainly would not want to take both constructor parameters –

public class WeatherForecastController : ControllerBase
{
	private readonly WeatherOptions _weatherOptions;
	private readonly ISomeService _someService;
	public WeatherForecastController(IOptionsMonitor<WeatherOptions> weatherOptions, ISomeService someService)
	{
		_someService = someService;
		_weatherOptions = weatherOptions.CurrentValue;
	}

But what if you also had to pass those configuration settings into a piece of middleware that executes on each request and if they configuration values change you want to be reflected in the execution of the middleware. This is not so easy because the constructor of a middleware class can only have singletons injected into it, same applies to the Configure method Startup.

Fortunately, the Invoke method can have a scoped service injected into it, see here for more.

public class SomeMiddleware
{
	private readonly RequestDelegate _next;

	public SomeMiddleware(RequestDelegate next)
	{
		_next = next;
	}

	public async Task InvokeAsync(HttpContext context, ISomeService someService)
	{
		someService.DoSomething("Middleware - ");
		await _next(context);
	}
}

To use the middleware, add a single line to the Configure method –

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
	app.UseMiddleware<SomeMiddleware>();
	//snip..

That’s it.

Full source code here.

Simmy Chaos Engine for .NET – Part 5, Breaking Your Own Code

Full source code here.

The blog posts I have written so far about Simmy all deal with the scenario where you don’t have control over the source code that you want to break, in these cases Simmy policies are applied to the calling code.

In this post I’m going to show a very simple way of generating faults inside your own code. (For those of you familiar with Polly, you could use a policy registry, but I’m not going to in this example).

I have a Web Api application that has a very contrived RandomValueGenerator to return numbers and strings.

The RandomValueGenerator has fault policy that throws exceptions and a behavior policy that replaces good data with bad data.

The fault policy looks like this –

InjectOutcomePolicy _faultPolicy = MonkeyPolicy.InjectFault( 
    new Exception("Simmy threw an exception"),
    injectionRate: .5,
    enabled: () => true
);

50% of the time it will throw an exception.

The behavior policy looks like this –

InjectOutcomePolicy<NameAndLength> _corruptDataPolicy = MonkeyPolicy.InjectFault<NameAndLength>(
    new NameAndLength { Name = "xxxxx", Lenght = -100 },
    injectionRate: .5,
    enabled: () => true
);

50% of the time it will change the name to “xxxxx” and the length to -100.

Here’s how to use these policies inside the RandomValueGenerator

public string GetRandomName()
{
    return _faultPolicy.Execute(() => names[_random.Next(0, 5)]);
}

public int GetRandomNumber()
{
    return _faultPolicy.Execute(() => _random.Next(0, 100));
}

public NameAndLength GetNameAndLength()
{
    string name = names[_random.Next(0, 5)];
    NameAndLength nameAndLength = new NameAndLength { Name = name, Lenght = name.Length };
    return _corruptDataPolicy.Execute(() => nameAndLength);
}

As mentioned earlier, a policy registry might be a better way of passing and reusing the Simmy policies and this will be covered in the next post on Simmy.

Full source code here.

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.