Як і куди викликати Database.EnsureCreated та Database.Migrate?


83

У мене є програма ASP.NET MVC 6, і мені потрібно викликати методи Database.EnsureCreatedand Database.Migrate.

Але куди мені їх подзвонити?


Можливо, ви не захочете використовувати будь-який. Документи MS говорять про використання програми Migrate (): "Хоча це чудово підходить для додатків з локальною базою даних, більшість додатків потребуватимуть більш надійної стратегії розгортання, наприклад, створення сценаріїв SQL". docs.microsoft.com/en-us/ef/core/managing-schemas/migrations
Джон Панкович

Відповіді:


99

Я вважаю, що це важливе питання, і на нього слід добре відповісти!

Що таке Database.EnsureCreated?

context.Database.EnsureCreated()- це новий основний метод EF, який гарантує наявність бази даних для контексту. Якщо він існує, ніяких дій не вживається. Якщо вона не існує, то створюється база даних та всі її схеми, а також вона забезпечує її сумісність із моделлю для цього контексту.

Примітка: Цей метод не використовує міграції для створення бази даних. Крім того, створену базу даних не можна згодом оновити за допомогою міграцій. Якщо ви націлюєтеся на реляційну базу даних і використовуєте міграції, ви можете використовувати DbContext.Database.Migrate()метод, щоб переконатися, що база даних створена і всі міграції застосовані.

Як ми це зробили з EF 6?

context.Database.EnsureCreated() еквівалентно наведеним нижче підходам EF 6:

  1. Консоль менеджера пакетів:

    Enable-Migrations -EnableAutomaticMigrations. Add-Migration / Update-Database.

  2. З коду:

    Database.SetInitializer CreateDatabaseIfNotExists

або

За допомогою DbMigrationsConfiguration та встановіть AutomaticMigrationsEnabled = true;

Що таке Database.Migrate?

Застосовує будь-які очікувані міграції для контексту до бази даних. Створить базу даних, якщо вона ще не існує.

Як ми це зробили з EF 6?

context.Database.Migrate() еквівалентно наведеним нижче підходам EF 6:

  1. Консоль менеджера пакетів:

    Update-Database -TargetMigration

  2. За допомогою власної DbMigrationsConfiguration:

    AutomaticMigrationsEnabled = false; або з DbMigrator.

Висновок :

Якщо ви використовуєте міграції, є context.Database.Migrate(). Якщо ви не хочете міграції і просто хочете швидку базу даних (зазвичай для тестування), тоді використовуйте context.Database.EnsureCreated () / EnsureDeleted ().


2
Привіт Басам Алуджілі, дякую за вашу відповідь! у своєму проекті я використовую міграції, я не знав, що не слід використовувати обидва методи разом.
bailando bailando

1
uw, і ось приклад, як це назвати! stefanhendriks.com/2016/04/29/…
Bassam Alugili

1
Я думав, що Database.Migrate()міграції Creats (за потреби) потім оновлюють базу на ньому. Подібно до автоматичної міграції в EF 6. Але я помилився. Він застосовує лише існуючі міграції (якщо такі є) у базі даних.
Афшар Мохебі,

Як я розумію, Database.Migrate використовує ті самі дані, що використовуються програмою під час вставки / запитів та ін. Чи хочемо ми, щоб ці дії робив користувач із правами створення / скидання? Це спосіб дозволити Database.Migrate () використовувати інші облікові дані (із правами створення / скидання)?
Ásgeir Gunnar Stefánsson

2
Ви щойно врятували мене від майбутньої катастрофи. Слава
Шасват Рунгта

26

З інформацією, яку надали Джеймс П та Бассам Алуджілі, я в підсумку зробив додавання цих рядків коду до Configureметоду в Startupкласі ( Startup.cs ):

using (var scope = 
  app.ApplicationServices.CreateScope())
using (var context = scope.ServiceProvider.GetService<MyDbContext>())
    context.Database.Migrate();

Це саме те, що я шукав. Більшість прикладів використовують або ядро ​​.Net, або Інтернет, і я був у програмі Windows Forms із .Net 4.6. База даних уже створена (оскільки користувач у рядку підключення не має прав на створення баз даних). І вищевказаний код створив усі таблиці та все з міграцій.
nivs1978

16

Просто вперед, ви повинні прочитати це від Роуена Міллера:

... EnsureCreatedповністю обходить міграції і просто створює схему для вас, ви не можете змішувати це з міграціями. EnsureCreatedпризначений для тестування або швидкого створення прототипів, де ви можете щоразу скидати та повторно створювати базу даних. Якщо ви використовуєте міграції та хочете, щоб вони автоматично застосовувались під час запуску програми, тоді ви можете використовувати їх context.Database.Migrate().

Відповідно до відповіді тут вам потрібно додати Globals.EnsureDatabaseCreated();його до Startup.cs:

Функція запуску в Startup.cs :

