Расширенные возможности настройки Code-First
141Работа с базами данных в .NET Framework --- Entity Framework 6 --- Расширенные возможности настройки Code-First
Итак, в предыдущих статьях мы описали практически всю функциональность, которую нужно знать при определении модели данных с Code-First. Теперь мы знаем какие соглашения использует Entity Framework при проектировании базы данных из классов модели, и как переопределять эти соглашения с использованием аннотаций данных и Fluent API. Тем не менее, Code-First также включает в себя некоторые более продвинутые функциональные возможности, которые вы можете использовать в своих приложениях. Далее мы рассмотрим некоторые из таких возможностей.
Удаление соглашений
В предыдущих статьях мы видели, что Code-First следует некоторым соглашениям при проектировании базы данных из классов модели данных. Вы можете удалить одно или несколько соглашений из вашего приложения, используемых по умолчанию. Соглашения реализуется в виде классов, находящихся в пространстве имен System.Data.Entity.ModelConfiguration.Conventions. На рисунке ниже показан полный список соглашений, используемых в Code-First версии Entity Framework 6:
Например, вы можете удалить соглашение о каскадном удалении, при котором каскадное удаление автоматически добавляется при связи один-ко-многим. Для этого в Fluent API можно использовать метод Remove(), который вызывается на объекте ConvenctionsConfiguration, который доступен через свойство Conventions объекта DbModelBuilder. Ниже показан соответствующий пример:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
}
Теперь при создании связи один-ко-многим каскадное удаление не будет использоваться автоматически, это позволяет сэкономить время, если в приложении нужно везде отключить это соглашение. Если вы захотите явно включить каскадное удаление при отключенном соглашении, вы можете использовать для этого средства Fluent API, о которых мы рассказывали в статье “Каскадное удаление данных”.
Управление кэшированием модели
Ранее вы видели, что Code-First заботится о многих вещах используя автоматические соглашения, но вы можете взять контроль над ними и изменить поведение, когда это необходимо. Кэширование модели не является исключением, хотя до этого раздела мы не говорили о том, что Code-First использует кэширование. На самом деле Code-First сохраняет в памяти процесса классы модели и примененные к ним конфигурации, так, что при повторном запуске приложения Code-First загружает кэшированную версию модели если она не изменилась.
Процесс создания модели включает в себя загрузку сущностных типов с помощью свойств DbSet<T> класса контекста, применение к ним сначала автоматических соглашений, а затем настроек, которые вы указывали с помощью аннотаций данных и Fluent API. Этот процесс является довольно дорогостоящим в плане производительности, особенно если вы используете большую модель и применяете к ней сложные настройки. Чтобы избежать этих накладных расходов, Code-First использует кэширование после первого построения модели, а затем просто воссоздает модель из кэша приложения.
Наблюдать процесс кэширования вы можете на примере вызова метода OnModelCreating(). Напомним, что этот метод используется в классе контекста и вызывается перед построением модели. Мы можем добавить в него отладочное сообщение:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
System.Diagnostics.Debug.WriteLine("Вызов OnModelCreating");
}
Если вы запустите приложение и начнете работать с данными с помощью Entity Framework, то в окне отладки Output высветится это сообщение, но только один раз, при первом обращении к модели. При последующих обращениях к модели используется ее кэшированная версия и метод OnModelCreating() больше не вызывается.
Вы можете переопределить стандартный процесс кэширования модели в приложениях Entity Framework. Обычно это используется довольно редко. Одним из примеров необходимости отключения кэширования является использование нескольких схем для одинаковых таблиц в базе данных. Например, на одном сайте может находится блог и форум, на которых могут регистрироваться пользователи, при этом пользователи с блога не должны иметь доступ к форуму через свою учетную запись на блоге. Для такой модели приложения в базе данных можно сохранить две таблицы Users, имеющих разную схему, например blog.Users и forum.Users. Очевидно, что эти таблицы будут иметь одну структуру и для них можно создать один класс модели User в приложении с разными настройками схемы базы данных. В этом случае нужно будет отключить явное использование кэширования, чтобы в приложении не возникло путаницы между работой с двумя таблицами Users.
Отключить кэширование модели в настройках приложения вы не можете, но вы можете вручную построить модель используя метод DbModelBuilder.Build(). Получить доступ к объекту DbModelBuilder в коде приложения не так то просто. Для этого вы должны вручную создать этот объект, указав какие классы должны отображаться на таблицы базы данных, как показано в примере ниже:
protected void Page_Load(object sender, EventArgs e)
{
DbModelBuilder builder = new DbModelBuilder();
// Указываем необходимы классы модели
builder.Entity<Customer>();
builder.Entity<Order>();
// Запускаем вручную процесс создания модели
builder.Build(new DbProviderInfo("System.Data.SqlClient", "2012"))
.Compile();
}
Обратите внимание, что в методе Build() мы указываем тип поставщика данных. В нашем случае таким поставщиком будет SQL Server 2012.
Использование ObjectContext вместо DbContext
Во всех примерах, показанных ранее мы использовали класс контекста, унаследованный от DbContext, при этом мы использовали DbContext API для работы с данными. Как вы знаете, DbContext появился в версии Entity Framework 4.1 вместе с подходом для работы Code-First. Альтернативой является использование класса ObjectContext, который был доступен с начала появления платформы Entity Framework. Мы описывали этот класс в статье “Сущности и класс ObjectContext” раздела ADO.NET, а также использовали его в примерах LINQ to Entities.
Рекомендуемым классом контекста в современных приложениях Entity Framework является DbContext. Теме не менее, если вам необходимы продвинутые возможности ObjectContext через DbContext, вы можете реализовать в вашем классе контекста интерфейс IObjectContextAdapter, для доступа к свойствам и методам ObjectContext из DbContext. Также вы можете напрямую использовать ObjectContext в Code-First. Это бывает необходимо, если вы хотите применить подход Code-First для старого приложения, в котором написана куча методов доступа к данным на основе ObjectContext.
Подробно описывать здесь возможности ObjectContext мы не будем (для этого вы можете посетить статьи, ссылки на которые показаны выше), вместо этого давайте покажем как создать класс контекста с использованием ObjectContext, аналогичный тому, что мы использовали в примерах ранее с использованием DbContext (начиная с версии Entity Framework 4.1 класс ObjectContext был перенесен из основного пространства имен System.Data.Entity в пространство имен с классами ядра конфигурации EF System.Data.Entity.Core.Objects):
using System.Data.Entity.Core.Objects;
using System.Data.Entity.Core.EntityClient;
namespace CodeFirst
{
public class SampleContext : ObjectContext
{
public SampleContext(EntityConnection connection)
: base(connection)
{
this.Customers = CreateObjectSet<Customer>();
this.Orders = CreateObjectSet<Order>();
}
public ObjectSet<Customer> Customers { get; private set; }
public ObjectSet<Order> Orders { get; private set; }
}
}
Обратите внимание, что здесь для установки объектов сущностей используется объект ObjectSet, а не DbSet, при этом эти объекты нужно автоматически зарегистрировать в конструкторе класса контекста с помощью метода CreateObjectSet.