Миграции CodeFirst: как автоматически запустить скрипт базы данных (C# или SQL) после завершения обновления базы данных?

Я использую EF.Core с миграцией кода сначала для обновления базы данных SQL.

Всякий раз, когда я добавляю миграции (Консоль диспетчера пакетов: add-migration), я обновляю базу данных с помощью известной команды update-database. Есть ли способ запустить пакетный сценарий SQL после выполнения этой команды автоматически (как вы можете сделать с событиями после сборки в Visual Studio)?

Этот сценарий может создавать резервные копии базы данных или выполнять другие задачи (например, настройку ролей пользователей и т. д.).

Я не хочу изменять существующие миграции для этого, я знаю, что вы можете добавить что-то вроде

   protected override void Up(MigrationBuilder migrationBuilder)
   {
        var sqlFile = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, 
                       @"Migrations\20200701103006_MySQLBatch_Up.sql");
        var sqlCommands = System.IO.File.ReadAllText(sqlFile);
        migrationBuilder.Sql(sqlCommands);

        // ...
   }

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

Есть ли событие или метод, который можно переопределить для его достижения? Или что-то, что можно запустить, например скрипт?

По сути, я хочу добиться наличия сценария или метода, вызывающего:

update-database
pg_dump -h localhost -U postgres -p 5432 myDatabase > C:\Temp\myDatabase.sql

Примечание: update-database запускается в контексте диспетчера пакетов, pg_dump запускается в командной оболочке (cmd.exe), поэтому вы не можете запускать update-database напрямую в сценарии .cmd или .bat.


person Matt    schedule 13.07.2020    source источник
comment
Что в вашей БД обновляет SQL? Возможно, нет необходимости запускать пакетный скрипт каждый раз в зависимости от ваших изменений.   -  person Captain Kenpachi    schedule 03.08.2020
comment
@CaptainKenpachi - база данных здесь PostgreSQL, и я хочу запустить скрипт pg_dump -h localhost -U postgres -p 5432 myDatabase > C:\Temp\myDatabase.sql, который создает файл SQL со структурой и данными, которые я могу передать администратору базы данных. В идеале это должно срабатывать сразу после успешного завершения update-database. В случае ошибок миграции он не должен запускаться.   -  person Matt    schedule 03.08.2020
comment
Вы в основном описываете, что делает DACPAC. Я думаю, вы должны изучить их как стратегию миграции.   -  person Captain Kenpachi    schedule 03.08.2020


Ответы (2)


Два варианта, которые я могу придумать

  1. Проще: почему бы не использовать простой пакетный файл, который последовательно выполняет обе команды, а затем вы можете запустить пакетный файл вместо команды Update-Database? Вы даже можете получить большинство параметров из стандартного файла конфигурации проекта, и таким образом вы сможете использовать один и тот же сценарий в нескольких проектах, не меняя ничего, кроме файла конфигурации проекта. Таким образом, вы убедитесь, что ваши дополнительные скрипты запускаются в ответ на команду Update-Database, когда она действительно предназначена.

  2. Если это для сеанса Powershell, который может включать несколько команд обновления в динамическом режиме, и вам не нужен описанный выше подход, вы можете попробовать подписаться на событие Exiting (т.е. [System.Management.Automation.PsEngineEvent]::Exiting) механизма Powershell и выполнить скрипт в ответ автоматически через его параметр -Action в любое время, когда выполняется Update-Database (но только если в том же сеансе Powershell).

Подробности и примеры см. в команде Register-EngineEvent.

https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/register-engineevent?view=powershell-7

Внутри вашего сценария действия вы можете получить сведения о событии (например, $Events[0].MessageData) и найти текст " Database-Update ", а затем выполнить в ответ нужные вам команды. Это может стать ошибочным, если текст " Database-Update " появляется в любом непредусмотренном контексте сеанса.

Подробности и примеры команд Get-Event можно просмотреть здесь.

https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/get-event?view=powershell-7

Вы можете настроить постоянный сеанс с локальным или удаленным компьютером с помощью команды New-PSSession session, чтобы подписчик событий мог учитывать команды, выполняемые в нескольких файлах.

https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/new-pssession?view=powershell-7

Подробнее о различных типах сеансов Powershell

https://www.sconstantinou.com/windows-powershell-sessions-pssessions/< /а>