public Startup(IHostingEnvironment env)
{
    // Set up configuration sources.
    var builder = new ConfigurationBuilder()
            .AddJsonFile("appsettings.json")
            .AddEnvironmentVariables();

    if (env.IsDevelopment())
    {
        // This will push telemetry data through Application Insights pipeline faster, allowing you to view results immediately.
            builder.AddApplicationInsightsSettings(developerMode: true);
    }
    Configuration = builder.Build();
    Globals.Configuration = Configuration;
    Globals.HostingEnvironment = env;
    Globals.EnsureDatabaseCreated();
}

І визначимо Globals.EnsureDatabaseCreated()таким чином:

public static void EnsureDatabaseCreated()
    {
        var optionsBuilder = new DbContextOptionsBuilder();
        if (HostingEnvironment.IsDevelopment()) optionsBuilder.UseSqlServer(Configuration["Data:dev:DataContext"]);
        else if (HostingEnvironment.IsStaging()) optionsBuilder.UseSqlServer(Configuration["Data:staging:DataContext"]);
        else if (HostingEnvironment.IsProduction()) optionsBuilder.UseSqlServer(Configuration["Data:live:DataContext"]);
        var context = new ApplicationContext(optionsBuilder.Options);
        context.Database.EnsureCreated();

        optionsBuilder = new DbContextOptionsBuilder();
        if (HostingEnvironment.IsDevelopment()) optionsBuilder.UseSqlServer(Configuration["Data:dev:TransientContext"]);
        else if (HostingEnvironment.IsStaging()) optionsBuilder.UseSqlServer(Configuration["Data:staging:TransientContext"]);
        else if (HostingEnvironment.IsProduction()) optionsBuilder.UseSqlServer(Configuration["Data:live:TransientContext"]);
        new TransientContext(optionsBuilder.Options).Database.EnsureCreated();
    }

Для використання context.Database.Migrate()дивіться тут або тут .


Привіт, Джеймсе, дякую за відповідь !, у мене немає способу доступу до різного імені Globals у моєму способі запуску, як я можу отримати до нього доступ?
bailando bailando

2
Те саме, не бачачи Globals. Це виглядає як нестандартний спосіб спробувати це зробити
Дуглас Гаскелл,

Наскільки я розумію, стандартним способом є те, що DbContext створюється в Startup.ConfigureServices, але за допомогою непрямих методів. Ви можете виловити його там, або в Startup.Configure за допомогою app.ApplicationServices.GetRequiredService <T>. Я думаю.
Джош Саттерфілд,

7

Зазвичай DbContextдодається до контейнера для введення залежностей приблизно Startup.ConfigureServices()так:

public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
        // Add DbContext to the injection container
        services.AddDbContext<MyDbContext>(options =>
                options.UseSqlServer(
                    this.Configuration.GetConnectionString("DefaultConnection")));
    }
}

Однак, IServiceCollectionвін не діє як постачальник послуг, і оскільки DbContextвін не був зареєстрований у контейнері для введення до поточної області дії ( Startup.ConfigureServices), ми не можемо отримати доступ до контексту за допомогою введення залежності тут.

Хенк Моллем обговорює вручну дозвіл служб при запуску тут , але згадує , що ...

розпізнавання послуг вручну (він же Locator Service), як правило, вважається анти-шаблоном ... [і] вам слід якомога більше уникати цього.

Хенк також зазначає, що Startupвведення залежностей конструктора дуже обмежене і не включає служби, налаштовані на Startup.ConfigureServices(), тому використання DbContext є найпростішим та найбільш доцільним через контейнер для ін'єкцій, що використовується в іншій частині програми.

Постачальник послуг хостингу середовища виконання може вводити певні послуги в конструктор Startupкласу, наприклад IConfiguration, IWebHostEnvironment( IHostingEnvironmentу версіях до 3.0) ILoggerFactoryта IServiceProvider. Зверніть увагу, що останній є екземпляром, побудованим хостинговим рівнем, і містить лише основні послуги для запуску програми.

Для того, щоб зателефонувати Database.EnsureCreated()або Database.Migrate()ми можемо і хочемо мати автоматичне вирішення DbContext Startup.Configure(), де наші налаштовані служби тепер доступні через DI:

public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
        // Add DbContext to the injection container
        services.AddDbContext<MyDbContext>(options =>
                options.UseSqlServer(
                    this.Configuration.GetConnectionString("DefaultConnection")));
    }

    public static void Configure(IApplicationBuilder app, IWebHostEnvironment env, MyDbContext context)
    {
        if (env.IsDevelopment())
        {
            context.Database.EnsureCreated();
            //context.Database.Migrate();
        }
    }
}

Будь ласка , пам'ятайте , як відповідь Бассама Alugili в посилального від EF Основний документації, Database.EnsureCreated()і Database.Migrate()не призначені для використання разом , тому що один гарантує , що ваші існуючі міграції застосовуються до бази даних, яка створюється при необхідності. Інший просто гарантує, що база даних існує, а якщо ні, то створює базу даних, яка відображає вашу DbContext, включаючи будь-які затравки, зроблені через API Fluent у контексті.


1

Крім того, ви можете побачити показ продуктивності, якщо викликати це у конструкторі вашого контексту ... Після переходу EnsureCreatedдо утиліти setup.cs я помітив значне покращення часу відгуку.

Примітка. Я використовую EFC та UWP.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.