Entity Framework Code First на практике
В этой публикации хочу поделиться личным опытом использования Entity Framework (EF) в реальном приложении и дать несколько полезных советов использования данного фреймворка.
Еще раз хочу повторить, что, на мой взгляд, в enterprise приложении главное — это данные, а не ваше приложение. Приложения приходят и уходят, заменяются на более новые, а данные остаются и используются годами. Поэтому в любом моем приложении база данных всегда создается проектировщиком базы данных, а не моим приложением.
Первое, что нужно сделать, если у вас уже есть готовая база данных — это создать классы, которые бы соответствовали таблицам. Вручную это делать очень утомительно, поэтому:
Совет №1. Генерация класса на основе таблицы
Cкрипт T-SQL, который можно взять отсюда, реально работает и очень удобен.
Совет №2. Прегенерация view
EF известен очень долгим временем обработки первого запроса на получение и сохранение данных. Для того, чтобы исправить эту ситуацию, можно использовать прегенерацию view.
Прегенерация view — это нечто, что происходит внутри фреймворка при выполнении первого запроса. Этот процесс можно перенести на этап компиляции вашего приложения, что существенно сократит скорость выполнения первого запроса. Для разных версий EF способы несколько отличаются. Я использовал этот подход и полностью остался им доволен.
Совет №3. Массовое обновление данных с помощью DetectChanges
По умолчанию фреймворк настроен на то, чтобы автоматически отслеживать изменения, которые затем нужно будет сохранить в базу данных.
Например, рассмотрим такой простой пример:
Запустив его, многие наверняка будут удивлены, что основное время будет потрачено не собственно на запрос к базе данных, а на вставку объектов в сам dbContext. Дело в том, что при изменении данных внутри DbContext-а происходит масса проверок и других малоизученных вещей, подробнее о которых можно прочесть здесь. Чтобы этого избежать можно отключить слежение за изменениями в классе DbContext, а затем явно вызвать метод DetectChanges(), который сам эти изменения обнаружит. Так, данный код будет работать значительно быстрее:
Совет №4. Следите за sql-запросами, которые формирует фреймворк
Это довольно универсальный совет, применимый, наверное, ко всем ORM, но многие почему-то о нем забывают. И после внедрения приложения, открыв профайлер и увидев 50 запросов к БД для показа довольно простой формы, бывают крайне удивлены.
Между тем, фреймворк предлагает механизмы для некоторой оптимизации. Среди этих механизмов наиболее известный — метод Include, который «вытягивает» дочерние объекты в том же запросе. Например:
Совет №5. Изолируйте логику работы с базой данных
Хотя в названии EF есть слово «фреймворк», его можно использовать и как обычную библиотеку просто для упрощения работы с базой данных.
В большинстве книг и презентаций мы видим код, похожий на этот:
Проблема этого кода в том, что он плотно привязывает все части приложения к Entity Framework. Это не хорошо и не плохо само по себе, но при этом вы должны понимать, что существенно осложняете себе жизнь, если архитектуру доступа к данным придется менять. А вариантов тут может быть масса:
Этот подход хорош тем, что ваше приложение будет знать только о ваших объектах и о том, как их можно получить из БД и как сохранить в БД. Поверьте, этим вы существенно облегчите себе жизнь.
Code-first в Entity Framework
Entity Framework предлагает удобный дизайнер, огромное количество вариантов маппинга, автогенерацию классов-моделей, но на все это есть жирный минус – гигантские и раздутые сгенерированные классы, которые к тому же нельзя изменять вручную – ибо при каждом изменении модели в дизайнере, все будет пересоздано заново. Сравните это с чистыми классами, и добавленными к ним атрибутами, как в Linq2Sql, и вы поймете, почему такое количество людей заявляет о легковесности Linq2Sql и монструозности EF.
Конечно, каждая проблема имеет решение, и эта не исключение. Частичные классы позволят добавить нужный функционал, а специально созданные классы с правилами валидации, помеченные атрибутом [MetadataType], дадут возможность использовать атрибуты валидации для классов-моделей. Но вместе это получается не очень красиво – размазанные по проекту классы, увеличение их количества, и все та же сложность в поддержке.
Не стоит также забывать об условиях работы классов-моделей: они должны либо наследоваться от EntityObject или реализовывать интерфейсы EntityWithKey, IEntityWithChangeTracker и IEntityWithRelationships
Так что же делать тем, кто хочет получить максимально простые классы для работы внутри ORM?
РОСО и Code-First
Впервые поддержка РОСО(Plain Old CLR Objects), т.е работы с простыми классами, как с моделями появилась в EF 4.
Примечание
Несмотря на заявленную поддержку в Visual Studio SP1, мне для корректной работы все-таки пришлось установить Visual Studio Tools for SQL Server Compact 4 и EF Code First Package
Например, вот такой класс
Описывает полнофункциональную сущность «автор», причем он не нуждается ни в базовом классе, ни в интерфейсах, ни в атрибутах или метадананных.
Простейшая же точка входа будет выглядеть так:
И все! Сразу после создания объекта Library, вы можете работать с ним, используя все преимущества EF.
Code-First – новая возможность, стоящая в одном ряду с Model-First и Database-First. Теперь можно сначала написать код, описывающий классы-модели, а потом фреймворк автоматически создаст БД по такому коду.
Самое приятное, что виды отношений будут распознаны – достаточно определить просто ссылки на объекты для 1:1, ICollection для отношения 1:n, и взаимные ICollection для m:n. В этом случае промежуточная таблица также создастся автоматически. Для обеспечения «ленивой» загрузки хватит ключевого слова virtual в определении свойства.
Также полностью поддерживаются атрибуты из пространства имен System.ComponentModel.DataAnnotations
Основные из них:
• Code first что это – указывает на то, что данное свойство будет ключевым.
• [NotMapped] – не отображать свойство на колонки в БД.
• [Column(“columnName”, TypeName=”typeName”] – указывает колонку в БД, на которую отображается поле и его тип. Полезно когда в классе есть свойство типа byte[], хранящий, например рисунок, а в базе это отобразится на специальный тип image.
UPD Однако нельзя указать подмножество типа таким образом: например для строк по умолчанию в базе выбирается тип nvarchar(MAX) и с помощью этого атрибута нельзя явно указать длину поля nvarchar(30)
• [MinLength], [MaxLength], [Required] и другие для обеспечения проверки данных
Code-First заточен под работу с новой версией SQL Server CE 4.0. Поддержка этой базы данных включена в MVC3, и некоторые люди уже переводят сайты на работу с такой БД, а не стандартным SQL Express.
По умолчанию, генератор БД пытается создать SQL Express базу данных, если у него не получается – тогда SQL Server CE. Это поведение легко переопределяется в app.config
Обратите внимание, что имя строки соединения должно совпадать с именем класса-точки доступа(DbContext).
Также можно определить стратегию создания базы данных (первичная генерация или обновление). Всякая стратегия – это класс, реализующий интерфейс IDatabaseInitializer, т.е. можно определить свою стратегию, заносящую какие-либо данные в БД в период активной разработки программы.
Предустановленные стратегии – CreateDatabaseIfNotExists (создавать БД, если ее нет), DropCreateDatabaseAlways (БД всегда будет удаляться и пересоздаваться), DropCreateDatabaseIfModelChanges (пересоздается при изменениях в модели)
Изменяется стратегия работы с БД вызовом
Database.SetInitializer(new DropCreateDatabaseAlways());
При создании БД фреймворк создает еще одну, служебную таблицу EdmMetadata, в которой хранится хеш-отпечаток модели на момент создания. Это неудобно при активной разработке БД и классов, но есть решение – перегрузка метода OnModelCreating класса DbContext.
Вообще, Conventions это довольно мощная вещь. Они позволяют конфигурировать отображение свойств на БД на основе разнообразных признаков, подключать/отключать пространства имен и многое другое. Подробнее о них можно почитать здесь
Code-First действительно предоставляет много возможностей по управлению кодом моделей, максимально удобным способом для вас. Я считаю, что в особенности для небольших проектов, как и для мобильных устройств(да, SQL Server CE 4.0 будет поддерживаться в Windows Phone Mango) такой подход будет максимально оправдан.
Разработка баз данных с Code First
Повсеместно принято, что в «серьезных» CRUD приложениях база данных становится во главу угла. Ее проектируют самой первой, она обрастает хранимыми процедурами (stored procedures), с ней приходиться возиться больше всего. Но это не единственный путь! Для Entity Framework есть Code First подход, где главным становится код, а не база. Преимущества:
Есть и пара недостатков, но они скорее связаны с Entity Framework, а не с Code First подходом как таковым; о них чуть позже.
Ниже я покажу на примере, насколько просто разрабатывать с Code First подходом.
Пример
Возьмем простую модель:
В качестве фронт-энда будет ASP.NET MVC, так что создаем соответствующий проект. Выбираем No Authentication — в этом проекте нельзя будет логиниться и весь контент доступен для всех.
Я сделал еще 2 проекта — для бизнес-объектов и DAL, но при желании можно просто создать соответствующие папки в web проекте. Не забудте установить Entity Framework в соответствующие проекты через NuGet.
Создаем классы, которые будут отображать сущности (entities):
Как видно, все повторяющееся свойства (properties) можно убрать в абстрактный класс и наследоваться от него. В данном случае у каждой таблицы будет Primary Key колонка типа Guid, который будет генерироваться при записи в базу.
Grade — это просто энумератор, ничего особенного:
Создаем контекстный класс:
Отношения дефинированы через Fluent API, читаются с конца — например, Student — Enrollment относятся как one (Student): many (Enrollment).
Стоит отметить, что конфигурировать модели можно как через Fluent API, так и аннотациями. Для некоторых настроек аннотаций не существует, но их можно создать самим. Я предпочитаю все-же Fluent API.
И, наконец, заполнение базы данными:
Примечание: как следует из названия DropCreateDatabaseIfModelChanges, база будет дропаться при изменениях в соответствующих классах моделей. То есть данные — капут.
Как реализовать миграции, чтобы данные не капут, выходит за область этой статьи.
Последнее, что надо сделать — добавить информацию в web.config. Используем LocalDb, которая идет вместе с Visual Studio, которой вполне достаточно для целей этого проекта. Следующий код идет в элемент configuration:
А следующая разметка — в элемент entityFramework:
В атрибуте type элемента context указываются через запятую название класса контекста и assembly, где этот класс находится. То же самое для инициализатора в элементе databaseInitializer.
Это вообщем-то и все, проект готов к запуску.
Скачать пример можно тут.
Недостатки
Во-первых, к существующей базе данных подобный подход применить сложно. Так что вообщем-то это для разработки с нуля.
Часто подножки ставит Entity Framework, который часто принимает решения за программиста — есть так называемые конвенции, что, допустим, property который называется Id, будет по умолчанию преобразован в Primary Key таблицы. Мне такой подход не нравится.
Продолжение темы
Разработка с помощью Code First подхода в Entity Framework достаточно объемная тема. Я не касался вопроса миграций, проблем с многопоточностью (concurrency) и многого другого. Если сообществу интересно, я могу продолжить эту тему в дальнейших статьях.
1. Getting started with Entity Framework 6 Code First using MVC 5
2. Database initialization in Code-First
3. Lerman J., Miller R. — Programming Entity Framework. Code First (2011)
Разработка REST API — что такое Code First подход?
Это четвертая статья в серии статей по REST API:
Разработка хорошего REST API важна для того, чтобы иметь хорошие микросервисы. Подход Code-First фокусируется на генерации контракта из кода. Это наилучший из возможных подходов?
Вы изучите
Что такое Code-First подход?
Всякий раз, когда вы разрабатываете сервис, такой как REST API или SOAP API, вы можете выбрать один из двух подходов:
Spring Boot Code First пример REST API
Мы разрабатываем RESTful веб-сервис, используя Spring Boot Framework для генерации API. Например, в API retrieveAllUsers () мы открываем URI «/users» и
возвращаем всех пользователей (ресурс /users),
вызывая метод сервиса.
Когда мы переходим на этот URL, мы возвращаем всех пользователей:
Аналогичным образом определены и другие сервисные методы, каждый из которых имеет свой собственный URI. В этом примере мы берем код и генерируем из него документацию. В этой документации указано, как пользователь может использовать сервис. Для этого мы используем формат документации Swagger:
Swagger позволяет нам генерировать документацию из кода. Например, вот что Swagger генерирует для запроса на получение всех пользователей:
Он выводит тип получаемого нами ответного сообщения и сопровождающий его статус ответа. Вы даже можете вызвать этот сервис из Swagger получить ответ:
Вы также можете отправить запрос POST в «/users«:
Swagger сообщит нам, как структурировать сообщение запроса и указать внутри него отдельные форматы полей. Он также сообщит вам тип ответа, который вы получите, вместе с кодом ответа. То, что Swagger генерирует из кода, называется контрактом.
Преимущества Code First
Основные преимущества этого подхода:
Недостатки Code First
Недостатки этого подхода заключаются в следующем:
Нет параллельной разработки
Производитель услуг и потребители услуг не могут разрабатывать параллельно. Сначала необходимо разработать сервис, затем сгенерировать контракт, и только после этого можно написать код потребителя, который будет придерживаться контракта. Без понимания контракта потребитель не может быть разработан.
Нет цели для команд
Поскольку договор не может быть известен до того, как сервис будет разработан, не существует цели для различных заинтересованных сторон в разработке. Следовательно, есть все шансы, что направления будут отклоняться, и будут внесены ненужные изменения, что приведет к напрасной трате усилий.
Нет кроссплатформенной совместимости
На некоторых старых платформах не так просто сгенерировать контракт из кода. В результате этого для сгенерированных контрактов довольно часто возникает несовместимость между платформами.
По этому вопросу имеется авторское видео.
Резюме
В этой статье мы исследовали Code First подход построения REST API. Несмотря на то, что подход, основанный на коде, эффективен с точки зрения разработчика, он сталкивается с серьезными проблемами, когда речь идет о совместной разработке поставщика и потребителя.
Создание базы данных с помощью Code First
в этом видео и пошаговом руководстве представлены общие сведения о Code First разработке, предназначенной для новой базы данных. этот сценарий включает в себя целевую базу данных, которая не существует и Code First создаст, или пустую базу данных, в которую Code First добавит новые таблицы. Code First позволяет определить модель с помощью классов C# или VB.Net. Дополнительную конфигурацию можно также выполнить с помощью атрибутов в классах и свойствах или с помощью API Fluent.
Просмотреть видео
в этом видео представлены общие сведения о Code First разработке, предназначенной для новой базы данных. этот сценарий включает в себя целевую базу данных, которая не существует и Code First создаст, или пустую базу данных, в которую Code First добавит новые таблицы. Code First позволяет определить модель с помощью классов C# или VB.Net. Дополнительную конфигурацию можно также выполнить с помощью атрибутов в классах и свойствах или с помощью API Fluent.
Предварительные требования
для выполнения инструкций этого пошагового руководства необходимо установить по крайней мере Visual Studio 2010 или Visual Studio 2012.
1. Создание приложения
чтобы не усложнять, мы создадим простое консольное приложение, которое использует Code First для доступа к данным.
2. Создание модели
Давайте определим очень простую модель с помощью классов. Мы просто определяем их в файле Program. cs, но в реальном мире вы разбиваете классы на отдельные файлы и, возможно, на отдельный проект.
Под определением класса программы в Program. cs добавьте следующие два класса.
Вы заметите, что два свойства навигации (blog. posts и Post.Blog) являются виртуальными. Это включает функцию отложенной загрузки Entity Framework. Отложенная загрузка означает, что содержимое этих свойств будет автоматически загружаться из базы данных при попытке доступа к ним.
3. Создание контекста
Теперь пора определить производный контекст, который представляет сеанс с базой данных, что позволит нам запрашивать и сохранять данные. Мы определяем контекст, производный от System. Data. Entity. DbContext, и предоставляет типизированный DbSetный для каждого класса в нашей модели.
теперь мы начали использовать типы из Entity Framework, поэтому нам нужно добавить пакет NuGet EntityFramework.
Добавьте инструкцию using для System. Data. Entity в начало программы. cs.
Под классом POST в Program. cs добавьте следующий производный контекст.
Ниже приведен полный список сведений о программе. cs, которая теперь должна содержаться в.
Это весь код, необходимый для начала хранения и извлечения данных. Очевидно, что в фоновом режиме происходит довольно много времени, и мы рассмотрим его в действии.
4. чтение & данных записи
Реализуйте метод Main в Program. cs, как показано ниже. Этот код создает новый экземпляр нашего контекста, а затем использует его для вставки нового блога. Затем он использует запрос LINQ для получения всех блогов из базы данных, упорядоченного в алфавитном порядке по названию.
Теперь можно запустить приложение и протестировать его.
Где мои данные?
По соглашению DbContext создала базу данных.
Просмотр — Обозреватель сервера
Щелкните правой кнопкой мыши подключения к данным и выберите Добавить подключение.
если вы не подключились к базе данных из обозреватель сервера, прежде чем нужно будет выбрать Microsoft SQL Server в качестве источника данных
Подключение либо LocalDB, либо SQL Express в зависимости от того, какой из них был установлен.
теперь можно проверить схему, созданную Code First.
DbContext выработала, какие классы следует включить в модель, взглянув на определяемые нами свойства DbSet. затем он использует набор соглашений по умолчанию Code First для определения имен таблиц и столбцов, определения типов данных, поиска первичных ключей и т. д. Далее в этом пошаговом руководстве мы рассмотрим, как можно переопределить эти соглашения.
5. Работа с изменениями модели
Пришло время внести некоторые изменения в нашу модель, при внесении этих изменений нам также потребуется обновить схему базы данных. для этого мы будем использовать функцию, именуемую Code First Migrations, или миграцию для коротких.
Миграция позволяет нам выполнить упорядоченный набор шагов, описывающих порядок обновления схемы базы данных. Каждый из этих шагов, называемый миграцией, содержит код, описывающий применяемые изменения.
первым шагом является включение Code First Migrations для нашего BloggingContext.
инструменты- библиотека диспетчер пакетов — > консоль диспетчер пакетов
Запустите команду Enable-Migrations в консоли диспетчера пакетов.
В наш проект добавлена новая папка миграции, содержащая два элемента:
Теперь изменим нашу модель, добавив свойство URL в класс Blog:
Новый столбец URL-адреса теперь добавляется в таблицу блоги базы данных:
6. заметки к данным
До сих пор мы просто добавим EF к модели, используя соглашения по умолчанию, но бывают случаи, когда наши классы не соответствуют соглашениям, и нам нужно иметь возможность выполнить дальнейшую настройку. Для этого есть два варианта. Мы рассмотрим примечания к данным в этом разделе, а затем API Fluent в следующем разделе.
Новая таблица будет добавлена в базу данных:
Полный список заметок, поддерживаемых EF,:
7. Fluent API
В предыдущем разделе мы рассматривали использование заметок к данным для дополнения или переопределения того, что было обнаружено соглашением. другой способ настройки модели — через API-интерфейс Code First fluent.
Большую часть конфигурации модели можно выполнить с помощью простых заметок к данным. API-интерфейс Fluent — это более сложный способ определения конфигурации модели, охватывающий все возможности, которые могут делать заметки к данным в дополнение к более сложной конфигурации с заметками данных. Заметки к данным и API Fluent можно использовать вместе.
Чтобы получить доступ к API Fluent, переопределите метод OnModelCreating в DbContext. Предположим, что нужно переименовать столбец, в котором хранится User. DisplayName, в display_name.
Столбец DisplayName теперь переименован в display_name:
Сводка
в этом пошаговом руководстве мы рассматривали Code Firstную разработку с помощью новой базы данных. Мы определили модель, используя классы, которые затем использовали эту модель для создания базы данных, хранения и извлечения данных. после создания базы данных мы использовали Code First Migrations, чтобы изменить схему по мере развития нашей модели. мы также рассмотрели настройку модели с помощью заметок к данным и Fluent API.








