Entity Framework. Code First Migrations

Сегодня я попытаюсь разобраться с шаблоном разработки  Code First для Entity Framework, а так же что такое миграции и для чего они нужны. Речь пойдет о EF 5.0.

Code First позволяет определить модель данных на основе POCO-классов (Plain Old CLR Objects), а затем сопоставить эти классы с существующей или сгенерировать новую базу данных.  

Что же, попробуем начать и будем разбираться по ходу дела. Создадим пустой Console Application. Реализуем простенький пример с сотрудниками. Поэтому создадим для начала класс Employee:
    
    public class Employee
    {
        [StringLength(50)]
        public int Id { get; set; }
        public string FirstName { get; set; }
    }   

Так же добавим класс Department:
 
    public class Department
    {
        public int Id { get; set; }
        [StringLength(50)]
        public string Name { get; set; }
        public ICollection <Employee> Employees { get; set; }
    }

Виды отношений между таблицами будут распознаны, для отношения типа 1:n в классе Department необходимо реализовать интерфейс ICollection<T> - где T является типом объекта в другой таблице связи, в данном случае Employee. Если же нужно отношение тип n:m, тогда необходимо интерфейс ICollection<T> реализовать  в обоих классах.

По умолчанию свойство с именем Id или <class name>Id считается первичным ключом (регистр симвоолов при этом не учитывается), так же, начиная с EF 5.0, в качестве первичного ключа может использоваться Guid (в данном примере оставим int). Если же есть необходимость использовать другое имя для первичного ключа, то для это применяется атрибут Key.


Примечание:   при использовании Guid в качестве первичного ключа его необходимо        инициализировать в конструкторе


В приведенном выше коде я использовал атрибут [StringLength(int)], который задает максимальную длину строки, без использования данного атрибута будет применен тип nvarchar(max). Есть достаточно много атрибутов, которые облегчают жизнь, и которые будут применяться в этом примере, но я не хочу тратить время на их описание. Основные из них можно посмотреть тут.


Продолжаем!


Т.к. у нас пустое приложение, нам необходимо добавить ссылку на EF5 с помощью NuGet Packages, благодаря чему появятся все необходимые библиотеки:



Такого же эффекта можно добиться, используя Package Manager Console (TOOLS > Library  Package  Manager) совместно с командой Install-Package EntityFramework –Pre

Теперь добавим настройки подключения к БД. Для этого добавим в App.Config параметр <connectionStrings>:

<connectionStrings>
  <add name="CodeContext"  connectionString="Data Source=ServerName;
    Initial Catalog=EFTest;Integrated Security=true;" providerName="System.Data.SqlClient"/>
  </connectionStrings>

Далее создадим контекст базы данных, для чего необходимо создать производный от DbContext класс. Создадим файл CodeContext.cs и добавим следующий код:
 
namespace Employee.EF
{
    public  class CodeContext  : DbContext
    {
        public CodeContext() : base("CodeContext")
        {
            
        }

        public DbSet<Employee> Employees { get; set; }
        public DbSet<Department> Departments { get; set; }
    }
}

Обратите внимание! Если имя connectionString совпадает с именем контекста (в данном случае это CodeContext и они совпадают), то DbContext распознает строку подключения к БД и все будет ОК. Если же имена разные, то необходимо для DbContext использовать это подключение путем указания имени подключения в конструкторе DbContext. Вообще, если не указывать настройки подключения, то DbContext использует имя производного класса контекста - Employee.EF.CodeContext, как имя базы данных и создает подключении используя SQL Express или LocalDb (устанволен по умолчанию в VisualStudio 2012). Если же установлены оба из них, то по умолчанию будет использоваться SQL Express.

Итак, давайте перейдем в наш programs.cs и добавим следующий код в класс Program:

        static void Main(string[] args)
        {           
            using (var ctx = new CodeContext())
            {
                ctx.Employees.Add(new Employee {FirstName = "Alex"});
                ctx.SaveChanges();
                
                var employees = ctx.Employees.ToList();

                foreach (var employee in employees)
                {
                    Console.WriteLine(employee.FirstName);
                }

                Console.ReadKey();
            }
        }

Теперь можно запустить наш код!


Нам вывелось содержимое таблицы Employees, следовательно наша база данных создалась автоматически:


Как видите, связь между таблицами создалась автоматически.

Теперь предлагаю внести некоторые изменения в какой-либо из наших классов, например, Employee:


    public class Employee
    {
        public int Id { get; set; }
        [StringLength(50)]
        [Required]
        public string FirstName { get; set; }
        [StringLength(50)]
        [Required]
        public string SecondName { get; set; }
    }

И добавим фамилию нашему сотруднику, т.к. у поля SecondName появился атрибут [Required], т.е. not null.

ctx.Employees.Add(new Employee {FirstName = "Alex", SecondName = "Tarasov"});
ctx.SaveChanges();

и попробуем скомпилировать код, в результате чего получим следующую ошибку:


как видно из сообщения - сейчас самое время подойти к вопросу миграции.

Отступление:  Если на этапе разработки вы не работаете с большими объемами данных или эти данные вообще вам не важны, то можно применить следующий подход - добавим класс инициализатор:
    
    class Init: DropCreateDatabaseIfModelChanges<CodeContext>
    {

    }

и пропишем инициализатор в program.cs:

Database.SetInitializer<CodeContext>(new Init());

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

Короче говоря - МИГРАЦИИ БЫТЬ! Поэтому мы не будем при изменении модели пересоздавать БД.

Итак, миграция - это поддержка версионности базы данных.

Давайте посмотрим, что у нас есть сейчас


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

В следующей части этой статьи  побробнее разберем пример с миграцией

Полезные ссылки:




Комментариев нет:

Отправить комментарий