Миграции модели данных
125Работа с базами данных в .NET Framework --- Entity Framework 6 --- Миграции модели данных
В примерах ранее мы использовали автоматическое соглашение по инициализации базы данных при изменении модели. В предыдущей статье мы показали, как можно изменить способ инициализации базы данных. Но при любой настройке инициализатора, когда модель данных изменилась, вам в любом случае нужно удалить базу данных, чтобы воссоздать ее снова. Очевидно что такое поведение может вас не устроить, т.к. при удалении базы данных удаляются и все данные из нее.
Изменить такое поведение можно с использованием миграций, которые позволяют хранить различные версии модели в таблице __MigrationHistory. Как мы говорили ранее, для отслеживания состояния модели данных Entity Framework использует автоматически генерируемую таблицу __MigrationHistory, в которой хранится сериализованная в двоичный формат модель данных. (Также, как говорилось в статье “Использование Code-First” вы можете использовать подход Code-Second, при котором такая проблема вообще не возникает.)
Для управления миграциями модели в Code-First используется класс DbMigration из пространства имен System.Data.Entity.Migrations. Миграции позволяют указать текущую версию модели, показав какие должны быть внесены изменения в базу данных. Допустим у вас есть следующая модель данных:
public class Customer
{
public int CustomerId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public int? Age { get; set; }
[Column(TypeName = "image")]
public byte[] Photo { get; set; }
// Ссылка на заказы
public List<Order> Orders { get; set; }
}
public class Order
{
public int OrderId { get; set; }
public string ProductName { get; set; }
public string Description { get; set; }
public int Quantity { get; set; }
public DateTime PurchaseDate { get; set; }
// Ссылка на покупателя
public Customer Customer { get; set; }
}
Далее вы какое-то время работаете с приложением и возможно вставляете данные в таблицы Customers и Orders. В какой-то момент вы захотели ввести ограничение на длину для поля FirstName в классе Customer и удалить столбец LastName. Напомню, что для этого можно использовать атрибут MaxLength в классе модели:
public class Customer
{
public int CustomerId { get; set; }
[MaxLength(20)]
public string FirstName { get; set; }
// public string LastName { get; set; }
// ...
}
Если вы теперь запустите приложение, то Code-First обнаружит, что модель изменилась и, в зависимости от типа используемого инициализатора базы данных, либо выдаст исключение, либо попытается удалить и воссоздать базу данных с новой структурой. В любом случае, вы потеряете все данные из этих таблиц. Т.к. в нашей новой модели мы критически не затронули структуру модели, а просто добавили ограничение и удалили ненужный столбец, то мы можем использовать миграцию модели, чтобы указать Code-First, что можно просто изменить базу данных без ее удаления.
Поддержка миграций в Entity Framework реализована с помощью пакета EntityFramework.Migrations, который устанавливается автоматически, при установке Entity Framework из диспетчера NuGet. Чтобы включить миграции, нужно выполнить команду Enable-Migrations в консоли диспетчера NuGet (открыть консоль в Visual Studio можно с помощью команды меню Tools --> Library Package Manager --> Package manager Console):
Enable-Migrations -ProjectName "CodeFirst" -StartUpProjectName "ProfessorWeb.EntityFramework"
Указание директив ProjectName и StartUpProjectName не обязательно. Напомню, что в нашем тестовом проекте модель данных определяется в проекте классов CodeFirst, а само приложение ASP.NET, где мы работаем с данными, называется ProfessorWeb.EntityFramework, поэтому в этой команде я явно задал имена проектов. Если вы используете модель Code-First в том же проекте, где работаете с данными, указывать эти директивы необязательно, достаточно просто выполнить команду Enable-Migrations.
После запуска этой команды, в проекте CodeFirst будет добавлена новая папка Migrations, содержащая два файла:
В файле Configuration.cs настраивается конфигурации миграций. Во втором файле InitialCreate.cs указывается структура первоначальной модели. Далее вы можете использовать в консоли две команды, для управления миграциями:
- Add-Migration
Создает шаблон для следующей миграции на основании изменений, произведенных в модели с момента создания последней миграции.
- Update-Database
Обновляет базу данных с использованием последних миграций.
Давайте добавим новую миграцию, используя команду Add-Migration SampleMigrations. Если вы используете структуру решения как у меня, то в этой команде также нужно будет явно указать имя проекта CodeFirst, к которому будет добавлена новая миграция:
Add-Migration SampleMigrations -ProjectName "CodeFirst"
В результате Visual Studio добавит новый файл миграции в папку Migrations, имеющий в имени строку SampleMigration.cs. Откройте этот файл. Если вы изменили модель, как было показано в пример выше, то вы увидите следующий код:
namespace CodeFirst.Migrations
{
using System;
using System.Data.Entity.Migrations;
public partial class SampleMigrations : DbMigration
{
public override void Up()
{
AlterColumn("dbo.Customers", "FirstName", c => c.String(maxLength: 20));
DropColumn("dbo.Customers", "LastName");
}
public override void Down()
{
AddColumn("dbo.Customers", "LastName", c => c.String());
AlterColumn("dbo.Customers", "FirstName", c => c.String());
}
}
}
Здесь, в методе Up() указывается то, как должна выглядеть модель после изменения. Мы используем различные вспомогательные методы класса DbMigration, чтобы указать ограничение на длину поля FirstName (метод AlterColumn()) и удалить столбец LastName (метод DropColumn()).
В этом классе обязательно нужно реализовать метод Down(), в котором указывается то, как модель выглядела до изменений. Это нужно для того, чтобы Code-First понимал, к какой структуре модели можно применять миграцию, описанную в методе Up(). До изменения модели у нас не было ограничения на поле FirstName, поэтому мы вызываем метод AlterColumn() и не передаем никаких параметров в третьем аргументе этого метода, вызвав String() – тем самым указав, что для строкового поля не было никаких ограничений по длине. Также, до изменения модели у нас существовал столбец LastName, поэтому его нужно добавить в методе Down().
Теперь вы можете обновить базу данных на основе данной миграции, использовав команду Update-Database:
Update-Database -ProjectName "CodeFirst"
После запуска этой команды, пакет Code First Migrations сравнит список миграций в папке Migrations, выберет ту, которая соответствует текущей модели данных и обновит структуру базы без удаления записей из таблиц:
Если в таблице Customers содержались какие-нибудь данные, то они будут сохранены после обновления базы данных.