While working on our new Xamarin mobile app we migrated parts of our code base from .NET Framework to .NET Standard. This meant we needed a new dependency injection container for .NET Standard and Microsoft's DependencyInjection framework (as used in ASP.NET Core) ticked all the boxes... except for one.

Our requirements were straightforward:

☑ Must run on all platforms including web, desktop and mobile.

☑ Must support constructor injection.

☑ Modules that register services with the container should depend on a stable abstraction.

☒ It should be simple to register services that implement multiple interfaces.

Unfortunately, Microsoft's framework required us to manually bind all interfaces for a particular service. An unnecessary burden and one feature we took for granted with our previous abstraction (NServiceBus' Configurer).

The NuGet package Microsoft.Extensions.DependencyInjection.Abstractions provides two main interfaces

  • IServiceCollection - used to register interfaces to implementations for a specific lifetime with the container
void RegisterServices(IServiceCollection services)
  services.AddScoped<IMyDependency, MyDependency>();
  services.AddTransient<IOperationTransient, Operation>();
  services.AddScoped<IOperationScoped, Operation>();
  services.AddSingleton<IOperationSingleton, Operation>();
  services.AddSingleton<IOperationSingletonInstance>(new Operation());
  • IServiceProvider - used to build instances of interfaces/services
void BuildServices(IServiceProvider provider)
  var dependency1 = provider.GetRequiredService<IMyDependency>();
  var operation1 = provider.GetService<IOperationTransient>();

In order to meet our last requirement and simplify registering types, we created extension methods on IServiceCollection based off the work done by Particular/NServiceBus on their own DI adapters.

/// <summary>
/// Registers the type <see paramref="TType"/> and all its alias (interfaces + base type) with the container with the specified <see paramref="lifetime"/>
/// </summary>
/// <typeparam name="TType"></typeparam>
/// <param name="services"></param>
/// <param name="lifetime"></param>
/// <returns></returns>
public static IServiceCollection RegisterComponent<TType>(this IServiceCollection services, ServiceLifetime lifetime)
  var type = typeof(TType);
  services.Add(new ServiceDescriptor(type, type, lifetime));
  BindAliasesOfComponentToComponent(services, type, lifetime);
  return services;

In the above code we first add a service descriptor that maps the type to itself, then we bind all of its aliases (interfaces) to itself which looks like:

private static void BindAliasesOfComponentToComponent(IServiceCollection serviceCollection, Type component, ServiceLifetime lifetime)
  var services = GetAllServices(component).Where(t=> t != component);

  foreach (var service in services)
      // Bind all interfaces to a factory method which builds the implementation type
      serviceCollection.Add(new ServiceDescriptor(service, ctx => ctx.GetService(component), lifetime));

  var baseType = component.BaseType;

  if (baseType == null || !baseType.IsAbstract)
  // Don't forget to bind an abstract base type to the implementation
  serviceCollection.Add(new ServiceDescriptor(baseType, ctx => ctx.GetService(component), lifetime));

We use reflection to recursively find all of a type's interfaces:

static IEnumerable<Type> GetAllServices(Type type)
  if (type == null)
      return new List<Type>();

  var result = new List<Type>(type.GetInterfaces())

  foreach (var interfaceType in type.GetInterfaces())

  return result.Distinct();

Finally in our module we can use this new extension method to automatically bind a type to all of its interfaces:

using App.Extensions;
using Microsoft.Extensions.DependencyInjection;

public class ServiceAgent : IServiceAgent, IWantNotifications


public class Module
  public void Initialize(IServiceCollection services)

In this example the interfaces IServiceAgent and IWantNotifications are bound to the same singleton class ServiceAgent.  They can now be built using provider.GetService<IServiceAgent>() or used in constructor injection for other services.