Нашли ошибку или опечатку? Выделите текст и нажмите

Поменять цветовую

гамму сайта?

Поменять
Обновления сайта
и новые разделы

Рекомендовать в Google +1

Использование Code-First

128

В предыдущей статье мы кратко рассмотрели структуру различных подходов для работы с Entity Framework. В этой статье мы более подробно остановимся на подходе Code-First и рассмотрим примеры работы с Code-First. Мы создадим две таблицы, описывающие данные заказчика в интернет магазине и его заказами. Отношение между этими таблицами будет “один ко многим” (one-to-many).

Создание проекта

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

  1. Запустите Visual Studio 2012 (в примерах всех статей мы будем использовать версию Visual Studio 2012 в сочетании с Entity Framework 6).

  2. Создайте новый проект ASP.NET, выбрав в меню File --> New Project. После этого откроется диалоговое окно, в котором укажите шаблон приложения ASP.NET Empty Web Application. Назовите произвольно проект, выберите папку сохранения и нажмите кнопку OK.

    Создание нового проекта ASP.NET
  3. В созданном проекте щелкните правой кнопкой мыши по имени решения в окне Solution Explorer и выберите в контекстном меню команду Add --> New Project.

  4. Добавьте в решение проект, имеющий шаблон библиотеки классов (Class Library) и назовите его CodeFirst:

    Добавление проекта библиотеки классов Code-First
  5. Добавьте в новый проект файл класса Model.cs, в котором мы будем описывать модель данных.

  6. Добавьте ссылку на проект CodeFirst в базовом проекте веб-приложения. Для этого щелкните правой кнопкой мыши по вкладке References в окне Solution Explorer базового проекта и выберите пункт Add Reference. В открывшемся диалоговом окне перейдите на вкладку Solution и выберите проект CodeFirst.

    Добавление ссылки на проект Code-First

Определение классов модели

Как вы уже знаете, при использовании подхода Code-First сначала определяется модель в коде, а затем, на ее основе создается (или модифицируется) база данных. Для нашего примера потребуется создать два класса, описывающих заказчика и его товары. Эти классы добавляются в файл Model.cs, созданный в предыдущем разделе:

public class Customer
{
    public int CustomerId { get; set; }
    public string Name { get; set; }
    public string Email { get; set; }
    public int Age { get; set; }
    public byte[] Photo { get; set; }

    // Ссылка на заказы
    public virtual 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; }
}

Этот кусок кода определяет данные заказчика и его покупки. Для каждого заказчика указывается имя, email, возраст и фотография профиля. Идентификатор используется в качестве первичного ключа таблицы Customer. Кроме того, в этом классе есть ссылка на коллекцию покупок. Эта ссылка выражена в виде виртуального свойства и имеет тип обобщенной коллекции List<T>.

Класс Order содержит идентификатор заказа, который позволяет уникальным образом распознать каждый заказ в таблице. Кроме того этот класс содержит свойства, описывающие название товара, его количество, описание и дату заказа. Также здесь указана ссылка на покупателя в виде свойства Customer.

Установка Entity Framework 6 в проект

Всякий раз, когда вам впервые понадобится использовать Entity Framework в проекте при подходе Code-First, вы должны будете добавить ссылки на библиотеки EF, после чего можно будет работать с Entity Framework в коде. Используйте для этого следующие шаги:

  1. Начиная с версии 4, библиотека Entity Framework входит в удобный менеджер пакетов NuGet. Чтобы добавить поддержку в Entity Framework с помощью NuGet, выберите в окне Solution Explorer проект CodeFirst, щелкните по нему правой кнопкой мыши и выполните команду из контекстного меню Manage Nuget Packages.

  2. В появившемся диалоговом окне выберите последнюю версию Entity Framework на вкладке Online -> nuget.org и нажмите кнопку Install:

    Установка Entity Framework с помощью Nuget
  3. После этого появится окно с описанием лицензии на использование Entity Framework. Согласитесь с условиями и на жмите кнопку “I accept”, после чего NuGet установит в ваш проект Entity Framewrok.

  4. После этого добавьте ссылку на ключевое пространство имен в Entity Framework - System.Data.Entity в файле Model.cs:

    using System.Data.Entity;

