ASP.NET Core - Resolving proper implementation in runtime using Autofac -

ASP.NET Core – Resolving proper implementation in runtime using Autofac

A few days ago I faced an interesting problem. In a nutshell, I had one interface implemented by three classes:


    public interface IService
        string GetMessage();

    public class DomainService : IService
		public string GetMessage()
			=> "Hello from domain service!";

    public class ExternalService : IService
		public string GetMessage()
			=> "Hello from external service!";

    public class MockService : IService
		public string GetMessage()
			=> "Hello from mock service!";


All dependencies were registered in ASP.NET Core container. Now we come to the tricky part. Each HTTP request coming to API had a custom header which contains information about implementation that should be used. So, using another word – it was a client app that decided on implementation. My goal was very simple – to make it work.


Register dependencies by convention

Before we move to the code it’s worth to mention one thing. Developers decided to follow very simple convention. Each type of service (so domain, external, mock) were put in separate folders like
in the below example:



That meant that each type was placed in a different namespace which ended with “Domain”, “External” and “Mock”. That was a good place to start.
First thing I did was introducing Autofac container since it has way more options for configuring entire assemblies/namespaces. Then I created an extension method which helped me register all three namespaces:


		internal static void RegisterServicesByConvention(this ContainerBuilder containerBuilder)
			var serviceAssembly = typeof(MockService).GetTypeInfo().Assembly;
				.RegisterServicesByNamespace(serviceAssembly, "Domain")
				.RegisterServicesByNamespace(serviceAssembly, "External")
				.RegisterServicesByNamespace(serviceAssembly, "Mock");

		private static ContainerBuilder RegisterServicesByNamespace(this ContainerBuilder containerBuilder, Assembly assembly, string name)
				.Where(t => t.Namespace.EndsWith(name))
				.AsImplementedInterfaces().WithMetadata("Target", name);
			return containerBuilder;


As you see the code is very simple and it does two things. First, it gets the assembly where MockService is placed so I had an access to register all needed dependencies. Then I informed Autofac that I wanted to register services from each folder (so the namespace) as implemented interfaces so in this example IService. Notice that each registration had its own metadata which informed what type of service it is. So, at that point, I solved an issue with registration. For example, I knew that DomainService was registered as IService and it has a “Target” metadata with value “Domain”.


Creating a service factory

I still needed to resolve proper implementation at runtime based on HTTP request. That’s why I created a ServiceFactory:


    public interface IServiceFactory
        TService GetService<TService>() where TService : class;

	internal class ServiceFactory : IServiceFactory
		private readonly string _targetType;
		private readonly ICustomDependencyResolver _resolver;

		public ServiceFactory(string targetType, ICustomDependencyResolver resolver)
			_targetType = targetType;
			_resolver = resolver;

		public TService GetService<TService>() where TService : class
			var services = _resolver.Resolve<IEnumerable<Meta<TService>>>();
			var service = services.FirstOrDefault(s => _targetType.Equals($"{s.Metadata["Target"]}", StringComparison.OrdinalIgnoreCase))?.Value;
			return service ?? throw new InvalidOperationException();


As you see it gets two parameters:

  • string targetType – it’s a text got from request’s custom header
  • ICustomDependencyResolver resolver – it’s a small wrapper for Autofac’s ILifetimeScope which provides only one method – Resolve:


    public interface ICustomDependencyResolver
        TResolved Resolve<TResolved>();

	internal class CustomDependencyResolver : ICustomDependencyResolver
		private readonly ILifetimeScope _lifetimeScope;

		public CustomDependencyResolver(ILifetimeScope lifetimeScope)
			_lifetimeScope = lifetimeScope;

		public TResolved Resolve<TResolved>()
			=> _lifetimeScope.Resolve<TResolved>();


I simply don’t like using ILifetimeScope directly in my code but keep in mind that this step is not mandatory to make the whole thing work. Moving back to the factory class, the first line in GetService method does something weird:


var services = _resolver.Resolve<IEnumerable<Meta<TService>>>();


The reason for that is because all three types of services had to be registered as IService interface. What would be resolved by Autofac if we would ask for IService? Random pick? That’s why in that situation we need to ask about all implementations so we have IEnumerable. The next part so Meta<T> is present in the code because each of our service registration came with Metadata and that’s how Autofac wraps it inside. So the above line simply informs that we want all implementations of given interface which its metadata. Simple is that. In the next line, we pick proper implementation based on text got from HTTP request:


var service = services.FirstOrDefault(s => _targetType.Equals($"{s.Metadata["Target"]}", StringComparison.OrdinalIgnoreCase))?.Value;


Notice that I inverted the condition so Equals method comes from a string, not the Metadata object. This allowed me to add StringComparison enum. In the last line, the service is returned or the exception is thrown if service was not found (that basically means that someone put text to custom header other than three allowed). We still miss one thing. How did I provide a target type from request to factory? Below you have an extension method which did the job:


		internal static void RegisterServiceFactory(this ContainerBuilder containerBuilder)
		   => containerBuilder.Register(ctx =>
			   var dataSource = ctx.Resolve<IHttpContextAccessor>().HttpContext.Request.Headers
				   .Single(h => h.Key == "implementation-type").Value.First();
			   var resolver = ctx.Resolve<ICustomDependencyResolver>();
			   return new ServiceFactory(dataSource, resolver);


Now the whole mystery is revealed 🙂  I used ASP.NET Core IHttpContextAccessor to get the request and then I took of the target type. Of course, this action is kicked off each time I want to resolve my factory somewhere in the code. Of course, to make it work I had to register everything in Startup class:


        public IServiceProvider ConfigureServices(IServiceCollection services)

			var containerBuilder = new ContainerBuilder();

			var container = containerBuilder.Build();
            return container.Resolve<IServiceProvider>();


And below you can see the example usage of the factory inside default ValuesController:


    public class ValuesController : Controller
        private readonly IService _service;

        public ValuesController(IServiceFactory factory)
            _service = factory.GetService<IService>();

        public string Get()
            => _service.GetMessage();


Before we’ll move to testing let’s summarise the entire flow:

  1.  HTTP request comes to the API and proper action on MVC controller is picked.
  2. Since controller has ServiceFactory as a dependency it needs to be resolved by Autofac.
  3. To create an instance of the factory, target type is got from the custom header and new instance of the factory is provided to the controller.
  4. When Controller’s constructor calls GetService method,
    beneath it resolves all implementations for given interface with its metadata.
  5. Based on the target type, proper implementation is picked and returned to the controller



To check whether it’s working I used a Postman app. The result is presented on the below gif:



Honestly, that was very interesting thing to do! A little bit challenging but the result is very cool. I hope that this will be helpful for some of you. Of course in production code, you need to add some more error handling to prevent all kinds of unexpected behaviors.

You may also like...