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;

How to create an NUnit test project that is also a self-running console app .exe

  1. Create your NUnit Test project as a Windows Console Application, not as a Class Library.
  2. Then make your main Program look like this:
    [csharp]
    [TestFixture]
    public class Program
    {
    static void Main(string[] args)
    {
    NUnit.ConsoleRunner.Runner.Main(
    new[]{Assembly.GetExecutingAssembly().Location }
    );
    }

    [TestCase("Aa - Bb")]
    public void WhenValidatingForename_should_accept_valid_characters(string validInput)
    {
    var result= new ClassUnderTest().Validate(validInput);
    Assert.IsTrue(result);
    }

    [TestCase("X<")]
    public void WhenValidatingForename_should_reject_invalid_characters(string invalidInput)
    {
    var result= new ClassUnderTest().Validate(invalidInput);
    Assert.IsFalse(result);
    }
    }[/csharp]

  3. Then, add a reference not only to nunit.framework.dll but also to nunit-console-runner.dll

You now have a self-running executable that runs all your tests, but still behaves in the usual way in a build script, or when running tests inside Visual Studio with TestRunner or Resharper or similar.
NB You may need to check if your build scripts are hard-coded to expect a Test project to be a .dll.

Web Forms – Mocking HttpSession

With thanks to http://stackoverflow.com/users/603670/ben-barreth
at http://stackoverflow.com/questions/1981426/how-do-i-mock-fake-the-session-object-in-asp-net-web-forms
[csharp]
[SetUp]
public void SetUpHttpSessionMock()
{
HttpWorkerRequest _wr = new SimpleWorkerRequest("/dummyWorkerRequest", @"c:\inetpub\wwwroot\dummy", "default.aspx", null, new StringWriter());
HttpContext.Current = new HttpContext(_wr);
HttpSessionStateContainer sessionContainer = new HttpSessionStateContainer("id", new SessionStateItemCollection(), new HttpStaticObjectsCollection(), 10, true, HttpCookieMode.AutoDetect, SessionStateMode.InProc, false);
SessionStateUtility.AddHttpSessionStateToContext(HttpContext.Current, sessionContainer);
}
[/csharp]

Refactoring a static class with hard-coded dependencies for testability

is not that hard. You can add a factory method to the static class to create the dependency, and change the factory method at test-time.
Here's an example:
[csharp]
public static class WithDependencies
{
public static string MethodWithDependencies()
{
using (var thing = new HardCodedThing())
{
return DoSomething();
}
}
}
[/csharp]
which can be turned into:
[csharp]
public static class WithDependencies
{
public static Func<HardCodedThing> CreateHardCodedThing = () => new HardCodedThing();

public static void MethodWithDependencies()
{
using (var thing = CreateHardCodedThing())
{
DoSomething();
}
}
}
[/csharp]
With this code in your test:
[csharp]
[TestFixture]
public class WhenDoingSomething
{
private Mock<HardCodedThing> mockThing;

[SetUp]
public void SetUpMockThing()
{
// mockThing.Setup( ... ) ... etc ...
}

public void Given_hardcodedthing_does_X_should_get_Y()
{
//Arrange
WithDependencies.CreateHardCodedThing = () => mockThing.Object;
//Act
var result= WithDependencies.MethodWithDependencies();
//Assert
Assert.AreEqual("Y", result);
}
}[/csharp]