Повторите эти действия без 4 пункта для установки Entity Framework в проект ASP.NET.

Класс контекста данных

Сами по себе классы модели, созданные ранее, не имеют ничего общего с Entity Framework. На данном этапе они просто описывают структуру бизнес-модели, которая используется в приложении. Чтобы Entity Framework был в курсе, что эти классы служат также для управления данными в базе данных, нужно использовать класс контекста. EF имеет два базовых класса контекста:

ObjectContext

Этот класс является более общим классом контекста данных, и используется начиная с самых ранних версий Entity Framework.

DbContext

Этот класс контекста данных появился в Entity Framework 4.1 и он обеспечивает поддержку подхода Code-First (ObjectContext также обеспечивает работу подхода Code-First, но он труднее в использовании). Далее мы будем использовать DbContext.

Для создания класса контекста добавьте следующий новый класс SampleContext в проект CodeFirst:

using System.Data.Entity;

namespace CodeFirst
{
    public class SampleContext : DbContext
    {
        // Имя будущей базы данных можно указать через
        // вызов конструктора базового класса
        public SampleContext() : base("MyShop")
        { }

        // Отражение таблиц базы данных на свойства с типом DbSet
        public DbSet<Customer> Customers { get; set; }
        public DbSet<Order> Orders { get; set; }
    }
}

Этот небольшой класс контекста представляет полный слой данных, который можно использовать в приложениях. Благодаря DbContext, вы сможете запросить, изменить, удалить или вставить значения в базу данных. Обратите внимание на использование конструктора в этом классе с вызовом конструктора базового класса DbContext и передачей ему строкового параметра. В этом параметре указывается либо имя базы данных либо строка подключения к базе данных (Entity Framework достаточно интеллектуален чтобы отличить тип параметра). В данном случае мы указываем явно имя базы данных, т.к. по умолчанию, при генерации базы данных Entity Framework использует имя приложения и контекста данных (например CodeFirst.SampleContext), которое нам не подходит.

После этого скомпилируйте приложение (горячая клавиша F6), чтобы исключить наличие ошибок на данном этапе.

