Обновите существующую базу данных с помощью Entity Framework Code First в MVC

В моем приложении MVC я использовал Entity Framework 6 и создал базу данных с первым подходом кода. Через некоторое время я обновил один из классов сущностей, добавив новый столбец и удалив некоторые столбцы. Чтобы отразить эти изменения в базе данных, я выполнил следующие шаги:

  1. Удалил папку миграции в проекте.
  2. Удалена таблица __MigrationHistory в базе данных.
  3. Затем выполните следующую команду в консоли диспетчера пакетов:
    Enable-Migrations -EnableAutomaticMigrations -Force

  4. Добавьте в файл конфигурации следующие строки:
    AutomaticMigrationsEnabled = true;
    AutomaticMigrationDataLossAllowed = true;

  5. Выполнить:
    Добавить начальную миграцию

  6. И, наконец, запустите:
    Update-Database -Verbose

Однако возникает ошибка "В базе данных уже есть объект с именем 'xxx'".

Чтобы избавиться от этой проблемы, я комментирую код метода Up в исходном файле, созданном после 5-го шага. Это предотвратит ошибку, но в базе данных ничего не изменится (обновленные таблицы сущностей останутся прежними). Где ошибка? Заранее спасибо за вашу помощь.

Вот метод Up, который я прокомментировал в файле migrate.cs:

    public override void Up()
    {
        CreateTable(
            "dbo.City",
            c => new
                {
                    ID = c.Int(nullable: false, identity: true),
                    Name = c.String(nullable: false),
                    RegionID = c.Int(nullable: false),
                })
            .PrimaryKey(t => t.ID)
            .ForeignKey("dbo.Region", t => t.RegionID)
            .Index(t => t.RegionID);

        CreateTable(
            "dbo.Multiplier",
            c => new
                {
                    ID = c.Int(nullable: false, identity: true),
                    Status = c.Int(nullable: false),
                    Term = c.Int(nullable: false),
                    CityID = c.Int(nullable: false),
                    WhoIsOnline = c.String(nullable: false),
                    UserId = c.String(nullable: false),
                    InstituteName = c.String(nullable: false),
                    InstituteStatusID = c.Int(nullable: false),
                    InstituteAccreditationDate = c.DateTime(nullable: false),
                    Address = c.String(nullable: false),
                    Phone = c.String(nullable: false),
                    Fax = c.String(),
                    Email = c.String(nullable: false),
                    EurodeskEmail = c.String(nullable: false),
                    WebSite = c.String(),
                    ContactName = c.String(nullable: false),
                    ContactSurname = c.String(nullable: false),
                    ContactJobTitle = c.String(),
                    ContactAssignmentDate = c.DateTime(),
                    ContactWorkingStart = c.String(),
                    ContactWorkingkEnd = c.String(),
                    ContactPhone = c.String(),
                    ContactMobile = c.String(nullable: false),
                    ContactEmail = c.String(nullable: false),
                    ContactCityID = c.Int(nullable: false),
                    LegalRepresentativeName = c.String(nullable: false),
                    LegalRepresentativeSurname = c.String(nullable: false),
                })
            .PrimaryKey(t => t.ID)
            .ForeignKey("dbo.City", t => t.CityID)
            .ForeignKey("dbo.InstituteStatus", t => t.InstituteStatusID)
            .Index(t => t.CityID)
            .Index(t => t.InstituteStatusID);

        CreateTable(
            "dbo.InstituteStatus",
            c => new
                {
                    ID = c.Int(nullable: false, identity: true),
                    Name = c.String(nullable: false),
                })
            .PrimaryKey(t => t.ID);

        CreateTable(
            "dbo.TrainingParticipant",
            c => new
                {
                    ID = c.Int(nullable: false, identity: true),
                    TrainingID = c.Int(nullable: false),
                    ParticipantID = c.Int(nullable: false),
                    Multiplier_ID = c.Int(),
                })
            .PrimaryKey(t => t.ID)
            .ForeignKey("dbo.Participant", t => t.ParticipantID)
            .ForeignKey("dbo.Training", t => t.TrainingID)
            .ForeignKey("dbo.Multiplier", t => t.Multiplier_ID)
            .Index(t => t.TrainingID)
            .Index(t => t.ParticipantID)
            .Index(t => t.Multiplier_ID);

        CreateTable(
            "dbo.Participant",
            c => new
                {
                    ID = c.Int(nullable: false, identity: true),
                    Name = c.String(nullable: false),
                    Surname = c.String(nullable: false),
                    MultiplierID = c.Int(nullable: false),
                })
            .PrimaryKey(t => t.ID)
            .ForeignKey("dbo.Multiplier", t => t.MultiplierID)
            .Index(t => t.MultiplierID);

        CreateTable(
            "dbo.Training",
            c => new
                {
                    ID = c.Int(nullable: false, identity: true),
                    Name = c.String(nullable: false),
                    Date = c.DateTime(nullable: false),
                    CityID = c.Int(nullable: false),
                })
            .PrimaryKey(t => t.ID)
            .ForeignKey("dbo.City", t => t.CityID)
            .Index(t => t.CityID);

        CreateTable(
            "dbo.Region",
            c => new
                {
                    ID = c.Int(nullable: false, identity: true),
                    Name = c.String(nullable: false),
                })
            .PrimaryKey(t => t.ID);

    }


