ASP.NET Core разрешает зависимость DbContext для инициализации миграции

Замечательно, что ASP.NET DI работает нестандартно и рекурсивно разрешает все зависимости конструктора. Хотя иногда вы хотите иметь прямой доступ к контейнеру DI. Интересно, есть ли способ? Может быть, примерно так:

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

Я ничего не нашел в документах (хотя я знаю , Могу заменить встроенный DI framework).


По большей части вам это не нужно, но есть некоторые специфические случаи. В моей ситуации мне нужно инициализировать мой EF DbContext с помощью контейнера IoC при запуске приложения, чтобы инициировать автоматические миграции, которые должны работать таким образом с EF Core:

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

Мой контекст зарегистрирован в контейнере, но в отдельной библиотеке классов. И я не хочу называть это DbContextOptions конструктором напрямую.


person Andrei    schedule 13.07.2016    source источник
comment
Это крайне обескураживающая практика, поскольку она в основном убивает все преимущества наличия IoC и фактически становится шаблоном Service Locator, который является анти-шаблоном (он скрывает зависимости). Было бы интереснее узнать, зачем вам это нужно. В 99% случаев это просто плохой дизайн, и в контексте ASP.NET Core я не могу придумать ни одного случая, когда это необходимо, которое нельзя было бы решить иначе.   -  person Tseng    schedule 14.07.2016
comment
@Tseng И я с тобой согласен. Если дизайн приложения хорош, разрешение происходит от контроллеров до репозиториев по всей системе. Но есть частные случаи. В моей конкретной ситуации я хочу инициализировать свой EF DbContext с помощью DI при запуске, чтобы инициировать автоматическую миграцию. Это происходит при запуске, а не когда запрос попадает в контроллер.   -  person Andrei    schedule 14.07.2016
comment
Ключевые слова - на пуске. Когда вы разрешаете зависимости от контейнера во время запуска, это ваш корень композиции. Это именно то, что принадлежит. См. этот пост о разрешении фильтров из контейнера при запуске, написано от Марка Земанна (официальный Really Smart Guy относительно внедрения зависимостей в .NET).   -  person Scott Hannen    schedule 14.07.2016
comment
@ScottHannen, я знаю, где это место. Я просто пытаюсь понять, смогу ли я лучше подойти к этому, не повторяя себя. Я имею в виду, что я не хочу, чтобы в системе было два места, где я инициализирую свой контекст. Место №1 - это регистрация IServiceCollection, а место №2 - это место, где я запускаю миграции. По-видимому, мне придется написать factory для создания контекста, я надеялся, что есть способ использовать IoC напрямую, но похоже, что его нет.   -  person Andrei    schedule 14.07.2016
comment
@Anrei M - это на самом деле предназначалось для всех, кто говорит, что неправильно разрешать что-либо явно из контейнера. Просто укажите, что это не шаблон Service Locator, когда он находится в корне композиции.   -  person Scott Hannen    schedule 14.07.2016
comment
Существует полуофициальный способ вызова миграции во время запуска, позвольте мне посмотреть, смогу ли я его откопать. Это немного сложно, потому что, если вы разрешите его непосредственно в классе, вы фактически получите синглтон (потому что при запуске нет запроса / области действия), поэтому трюк состоит в том, чтобы создать область, выполнить миграцию и удалить ее.   -  person Tseng    schedule 14.07.2016


Ответы (1)


После того, как вопрос был обновлен и стало ясно, что речь идет о миграции EntityFramework Core, есть полуофициальный способ от команды ASP.NET Core о том, как правильно разрешить DbContext во время запуска приложения.

По умолчанию DbContext зарегистрирован как служба с заданной областью действия, поэтому экземпляр создается один раз для каждого запроса. Уловка во время запуска приложения заключается в том, что еще нет контекста, и только app.ApplicationServices доступен в Configure методе, а поставщик ApplicationServices по существу разрешает синглтоны (область видимости - это одноэлемент для каждой области, а область действия приложения существует, пока работает приложение).

Таким образом, уловка состоит в том, чтобы сначала создать область видимости, разрешить DbContext, выполнить операции, а затем удалить контекст (и DbContext с ним).

Примеры можно найти в примере приложения MusicStore здесь и здесь .

На данный момент это единственный безопасный способ решить эту проблему, не вызывая проблем с исключениями удаленных объектов.

Также обратите внимание, что самое раннее, что вы можете сделать, это метод Configure, потому что только тогда был построен контейнер IoC. В ConfigureServices вы заполняете только IServiceCollection.

Соответствующие фрагменты кода:

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);
                }
            }
        }
    }
}

Изменить:

Дополнительные ресурсы по этой проблеме:

person Tseng    schedule 13.07.2016