Работа с данными

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

  1. Щелкните правой кнопкой мыши по имени проекта веб-сайта ASP.NET в окне Solution Explorer и выберите в контекстном меню команду Add --> New Item. В открывшемся диалоговом окне выберите шаблон Web Form, находящийся на вкладке Web. Назовите форму Default.aspx и нажмите кнопку Add.

  2. Измените разметку формы на ту, которая показана в примере ниже:

    <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" 
        Inherits="ProfessorWeb.EntityFramework.Default" %>
    
    <!DOCTYPE html>
    <html>
    <head runat="server">
        <title>Code-First в Entity Framework</title>
    </head>
    <body>
        <div class="form">
            <form id="form1" runat="server" enctype="multipart/form-data">
                <h3>Данные заказчика</h3>
                <div class="data">
                    <div>
                        <label>Имя</label>
                        <input name="name" />
                    </div>
                    <div>
                        <label>Email</label>
                        <input name="email" />
                    </div>
                    <div>
                        <label>Возраст</label>
                        <input name="age" />
                    </div>
                    <div>
                        <label>Фото</label>
                        <input type="file" name="photo" />
                    </div>
                    <input type="submit" value="Вставить в БД" />
                </div>
            </form>
        </div>
    
        <style>
            .form { position: absolute; left: 50%; width: 470px; margin-left: -235px; background: #888; border-radius: 5px; top: 20px; }
            form { background: #fff; border-radius: 2px;  margin: 5px;  }
            .data { border-top: 1px solid #d5d5d5; padding: 10px 15px; }
            .data div { margin: 8px 0; }
            h3 { padding: 10px 15px; margin: 0; }
            label { min-width: 100px; display: block; float: left; }
            input[type="submit"] { margin-top: 10px; }
        </style>
    </body>
    </html>

    Здесь мы просто добавляем форму, с помощью которой можно вставить данные заказчика в таблицу Customers.

  3. Теперь нужно добавить в файл отделенного кода веб-формы Default.aspx.cs обработку данных формы:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Data.Entity;
    using System.Web;
    using System.Web.UI;
    using System.Web.ModelBinding;
    using System.IO;
    using CodeFirst;
    
    namespace ProfessorWeb.EntityFramework
    {
        public partial class Default : System.Web.UI.Page
        {
            protected void Page_Load(object sender, EventArgs e)
            {
                if (Page.IsPostBack)
                {
                    Customer customer = new Customer();
    
                    // Получить данные из формы с помощью средств
                    // привязки моделей ASP.NET
                    IValueProvider provider = 
                        new FormValueProvider(ModelBindingExecutionContext);
                    if (TryUpdateModel<Customer>(customer, provider))
                    {
                        // Загрузить фото профиля с помощью средств .NET
                        HttpPostedFile photo = Request.Files["photo"];
                        if (photo != null)
                        {
                            BinaryReader b = new BinaryReader(photo.InputStream);
                            customer.Photo = b.ReadBytes((int)photo.InputStream.Length);
                        }
    
                        // В этой точке непосредственно начинается работа с Entity Framework
    
                        // Создать объект контекста
                        SampleContext context = new SampleContext();
    
                        // Вставить данные в таблицу Customers с помощью LINQ
                        context.Customers.Add(customer);
    
                        // Сохранить изменения в БД
                        context.SaveChanges();
                    }
                }
            }
        }
    }

    В этом коде мы используем механизм привязки моделей ASP.NET – очень удобного средства, пришедшего из ASP.NET MVC. Поле Photo хранит массив двоичных данных файла и для него привязка не работает, поэтому картинку в виде двоичного объекта мы загружаем вручную. Мы специально усложнили пример, добавив поле типа byte[], чтобы показать широкие возможности Entity Framework по поддержке работы с базами данных (в частности в этом случае поле Photo будет проецироваться в базе данных на объект BLOB (Binary Large OBject), имеющий тип VARBINARY(max)).

    Код работы с Entity Framework в этом примере создает объект контекста SampleContext и использует его, чтобы добавить новые данные в таблицу Customers. Вызов метода SaveChanges() сохраняет изменения в базе данных и при первой вставке данных вызов SaveChanges() создаст базу данных.

  4. Запустите это приложение, введите данные в форму и нажмите кнопку “Вставить в БД”:

    Ввод данных в тестовой веб-форме

После отправки формы вы можете повторить действия и вставить новых заказчиков. Обратите внимание, что при первой отправке возникает заметная задержка в ответе от сервера – именно в данной точке и создается база данных. Когда вы отправляете форму второй раз, эта задержка исчезает, т.к. данные вставляются в уже существующую базу данных.

Соглашения о конфигурации сгенерированной базы данных

После проделанных действий особый интерес вызывает структура сгенерированной базы данных. Чтобы проверить, что код выполнился правильно, откройте окно Server Explorer в Visual Studio, нажмите на кнопке Connect to Database, в открывшемся окне выберите поставщик SQL Server и настройте подключение к созданной базе данных MyShop, используя имя сервера “.\SQLEXPRESS” в окне Add Connection. После этого в окне Server Explorer отобразится новое подключение:

Подключение к сгенерированной базе данных

Как и ожидалось, в базе данных были созданы две таблицы, имеющие соответствующие столбцы. Согласно соглашениям Entity Framework по именованию таблиц, их название генерируется в множественном числе (основываясь на правилах английского языка), т.е. для класса Customer генерируется таблица Customers, для класса Order генерируется таблица Orders. Названия столбцов соответствуют названиям свойств сущностных классов, а типы данных .NET преобразованы в типы данных T-SQL: Int32 в INT, String в NVARCHAR(max), byte[] в VARBINARY(max) и т.д. Чтобы увидеть это, щелкните по имени таблицы в окне Server Explorer и выберете пункт Open Table Defenition:

Структура созданной таблицы Customer

Обратив пристальный взгляд на структуру таблиц, вы сможете увидеть некоторые соглашения о проецировании сущностных классов, которые используются в Code-First. Например, свойства CustomerId и OrderId в сущностном классе Entity Framework преобразовал в первичные ключи в базе данных (EF автоматически ищет подстроку “Id” в именах свойств модели с помощью механизма рефлексии). Эти поля используют автоинкремент с помощью инструкции IDENTITY (1,1) и не могут иметь значение NULL.

Если вы откроете структуру таблицы Orders, то сможете увидеть, что Entity Framework создал внешний ключ Customer_CustomerId, ссылающий на таблицу Customers. Это было достигнуто за счет того, что мы указали виртуальное свойство в классе Customer ссылающееся на класс Order и добавили обратное свойство в классе Order, ссылающееся на Customer. Благодаря тому, что тип виртуального свойства унаследован от IEnumerable<T>, Entity Framework догадался, что нужно реализовать отношение “один ко многим” (one-to-many) между этими таблицами. При подходе к именованию внешнего ключа EF использовал следующее соглашение:

[Имя навигационного свойства]_[Имя первичного ключа родительской таблицы]

Навигационные свойства в Entity Framework позволяют организовать взаимодействие между таблицами базами данных. Как вы уже видели, чтобы в родительском классе сослаться на другой связанный класс, навигационное свойство помечается как виртуальное. В Code-First есть также различные свойства для реализации отношений 0..1-1, 1-1 и many-to-many в таблицах, с которыми мы познакомимся позже, при подробном изучении аспектов Entity Framework.

Обратите внимание также на то, что Entity Framework сгенерировал еще одну таблицу с названием __MigrationHistory. Эта таблица хранит различные версии изменения структуры базы данных. В частности, в поле Model эта таблица хранит метаданные модели, представленные в виде двоичного объекта BLOB. Если позже вы измените модель в своем коде, Entity Framework вставит в эту таблицу новую запись, с метаданными новой модели.

Переопределение соглашений о конфигурации базы данных

Все указанные в предыдущем разделе соглашения можно переопределить с помощью средств Entity Framework. Для этого используются аннотации в виде атрибутов C# или строгая типизация с помощью Fluent API.

Использование аннотаций метаданных

Аннотации метаданных являются простейшей формой конфигурации и применяются непосредственно к классам и свойствам класса в виде атрибутов. Эти атрибуты доступны в приложении при наличии ссылок на сборки System.ComponentModel.DataAnnotations.dll и EntityFramework.dll. Помните, что аннотации позволяют простым образом настраивать конфигурацию Entity Framework, но при этом, с помощью аннотаций нельзя настроить некоторые сложные моменты, для которых используется Fluent API.

Как уже было сказано выше, аннотации используют синтаксис атрибутов языка C# или Visual Basic. В случае если вы не знакомы с использованием атрибутов, в C#, атрибуты применяются с использованием квадратных скобок. Например, аннотация данных для идентификации ключа в C# описывается как:

[Key]
public int CustomerId { get; set; }

а в Visual Basic, используются угловые скобки:

<Key>
…

В атрибутах может использоваться именованный параметр, в C# это выражается с помощью знака равенства:

[Table(Schema=”dbo”)]

в то время как Visual Basic использует двоеточие перед знаком равенства:

<Table(Schema:=”dbo”)>

Давайте используем аннотации метаданных к нашему классу Customer. Есть несколько вещей, которые я бы хотел изменить в этом классе:

  • Поле Name не должно иметь значение NULL в таблице базы данных и длина поля не должна превышать 30 символов.

  • Максимальная длина поля email не должна превышать 100 символов.

  • Поле Age должно находиться в пределах от 8 до 100.

  • Фотография должна хранится в типе изображения Image SQL Server, а не в VARBINARY (max).

Для этого сначала нужно добавить ссылки на пространства имен System.ComponentModel.DataAnnotations и System.ComponentModel.DataAnnotations.Schema в файле Model.cs приложения CodeFirst. После этого измените структуру класса Customer, чтобы указать условия, приведенные выше:

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace CodeFirst
{
    public class Customer
    {
        public int CustomerId { get; set; }

        [Required]
        [MaxLength(30)]
        public string Name { get; set; }

        [MaxLength(100)]
        public string Email { get; set; }

        [Range(8, 100)]
        public int Age { get; set; }

        [Column(TypeName = "image")]
        public byte[] Photo { get; set; }

        // Ссылка на заказы
        public virtual List<Order> Orders { get; set; }
    }

    public class Order
    {
        // ...
    }
}

Атрибут Required не нуждается в указании дополнительных параметров, в то время, как в атрибутах MaxLength, Range и Column эти параметры указаны. Атрибут Column влияет на схему базы данных (в частности позволяет менять тип данных столбца таблицы). Поэтому данный атрибут находится в пространстве имен System.ComponentModel.DataAnnotations.Schema, в отличие от других атрибутов. Аннотации являются компонуемыми, это означает, что вы можете использовать несколько атрибутов к одному свойству или классу, как это было сделано для свойства Name.

Помимо атрибутов, которые могут влиять на структуру базы данных, в пространстве имен System.ComponentModel.DataAnnotations существует атрибуты, которые Entity Framework просто игнорирует (атрибут Range, показанный выше, не влияет на структуру таблицы). Например, к типу Name можно использовать атрибут [MinLegth(3)], чтобы нельзя было указать в качестве имени покупателя строку, короче 3 символов. В T-SQL нельзя напрямую указать минимальную длину поля (хотя существует обходное решение с использованием инструкции CASE), поэтому Entity Framework будет игнорировать этот атрибут. Это показывает, что атрибуты метаданных, являются универсальными и могут использовать не только Entity Framework. Например, атрибуты MinLegth и Range могут использоваться в клиент-серверном коде проверки модели ASP.NET.

После того, как мы внесли изменения в модель данных, будет интересно как Enity Framework изменит структуру базы данных. Если вы теперь запустите приложение и попытаетесь вставить нового заказчика, то вы получите исключение InvalidOperationException. Мы ничего плохого не сделали с моделью данных, проблема заключается в способе инициализации базы данных, используемом в Code-First. Ниже показан текст исключения:

The model backing the 'SampleContext' context has changed since the database was created. Consider using Code First Migrations to update the database.

Модель класса контекста SampleContext была изменена, когда база данных уже была создана. Рассмотрите возможность обновления базы данных с помощью Code First Migrations.

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

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

Другим решением проблемы, которое мы будем использовать в нашем приложении, является использование возможности Code-First обнаруживать изменения в модели данных и автоматически удалять и генерировать новую базу данных. Поведением Code-First в данной ситуации можно управлять с помощью статического метода SetInitializer() класса Database. По умолчанию, этому методу передается экземпляр класса CreateDatabaseIfNotExists, который указывает, что создавать базу данных нужно только в том случае, если базы данных не существует, а если модель изменилась, то нужно сгенерировать исключение.

Давайте изменим это поведение и передадим этому методу экземпляр объекта DropCreateDatabaseIfModelChanges, который указывает Code-First, что при изменении модели данных, базу данных нужно будет удалить и воссоздать с новой структурой. Метод SetInitializer() должен вызываться до непосредственной работы с Entity Framework, в нашем случае мы можем вызвать его в начале метода Page_Load:

protected void Page_Load(object sender, EventArgs e)
{
    Database.SetInitializer(
        new DropCreateDatabaseIfModelChanges<SampleContext>());

    if (Page.IsPostBack)
    {
        // ...
    }
}

Теперь, когда вы запустите приложение и попытаетесь вставить данные, Code First найдет разницу в новой модели и, с разрешения инициализатора, удалит и заново создаст базу данных. Если вы открыли таблицу базы данных для чтения данных где-то еще (например, в окне Server Explorer среды Visual Studio), Code First будет не в состоянии удалить базу данных, т.к. она используется другим процессом. В этом случае, возникнет заметная задержка при вставке данных, пока EF пытается удалить базу данных, а затем в конце концов будет сгенерировано следующее исключение:

SqlException: Cannot drop database "MyShop" because it is currently in use.

SqlException: Невозможно удалить базу данных "MyShop" т.к. она сейчас используется.

Для решения этой проблемы зачастую хватает закрыть и открыть снова проект в Visual Studio.

Я также рекомендую вам рассмотреть возможность подхода к работе, когда вы вручную изменяете структуру базы данных с помощью T-SQL, а затем отражаете эти изменения в классах модели. Такой подход к работе еще называют Code-Second. Этот подход лишает вас одного из главных преимуществ Code-First – отсутствие непосредственной работы с базой данных, но он является единственной возможностью изменить структуру базы данных не удаляя ее (чтобы не удалять уже вставленные данные).

Допустим, нам нужно изменить структуру таблицы Customer изменив имя столбца Name на FirstName и добавив новый столбец LastName. Чтобы воспользоваться подходом Code-Second выполните следующие шаги:

  1. Используя программу SQL Server Management Studio или окно Server Explorer в Visual Studio удалите таблицу __MigrationHistory.

  2. Выполните следующий SQL-код для изменения структуры таблиц (для этого щелкните правой кнопкой мыши по имени таблицы Customer в окне Server Explorer и выберите из контекстного меню команду New Query):

    USE MyShop;
    ALTER TABLE Customers ADD LastName NVARCHAR(30) NULL;
    EXEC sp_rename @objname = 'Customers.Name', @newname = 'FirstName';
  3. Обновите модель данных, чтобы отразить эти изменения:

    public class Customer
    {
            public int CustomerId { get; set; }
    
            [Required]
            [MaxLength(30)]
            public string FirstName { get; set; }
    
            public string LastName { get; set; }
    
            [MaxLength(100)]
            public string Email { get; set; }
    
            [Range(8, 100)]
            public int Age { get; set; }
    
            [Column(TypeName = "image")]
            public byte[] Photo { get; set; }
    
            // Ссылка на заказы
            public virtual List<Order> Orders { get; set; }
    }
  4. Далее нам понадобится изменить разметку веб-формы и код обработчика, чтобы использовать эти изменения:

    ...
    <div class="data">
        <div>
            <label>Имя</label>
            <input name="firstname" />
        </div>
        <div>
            <label>Имя</label>
            <input name="lastname" />
        </div>
        ...

    В коде обработчика Default.aspx.cs удалите вызов метода SetInitializer().

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

Использование Fluent API

Настройка соглашений о конфигурации базы данных с помощью аннотаций является довольно простой, но она не может обеспечить проецирование сложной конфигурации, например создания отношения many-to-many между таблицами. Fluent API дает вам доступ к таким глубоким настройкам, но при этом является более сложным в использовании.

Концепция Fluent API заключается в вызове ряда стандартных методов для описания настройки конфигурации базы данных. Использование средства IntelliSense в редакторе кода Visual Studio облегчает использование этих методов. Например, в Code-First Fluent API, вы можете использовать метод Entity(), чтобы выбрать объект для настройки. IntelliSense покажет вам все методы, которые могут быть использованы для настройки объекта. Если вы затем используете метод Property(), чтобы выбрать свойство объекта сущности, то вы увидите все доступные настройки для конфигурирования.

Еще одним преимуществом Fluent API перед аннотациями является то, что он не засоряет код модели и организует взаимосвязь модели с контекстом данных.

Когда приходит время для построения модели, DbContext смотрит на структуру классов модели. Fluent API позволяет вмешаться в этот процесс и передать контексту дополнительные данные для конфигурации. Это становится возможным, благодаря переопределению метода DbContext.OnModelCreating(), который вызывается перед тем, как контекст построит сущностную модель данных. Этот метод является виртуальным, так что вы можете изменить его и вставить собственную логику, используя средства Fluent API.

Структура этого метода следующая:

protected override void OnModelCreating(DbModelBuilder modelBuilder)

Параметр типа DbModelBuilder в этом методе, позволяет добавлять настройки конфигурации. Методы класса DbModelBuilder являются обобщенными и поддерживают указание лямбда-выражений в качестве параметров (принимают типы делегатов), благодаря чему обеспечивается быстрая настройка конфигурации. Например, следующий код позволяет настроить таблицу Customer, аналогично показанным ранее атрибутам в модели:

using System.Data.Entity;

namespace CodeFirst
{
    public class SampleContext : DbContext
    {
        public SampleContext() : base("MyShop")
        { }

        public DbSet<Customer> Customers { get; set; }
        public DbSet<Order> Orders { get; set; }

        // Переопределяем метод OnModelCreating для добавления
        // настроек конфигурации
        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            // настройка полей с помощью Fluent API
            modelBuilder.Entity<Customer>()
                .Property(c => c.FirstName).IsRequired().HasMaxLength(30);

            modelBuilder.Entity<Customer>()
                .Property(c => c.Email).HasMaxLength(100);

            modelBuilder.Entity<Customer>()
                .Property(c => c.Photo).HasColumnType("image");
                
            // вы можете настроить таблицу
            modelBuilder.Entity<Customer>().ToTable("NewName_Customer");
        }
    }
}

Подходы с использованием аннотаций и Fluent API можно использовать в Code-First, преимущество при указании одинаковых правил отдается Fluent API.

Если у вас используется много настроек конфигурации Fluent API, то код метода OnModelCreating() может быстро стать перегруженным. Можно организовать код настроек Fluent API с помощью создания специальных классов конфигурации, унаследованных от EntityTypeConfiguration. Например, мы добавили следующий файл EntityConfigurations.cs в проект CodeFirst:

using System.Data.Entity.ModelConfiguration;

namespace CodeFirst
{
    // Настройки для Customer
    public class CustomerConfiguration : EntityTypeConfiguration<Customer>
    {
        public CustomerConfiguration()
        {
            this.Property(c => c.FirstName).IsRequired().HasMaxLength(30);
            this.Property(c => c.Email).HasMaxLength(100);
            this.Property(c => c.Photo).HasColumnType("image");
            this.ToTable("NewName_Customer");
        }
    }

    // Настройки для Order
    public class OrderConfiguration : EntityTypeConfiguration<Order>
    {
        public OrderConfiguration()
        {
            this.Property(o => o.Description).HasMaxLength(500);
        }
    }
}

Чтобы добавить эту конфигурацию, в вызове OnModelCreating() можно использовать свойство Configurations класса DbModelBuilder:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
     modelBuilder.Configurations.Add(new CustomerConfiguration());
     modelBuilder.Configurations.Add(new OrderConfiguration());
}

На этом мы заканчиваем краткое рассмотрение подхода для работы с Code-First, позже, при описании Entity Framework, мы более подробно познакомимся со всеми деталями использования аннотаций данных и Fluent API.

Пройди тесты
x

Чат для изучающих C# Подписывайтесь!