2

It's great that ASP.NET DI works out-of-the-box and recursively resolves all constructor dependencies. Though sometimes you want to be able to access DI container directly. I am wondering if there is a way? Maybe something like this:

IService service = Container.Instance.Resolve<IService>();

I didn't find anything in the docs (though I know, I can replace built-in DI framework).


For the most part you don't need it, but there are some specific cases. In my situation I need to initialize my EF DbContext using IoC container on app start-up to initiate auto-migrations that supposed to work like that with EF Core:

using (var context = new MyContext())
{
    context.Database.Migrate();
}

My context is registered in container but it's in a separate class library. And I don't want to call it's DbContextOptions constructor directly.

Andrei
  • 42,814
  • 35
  • 154
  • 218
  • 1
    It's highly discouraged practice to use this, as it basically kills all of the benefits of having IoC in the first place and effectively becomes a Service Locator pattern, which is an anti-pattern (it hides dependencies). It'd be more interesting to know **why** you'd want to do this. In 99% of the cases it's just a bad design and in context of ASP.NET Core I can't think of a single case where it's necessary which couldn't be solved otherwise – Tseng Jul 13 '16 at 21:37
  • @Tseng And I agree with you. If app design is good, the resolution happens from controllers to repositories across the system. But there are specific cases. In my particular situation I want to initialize my EF DbContext using DI on startup to initiate auto-migration. It happens on start-up and not when request hits controller. – Andrei Jul 13 '16 at 22:08
  • Key words - "on start-up." When you resolve dependencies from the container during start-up that's your composition root. It's exactly where that belongs. See [this post](http://blog.ploeh.dk/2014/06/13/passive-attributes/) on resolving filters from the container at startup, written by Mark Seemann (official Really Smart Guy regarding dependency injection in .NET.) – Scott Hannen Jul 13 '16 at 22:20
  • @ScottHannen oh I know where it belongs. I am just trying to figure out if I can approach it better without repeating myself. I mean I don't want to have 2 places in the system where I initialize my context. Where place #1 would be IServiceCollection registration and place #2 is where I run migrations. Apparently I'll have to write factory for context creation, I hoped there is a way to use IoC directly but looks like there isn't. – Andrei Jul 13 '16 at 22:27
  • @Anrei M - that was actually meant for anyone who says that's not correct to resolve anything explicitly from the container. Just pointing out that it's not the Service Locator pattern when it's at the composition root. – Scott Hannen Jul 13 '16 at 22:30
  • There is an "semi-official" way to call migrations during startup, let me see if I can dig it up. It's bit tricky, because if you resolve it directly in the class, you'll effectively get a singleton (cause no request/scope exist at startup), so the trick is to create a scope, do the migration and dispose it – Tseng Jul 13 '16 at 23:16

1 Answers1

3

After the question got updated and it's clear that it's going about EntityFramework Core migrations, there is an semi-official way form the ASP.NET Core team on how to correctly resolve the DbContext during application startup.

By default, the DbContext is registered as scoped service, so you get instantiated once per request. The catch during the application startup is, that there is no context yet and only app.ApplicationServices is available within Configure method and the ApplicationServices provider is essentially resolving singletons (scoped is a singleton per scope and application scope lives as long as the application does).

So the trick is to create a scope first, resolve the DbContext, do the operations and then dispose the context (and the DbContext with it).

Examples can be found in the MusicStore example application here and here.

As of right now, this is the only safe way to resolve it w/o causing any issues with i.e. disposed object exceptions.

Also please note, that the earliest point you can do this is in the Configure method, because only then the IoC container has been built. In ConfigureServices you only populate the IServiceCollection.

The relevant code snippets:

void Configure(IApplicationBuilder app)
{
    //Populates the MusicStore sample data
    SampleData.InitializeMusicStoreDatabaseAsync(app.ApplicationServices).Wait();
}

public static class SampleData
{
    ...

    public static async Task InitializeMusicStoreDatabaseAsync(IServiceProvider serviceProvider, bool createUsers = true)
    {
        using (var serviceScope = serviceProvider.GetRequiredService<IServiceScopeFactory>().CreateScope())
        {
            var db = serviceScope.ServiceProvider.GetService<MusicStoreContext>();

            if (await db.Database.EnsureCreatedAsync())
            {
                await InsertTestData(serviceProvider);
                if (createUsers)
                {
                    await CreateAdminUser(serviceProvider);
                }
            }
        }
    }
}

Edit:

Additional resources regarding this issue:

NightOwl888
  • 55,572
  • 24
  • 139
  • 212
Tseng
  • 61,549
  • 15
  • 193
  • 205