А это метод Down в файле migration.cs:

    public override void Down()
    {
        DropForeignKey("dbo.City", "RegionID", "dbo.Region");
        DropForeignKey("dbo.TrainingParticipant", "Multiplier_ID", "dbo.Multiplier");
        DropForeignKey("dbo.TrainingParticipant", "TrainingID", "dbo.Training");
        DropForeignKey("dbo.Training", "CityID", "dbo.City");
        DropForeignKey("dbo.TrainingParticipant", "ParticipantID", "dbo.Participant");
        DropForeignKey("dbo.Participant", "MultiplierID", "dbo.Multiplier");
        DropForeignKey("dbo.Multiplier", "InstituteStatusID", "dbo.InstituteStatus");
        DropForeignKey("dbo.Multiplier", "CityID", "dbo.City");
        DropIndex("dbo.Training", new[] { "CityID" });
        DropIndex("dbo.Participant", new[] { "MultiplierID" });
        DropIndex("dbo.TrainingParticipant", new[] { "Multiplier_ID" });
        DropIndex("dbo.TrainingParticipant", new[] { "ParticipantID" });
        DropIndex("dbo.TrainingParticipant", new[] { "TrainingID" });
        DropIndex("dbo.Multiplier", new[] { "InstituteStatusID" });
        DropIndex("dbo.Multiplier", new[] { "CityID" });
        DropIndex("dbo.City", new[] { "RegionID" });
        DropTable("dbo.Region");
        DropTable("dbo.Training");
        DropTable("dbo.Participant");
        DropTable("dbo.TrainingParticipant");
        DropTable("dbo.InstituteStatus");
        DropTable("dbo.Multiplier");
        DropTable("dbo.City");
    }

person Jack    schedule 16.02.2015    source источник
comment
Это помогло мне, потому что моя база данных Oracle, кажется, удаляет __MigrationHistory, когда команды обновления терпят неудачу. Я получаю исключение из-за того, что не могу обработать OracleException, поэтому мне приходится сбрасывать часто. (я до сих пор не знаю, почему Oracle не обрабатывает исключения)   -  person MrChrister    schedule 07.05.2015


Ответы (2)


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

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

  1. Верните свой код к тому моменту, когда вы начали изменять свои POCO.
  2. Создайте миграцию.
  3. Удалите все в методе Up
  4. Примените миграцию с помощью update-database
  5. Повторно примените изменения, внесенные в ваши POCO.
  6. Создайте еще одну миграцию (теперь она должна иметь только операторы добавления/изменения столбца вместо создания таблиц)
  7. Примените миграцию.

После этого вы должны быть готовы снова идти. Затем, в следующий раз, когда вы будете вносить изменения в код, просто выполните шаги 5 и 6.

person Chris Pratt    schedule 16.02.2015
comment
Это именно то, что я искал :) Большое спасибо за ваш ответ, который поможет тем, кому действительно нужно подать заявку, чтобы сначала получить преимущества кода EF. С уважением... - person Jack; 17.02.2015
comment
Спасибо. Это очень помогло мне в ASP.NET Core 2. Хотя мне любопытно. Сначала я начал с базы данных, а затем сделал то, что сделал Клинт Иствуд. Должен ли я сначала придерживаться базы данных и создать столбец в базе данных и каким-то образом синхронизироваться с моими лесами (я забыл команду)? - person johnny; 20.02.2018
comment
База данных в первую очередь является правильным подходом, НО я бы сказал, только если у вас есть администратор баз данных, который будет этим заниматься. Если вы не являетесь экспертом по базам данных, вам лучше позволить EF позаботиться о мелких деталях, поскольку создаваемый им SQL проверяется, корректируется и контролируется специалистами по базам данных как в Microsoft, так и в сообществе. - person Chris Pratt; 20.02.2018

У меня была именно эта проблема. Кажется, стоит отметить, что существуют команды, помогающие в этой ситуации, а именно флаги -IgnoreChanges и -Force.

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

На самом деле это довольно просто (хотя 2 дня безрезультатных поисков ответа заставили меня прочитать командные строки EF Code First Migrations и диспетчера пакетов...) Вот как я справился с этим.

Вы можете удалить папку миграции и таблицу _Migrations в SQL… тогда вам нужно будет использовать следующее: Enable-Migrations -Force

Но вы должны иметь возможность забрать отсюда, не принимая радикальных мер:

  1. Add-migration «Reset» –IgnoreChanges –Force (принудительно игнорирует изменения, которые могут существовать в вашей модели/классе – подходит для начала работы с существующей базой данных)
  2. Update-Database (просто пишет строку миграции за основу)
  3. Добавление-миграция «AddMyMigrationsToThisDb» — Force (принудительно выполняет итерацию объектной модели в коде для получения изменений)
  4. Обновление базы данных

Теперь вы должны вернуться к тому, чтобы просто использовать Add-Migration и Update-Database без дополнительных флагов.

person John Salewski    schedule 20.03.2015
comment
Спасибо Джон за ответ. - person Jack; 23.03.2015