Problem with Entity Framework DbConfiguration

Mar 12, 2015 at 9:38 PM
Edited Mar 12, 2015 at 9:40 PM
We're using SimpleInjector in a WebApi app.

We're following the usual pattern:
public class WebApiApplication : System.Web.HttpApplication
{
    protected void Application_Start()
    {
        ...
        SimpleInjectorConfig.Register(GlobalConfiguration.Configuration);
        ...
    }
    ...
public static class SimpleInjectorConfig
{
    public static void Register(HttpConfiguration configuration)
    {
        var container = new Container();
        InitializeContainer(container);

        container.RegisterWebApiControllers(GlobalConfiguration.Configuration);

        GlobalConfiguration.Configuration.DependencyResolver = 
            new SimpleInjectorWebApiDependencyResolver(container);

#if DEBUG
        container.Verify();
#endif
    }

    private static void InitializeContainer(Container container)
    {
        var webApiRequestLifestyle = new WebApiRequestLifestyle();

        container.Register<XxDbContext>(() =>
        {
            // Because we've added a second public constructor, we need to 
            // call the one we want to use explicitly
            var db = new XxDbContext();
            return db;
        }, webApiRequestLifestyle);

        container.Register<ILogger, DbLogger>(webApiRequestLifestyle);
        container.Register<UserContext, UserContext>(webApiRequestLifestyle);

        container.Register(() =>
        {
            var db = new ZdbContext();
            var logger = container.GetInstance<ILogger>();
            db.Database.Log = s => logger.logSql(s);
            return db;
        }, webApiRequestLifestyle);
    }
}
We're registering the DbContexts in lambdas in this way for two reasons - first, because we've added an additional constructor to them, for use in certain unit tests (allowing us to create a DbContext from an existing DbConnection), and second, so that we can wire up the logging. (We don't add logging to XxDbContext, because the DbLogger logs to a table in XxDbContext).

This pattern has been working fine for us in one WebApi project. Now we're working on a second. In the second, things also seemed to be working fine, until we created an ApiController that injected an XxDbContext. Now, at startup, we're getting an error:
The default DbConfiguration instance was used by the Entity Framework before the 'DbConfigurator' type was discovered. An instance of 'DbConfigurator' must be set at application start before using any Entity Framework features or must be registered in the application's config file. See http://go.microsoft.com/fwlink/?LinkId=260883 for more information.
This error is occurring inside container.Verify(), it's not happening when we comment that out. And it doesn't happen in the first app, which uses the same structure.

I'm confused.
Coordinator
Mar 12, 2015 at 10:09 PM
This has little to do with Simple Injector. Simple Injector's Verify() method just creates all registered components. Take a look at the stacktrace from where this exception is originating. Entity Framework has been a PITA in my experience when it comes to running in the startup path of your application. If you try to create a DbContext very early in the ASP.NET pipeline, EF might trouble you. Just take a look at this thread for instance.

You can probably simulate the same problem by removing the call to Verify() and calling new XxDbContext() directly from within your InitializeContainer method. I'm not experienced with the DbConfiguration class, but the exception message seems to indicate that you have a custom DbConfiguration class and that EF found this after EF already bootstrapped the context.

I have no experience with this, but what you might try is:
  • Find a way to explicitly tell EF to use your specific DbConfiguration class.
  • Configure that type in your configuration file, as explained here.
  • Create the DI configuration at a later point in time, for instance during the PostApplicationStartMethod.
  • Move the call to Verify() to an integration test.
Mar 12, 2015 at 10:57 PM
Figured it out. And yes, it didn't have anything to do with SI, itself.

Somebody had created a DbConfiguration class, in one of our dependent DLLs. And they'd modified the DbContext.Context.tt files, to decorate each generated DbContext classes with a DbConfigurationTypeAttribute.

For all of the DbContexts that existed, at that time.

Our new app uses a new DbContext, which was not so decorated. And hence the error.