person K4M    schedule 05.08.2020
comment
Я думаю, что я мог бы пойти на подход 1. при условии, что я могу запустить update-database из пакетного файла. Для этого я нашел этот ответ с упоминанием инструменты командной строки EF, я попробую это, и если это сработает, я смогу написать двухстрочный, который сделает эту работу. Эти инструменты можно установить через dotnet tool install --global dotnet-ef. Этот пакетный файл можно запустить как действие после сборки из VS, передав каталог проекта в качестве параметра. - person Matt; 06.08.2020
comment
Это возможно. См. здесь: stackoverflow.com/questions/39641932/ - person K4M; 06.08.2020
comment
Интересный подход. Сценарий powershell читает app.config, чтобы получить имя базы данных, и вносит некоторые изменения для подготовки базы данных для разработки. Он использует update-database для создания его из миграций. - person Matt; 07.08.2020

Кажется, я не могу найти в документации ничего, чтобы делать то, что вы хотите, так, как вы хотите.

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

Итак, вы можете сделать что-то вроде этого:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env, DataContext dataContext)
{
    // migrate changes on startup
    dataContext.Database.Migrate();
    foreach(var file in GetMigrationFiles())//you can write the code that searches a folder for SQL scripts to execute
    {       
       dataContext.Database.ExecuteSqlRaw(File.ReadAllText(file));
    }
}

Убедитесь, что упорядочили GetMigrationFiles() правильно.

Проблема здесь в том, что вам нужно будет также написать SQL для отката вашего пользовательского SQL в случае, если вы захотите откатиться. Однако поместите их в другую папку.

person Captain Kenpachi    schedule 03.08.2020
comment
Да, я пробовал dataContext.Database.Migrate();, но это не всегда срабатывало. Я не знаю почему, но бывают ситуации, когда успешно работает только update-database, запускаемый из консоли диспетчера пакетов. Я полагаю, что это когда у вас есть критические изменения в структуре базы данных. Вот почему я не хочу запускать автоматические миграции в рабочей среде, а только в средах разработки (и предоставить сценарий SQL, который можно просмотреть до того, как администратор базы данных развернет его). - person Matt; 03.08.2020
comment
Ах хорошо. Существует также возможность миграции DACPAC. Его сложно настроить, но после этого довольно легко использовать. Это полностью заменит автоматические миграции. Вы будете использовать свои миграции только для локальной разработки. - person Captain Kenpachi; 03.08.2020
comment
Вы имели в виду это, правильно? Это для сервера MS-SQL. Знаете ли вы, как это интегрируется с подходом «сначала код»? (С помощью кода сначала вы создаете декорированные классы моделей, добавляете миграции и update-database применяете их к локальной базе данных) - person Matt; 03.08.2020
comment
Способ, которым мы его используем, заключается в применении миграции с кодом только к нашей локальной базе данных разработки. DACPAC отправляется с исходным кодом и выполняется во всех других средах. Существуют эквиваленты DACPAC для Postgres и т. д. Однако я не знаю названия навскидку. - person Captain Kenpachi; 03.08.2020
comment
Я думаю, что мог бы использовать подход, о котором вы упомянули, если он срабатывает только в среде разработки — поэтому метод Configure требует добавления проверки, если мы запускаем его в DEV — я мог бы подумать о #if DEBUG или что-то подобное. - person Matt; 03.08.2020
comment
Нет, я имел в виду, что мы запускаем миграцию из командной строки против нашей локальной базы данных разработчиков. В нашем случае мы все разрабатываем БД на наших локальных машинах. Мы используем dacpac при развертывании во всех наших средах CI/CD. В любом случае все в порядке. - person Captain Kenpachi; 03.08.2020
comment
Да, я понял, как вы используете CI/CD для развертывания в средах с помощью DACPAC. Мой вопрос был в том, как обстоят дела, если вы измените что-то в базе данных? Вы меняете свою локальную БД, затем используете DACPAC для получения изменений из локальной, а затем запускаете конвейер для обновления среды? Помните, здесь у нас сначала код, а не база данных. - person Matt; 04.08.2020
comment
Мы используем код только в начале разработки. И если вы не вызываете context.Migrate() во время запуска, вполне нормально делать то, что делаем мы. Наша причина связана с доверенными соединениями SQL и образами Linux Docker. но это не супер актуально. - person Captain Kenpachi; 04.08.2020
comment
Ну, я спрашивал, потому что в более ранних проектах я использовал первый подход к базе данных, что также хорошо, вместе с инструментом сравнения схем, который предоставляет VS, и проектами базы данных SQL-сервера. Если вам нужна простая база данных, то, конечно, проще написать сначала код. - person Matt; 04.08.2020