Программное создание первых миграций кода

Я нахожусь в проекте, где мы используем Code First on Entity Framework для нашей базы данных.

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

  • Когда модель изменяется, мы должны сгенерировать миграцию на основе кода, иначе пакет сломается (Database vs. Model).
  • Мы предпочитаем удалять создание миграции из команды (на основе https://msdn.microsoft.com/en-us/data/dn481501.aspx)

Я пробовал разные вещи из Интернета, но для большинства, как представляется, требуется AutomaticMigrations для установки true, а также AutomaticMigrationDataLossAllowed (см.: http://romiller.com/2012/02/09/running-scripting-migrations-from-code/).

Я попытался воспроизвести то, что Add-Migration делает, просматривая отражатель .NET, но я не могу найти способ вызвать команду System.Data.Entity.Migrations.AddMigrationCommand, которая вызывается через Powershell.

У кого-нибудь есть идеи вообще о том, как я могу приблизиться к достижению этого, не делая что-то чрезвычайно грязное? Это то, что я думаю, что многие люди захотят сделать/сделали...

Большое спасибо заранее!

Ответы

Ответ 1

Прежде всего, нет возможности запуска Nuget powershell за пределами visual studio (он использует DTE). Кроме того, все, что вы пишете без Visual Studio, должно быть вставлено в csproj вручную (но не сложной задачей).

Просто, чтобы показать, как это работает, я посылаю вам несколько строк. Чтобы протестировать их, создайте dll MyDll (тестовый проект с контекстом и сущностями), а затем вручную включите миграции в MyDll с помощью Enable-Migrations (только для создания Configuration.cs).

После этого вы можете использовать этот фрагмент кода для генерации исходного кода

DbConnectionInfo connectionStringInfo = new DbConnectionInfo(
    "Server=.;Database=MigrationTest;User=sa;Password=dacambiare", "System.Data.SqlClient"); // We shoud retrieve this from App.config

ToolingFacade toolingFacade =  new ToolingFacade(
    "MyDll",   // MigrationAssemblyName. In this case dll should be located in "C:\\Temp\\MigrationTest" dir
    "MyDll",  // ContextAssemblyName. Same as above
    null,
    "C:\\Temp\\MigrationTest",   // Where the dlls are located
    "C:\\Temp\\MigrationTest\\App.config", // Insert the right directory and change with Web.config if required
    "C:\\Temp\\App_Data",
    connectionStringInfo)
{
    LogInfoDelegate = s => {Console.WriteLine(s);},
    LogWarningDelegate = s => { Console.WriteLine("WARNING: " + s); },
    LogVerboseDelegate = s => { Console.WriteLine("VERBOSE: " + s); }
};


ScaffoldedMigration scaffoldedMigration = toolingFacade.Scaffold("MyMigName", "C#", "MyAppNameSpace", false);

Console.WriteLine(scaffoldedMigration.DesignerCode);
Console.WriteLine("==================");
Console.WriteLine(scaffoldedMigration.UserCode);

// Don't forget the resource file that is in the scaffoldedMigration

ИЗМЕНИТЬ
Я забыл пространства имен и часто не используется, поэтому вы

using System;
using System.Data.Entity.Infrastructure;
using System.Data.Entity.Migrations.Design;

Ответ 2

Не реальный ответ на ваш вопрос, скорее доля моего опыта: я был бы очень осторожен с миграциями, созданными для вас таким образом. Много раз, когда я создавал миграцию (в VS), я просматриваю их. Я проверяю, являются ли предлагаемые изменения тем, что я хочу, чтобы они были, и EF не пытается делать что-то глупое (например, переименование столбца/таблицы, отбрасывая таблицу и создавая новую).

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

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

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

Обновление Только что обсуждалось с коллегой, и возникла еще одна проблема - как бы вы сделали свою локальную разработку? Создаете ли вы миграции на машине dev, убедитесь, что все работает? Затем удалите миграцию и зарегистрируйте код? и тогда ваш CI снова сгенерирует вашу миграцию? Я думаю, что при работе с EF это убьет умы.

Ответ 3

Вы можете использовать класс System.Data.Entity.Migrations.Design.MigrationScaffolder для программной генерации таких миграций:

[TestMethod]
public void GenerateTestMigration()
{
    var config = new MyDbMigrationsConfiguration();
    var scaffolder = new MigrationScaffolder(config);
    var pendingMigration = scaffolder.Scaffold("TestMigration");
    Trace.WriteLine(pendingMigration.UserCode);
}

Я использую эти и другие методы на нашем сервере сборки для сбоя сборки, если есть ожидающие миграции или если миграция должна быть создана следующим образом:

using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Data.Entity.Migrations;
using System.Data.Entity.Migrations.Design;
using System.Diagnostics;
using System.Linq;

namespace YabbaDabbaDoo
{
    [TestClass]
    public class MigrationTests
    {
        [TestMethod]
        [TestCategory("RunOnBuild")]
        public void VerifyThereAreNoPendingMigrations()
        {
            // Arrange
            var config = new MyDbMigrationsConfiguration();
            var dbMigrator = new DbMigrator(config);

            // Act
            var pendingMigrations = dbMigrator.GetPendingMigrations().ToList();

            // Visual Assertion
            Trace.WriteLine(pendingMigrations);

            // Assert
            Assert.AreEqual(0, pendingMigrations.Count(), "There are pending EF migrations that need to be ran.");
        }

        [TestMethod]
        [TestCategory("RunOnBuild")]
        public void VerifyDatabaseIsCompatibleWithModel()
        {
            // Arrange
            var context = new MyDbContext();

            // Act
            var isCompatible = context.Database.CompatibleWithModel(false);

            // Visual Assertion
            if (!isCompatible)
            {
                var config = new MyDbMigrationsConfiguration();
                var scaffolder = new MigrationScaffolder(config);
                var pendingMigration = scaffolder.Scaffold("MissingMigration");

                Trace.WriteLine("Missing Migration:");
                Trace.WriteLine("");
                Trace.WriteLine(pendingMigration.UserCode);
            }

            // Assert
            Assert.IsTrue(isCompatible, "The EF model is not compatible with the database. An EF migration needs to be created. See output for sample of missing migration.");
        }
    }
}