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.