EntityFramework Core, Dependency Injection, and how to inject more dependencies when you can’t use the constructor

Perhaps having dependency injection in .Net for fifteen years has spoilt us a little. We expect any class to simply declare some dependency or other as a constructor parameter and by DI magic it all just works.

It doesn't work for everything though. Notably, it doesn't ‘just work’ in the dependency injection factories provided by Microsoft for EntityFramework. The DI factory .AddDbContext<T>(...) method expects your constructor to take a single DbContextOptions<T> options parameter. No other dependencies can be declared.

The injection technique offered instead by Entity Framework is to add IDbContextOptionsExtension instances to the DbContextOptions. This requires some boilerplate.

The Problem

You use microsoft.extensions.dependencyinjection. You use EntityFrameworkCore. You want to inject dependencies into a DbContext created by the DI factory. You can’t add constructor parameters. How to do it?

How To Create and Use an IDbContextOptionsExtension

As best I can see, the boilerplate for this is about five separate pieces of code. The final piece is a single line in your DI setup, in the same place you add out-of-the-box extensions like EnableDetailErrors:

services.AddDbContext<MyApplicationsDbContext>(
  (s,o) =>  {
               o.UseSqlServer(dbString)
                .EnableDetailedErrors()
                .EnableSensitiveDataLogging(isDevOrTest)
                // -----------------------------------------
                // 👇 add one line to inject your dependency
                .InjectMyThings( s.GetRequiredService<ThingToInject>() );
                // 👆 --------------------------------------
});

The Boilerplate

To make that one line work, you write four more pieces of code.

1. The DbContextOptionsBuilder extension method

/// <summary>
/// Boilerplate to make <see cref="DbOptionsMyInjectedThingExtension"/> available.
/// </summary>
public static class MyInjectedThingExtensionDbContextOptionsBuilderExtensions
{
    public static DbContextOptionsBuilder InjectMyThings(this DbContextOptionsBuilder builder, ThingToInject myInjectedThing)
    {
        (builder as IDbContextOptionsBuilderInfrastructure)
            .AddOrUpdateExtension(new DbOptionsMyInjectedThingExtension(myInjectedThing));
        return builder;
    }
}

2. Your custom IDbContextOptionsExtension class which holds the dependencies to inject

/// <summary>
/// Add MyInjectedThing to the <see cref="DbContextOptions"/>.
/// </summary>
public class DbOptionsMyInjectedThingExtension : IDbContextOptionsExtension
{
    ///<summary>This class carries the thing you want to inject</summary>
    public bool MyInjectedBool { get; }
    public ThingToInject EvenMoreInjectedThings { get; }

    public DbOptionsMyInjectedThingExtension(ThingToInject myInjectedThing)
    {
        MyInjectedBool = myInjectedThing.MyInjectedBool;
        EvenMoreInjectedThings = myInjectedThing;
        Info = new DbOptionsMyInjectedThingExtensionInfo(this);
    }
    public void ApplyServices(IServiceCollection services)
    {
        services.AddSingleton(this); 
    }
    public void Validate(IDbContextOptions options) { }
    public DbContextOptionsExtensionInfo Info { get; }
}

3. The EFCore MetaData

/// <summary>
/// Boilerplate to make <see cref="DbOptionsMyInjectedThingExtension"/> available.
/// </summary>
public class DbOptionsMyInjectedThingExtensionInfo : DbContextOptionsExtensionInfo
{
    /// <inheritdoc/>
    public DbOptionsMyInjectedThingExtensionInfo(DbOptionsMyInjectedThingExtension extension) : base(extension) { }

    /// <inheritdoc/>
    public override bool IsDatabaseProvider => false;

    /// <inheritdoc/>
    public override string LogFragment => "MyInjectedThing: {MyInjectedThing}";

    /// <inheritdoc/>
    public override int GetServiceProviderHashCode() => 0;

    /// <inheritdoc/>
    public override bool ShouldUseSameServiceProvider(DbContextOptionsExtensionInfo other)
        => (other.Extension as DbOptionsMyInjectedThingExtension)?.MyInjectedBool == (Extension as DbOptionsMyInjectedThingExtension)?.MyInjectedBool;

    /// <inheritdoc/>
    public override void PopulateDebugInfo(IDictionary<string, string> debugInfo)
    {
        debugInfo["MyInjectedThing:"] = $"MyInjectedThing={(Extension as DbOptionsMyInjectedThingExtension)}";
    }
}

4. Finally, your DbContext constructor

    public MyApplicationsDbContext(DbContextOptions<MyApplicationsDbContext> options) 
          : base(options)
    {
        MyInjectedThing = options.FindExtension<DbOptionsMyInjectedThingExtension>()?.EvenMoreInjectedThings;
        MyInjectedBool = EvenMoreInjectedThings?.MyInjectedBool ?? false ;
    }
    protected bool MyInjectedBool;
    protected ThingToInject MyInjectedThing;

Using Castle Windsor DI for controllers in an Asp.Net WebApi Project

There are contrary signals on the web regarding Window DI and the new-ish MVC-ish WebApi IDependencyResolver. Fortunately, by late 2012 Mark Seemann summarised the state-of-the-art: don't use it.

The reason: it doesn't allow your DI container to properly manage lifetimes. Instead, do this:
[csharp]
public class WindsorCompositionRoot : IHttpControllerActivator
{
private readonly IWindsorContainer container;

public WindsorCompositionRoot(IWindsorContainer container)
{
this.container = container;
}

public IHttpController Create(
HttpRequestMessage request,
HttpControllerDescriptor controllerDescriptor,
Type controllerType)
{
var controller =
(IHttpController)this.container.Resolve(controllerType);

request.RegisterForDispose(
new Release(
() => this.container.Release(controller)));

return controller;
}

private class Release : IDisposable
{
private readonly Action release;

public Release(Action release)
{
this.release = release;
}

public void Dispose()
{
this.release();
}
}
}[/csharp]
and wire it up with this line during Application_Start in Global.asax.cs
[csharp]
GlobalConfiguration.Configuration.Services.Replace(
typeof(IHttpControllerActivator), new WindsorCompositionRoot());[/csharp]

Using Windsor for plain old MVC html pages inside WebApi projects

if you are using MVC controllers with views for html pages as well as IHttpControllers in your WebApi project, then you need to know that the WebApi controllers & activation is a quite separate subsystem from the MVC controllers. You still need the usual WindsorControllerFactory to hook Windsor into the MVC pages as well as the above for the WebApi controllers.

Code for a WindsorControllerFactory can be found in a couple of places, but this is from the Castle Windsor tutorial for Asp.Net 3 and is still valid for Asp.Net 4:
[csharp]using System;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using Castle.MicroKernel;

public class WindsorControllerFactory : DefaultControllerFactory
{
private readonly IKernel kernel;

public WindsorControllerFactory(IKernel kernel)
{
this.kernel = kernel;
}

public override void ReleaseController(IController controller)
{
kernel.ReleaseComponent(controller);
}

protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
if (controllerType == null)
{
throw new HttpException(404, string.Format("The controller for path '{0}' could not be found.", requestContext.HttpContext.Request.Path));
}
return (IController)kernel.Resolve(controllerType);
}
}[/csharp]
and again, you must hook it into the application object in Global.asax.cs
[csharp]
private static IWindsorContainer container;

//These lines should be called during Application_Start
container = new WindsorContainer().Install(FromAssembly.This());
var controllerFactory = new WindsorControllerFactory(container.Kernel);
ControllerBuilder.Current.SetControllerFactory(controllerFactory);

// And this in Application_End:
container.Dispose();
[/csharp]