Проектирование баз данных и работа с ними Веб-приложений. LINQ, ADO.NET Entities, DDD
10.1.3.9. Использование LINQ to Entities
С помощью служб Object Services можно выполнять динамические запросы, написанные на Entity SQL, для взаимодействия с сущностями EDM. Но в Entity Framework также можно работать со строго типизированными классами EDM, используя LINQ to Entities. Например, в только что продемонстрированном примере запрос на Entity SQL через Object Services может быть изменен для выполнения на LINQ to Entities, вот так:
string city = "Москва"; var query = from c in northwindContext.Customers where c.City == city select c; foreach (Customers c in query) Console.WriteLine(c.CompanyName);
В этом примере кода основанный на тексте синтаксис Entity SQL заменен на строго типизированный синтаксис LINQ, поддерживаемый в C# 3.0.
Теперь вспомним, как был создан в EDM тип EntityType, называющийся DiscontinuedProducts, произведенный от Products. Возможности EDM по наследованию могут быть использованы совместно с LINQ и Object Services для получения списка продуктов, которые больше не поддерживаются. Обратите внимание, что в следующем примере не указывается, что значение поля Discontinued должно быть равно 1. Вместо этого, тип для сущности продукта сверяется с производным EntityType DiscontinuedProducts, что в свою очередь задействует условие, которое было создано в сопоставлении (файле MSL), чтобы создать подходящее выражение SQL для получения только неподдерживаемых продуктов:
var query = from p in northwindContext.Products where p is DiscontinuedProducts select p; foreach (Products p in query) Console.WriteLine(p.ProductName);
Также можно создавать запросы, использующие преимущества встроенных отношений EDM (определяемых набором AssociationSet ). Например, список заказов для заказчиков из Москвы может быть получен посредством следующего выражения запроса LINQ:
var query = from o in northwindContext.Orders where o.Customers.City == "Москва" select o; foreach (Orders o in query) Console.WriteLine(o.OrderID);
Этот пример начинается с сущности Orders и использует ее свойство перемещения Customers для просмотра свойства City. Заказы от заказчиков не из Москвы полностью отфильтровываются. Поскольку возвращается список сущностей Orders, сущности могут быть изменены, и изменения могут быть сохранены в базе данных. Сохранение изменений в базе данных может быть выполнено при помощи метода SaveChanges.
Следующий пример получает список заказчиков из Москвы и устанавливает значение свойства Country для каждого из них в "Россия". Изменения сохраняются в StateManager, но не заносятся в базу данных, пока не вызывается метод SaveChanges:
var query = from c in northwindContext.Customers where c.City == "Москва" select c; foreach (Customers c in query) c.Country = "Россия"; northwindContext.SaveChanges();
Также можно создать новый экземпляр сущности и добавить его в EDM, используя метод AddObject объекта ObjectContext. В следующем примере показано, как добавить новую категорию в таблицу базы данных Categories:
Categories newCat = new Categories(); newCat.CategoryName = "Другое"; northwindContext.AddObject(newCat); northwindContext.SaveChanges(); int newCatID = newCat.CategoryID;
Сначала создается экземпляр сущности Categories и устанавливается значение его свойства CategoryName. Затем новая категория добавляется в EDM вызовом метода AddObject. Когда вызывается метод SaveChanges, создается оператор SQL, сохраняющий новую Category в базе данных и возвращает идентификатор CategoryID для новой строки.
Когда между сущностями есть связи, вам может потребоваться ассоциировать новую сущность с уже существующей. Например, можно создать новую сущность Orders и связать ее свойство Customers с существующей сущностью Customers. Хотя в таблице Orders в базе данных есть поле CustomerID, EDM представляет эту связь в объектно-ориентированном стиле, используя свойство Customers для обращения к существующей сущности Customers:
Orders newOrder = new Orders(); newOrder.OrderDate = DateTime.Today; Customers cust = northwindContext.Customers.Where( "it.CustomerID = 'ALFKI'").First(); newOrder.Customers = cust; northwindContext.AddObject(newOrder); northwindContext.SaveChanges();
10.1.4. Ключевые термины
Linq, Linq to SQL, ADO .NET Entity Framework.
10.2. Domain-Driven Design
10.2.1. Общие сведения
В заключение лекции рассмотрим практику Проблемно-ориентированного проектирования, которая получила в последнее время достаточно широкую известность и популяризируется, как отдельными специалистами в программной инженерии, так и крупными корпорациями, вроде Microsoft в том же ADO .NET Entity Framework.
Проблемно-ориентированное проектирование (Domain-Driven Design, DDD ) является подходом к разработке программного обеспечения для решения сложных задач, глубоко связывающем реализацию программной систем с развивающейся моделью бизнес-концепций [13].
DDD – это набор принципов и схем, помогающих разработчикам создавать изящные системы объектов. При правильном применении оно приводит к созданию программных абстракций, которые называются моделями предметных областей [14]. В эти модели входит сложная бизнес-логика, устраняющая промежуток между реальными условиями бизнеса и кодом.
Проблемно-ориентированное проектирование имеет следующие свойства [15]:
- фокусировка проекта на основной проблемной (предметной) области (домене) и бизнес-логике этой области;
- базирование всего проектирования на модели;
- введение творческого сотрудничества между техническими экспертами и экспертами в проблемной области, чтобы максимально понять концептуальную суть проблемы.
Проблемно-ориентированное проектирование не является отдельной технологией или методологией. DDD предоставляет набор методов и терминов для того, чтобы применять архитектурные решения, которые ускорят программные проекты, имеющие дело со сложными проблемными областями.
Термин DDD был придуман Эриком Эвансом в его книге "Domain-Driven Design: Tackling Complexity in the Heart of Software" [16].
Основными определениями DDD являются:
- Домен (проблемная область) – это сфера интересов, поле деятельности, область знаний. Предметная область, к которой пользователь применяет программу, является доменом программного обеспечения.
- Модель – это система абстракций, которая описывает выбранные аспекты домена и может использоваться для решения проблем, связанных с доменом.
- Общеупотребительный язык (Ubiquitous Language) – это язык, структурированный в модели домена и используемый всеми членами команды, разрабатывающими программное обеспечение.
- Контекст – это ситуация, в которой предложение или требование имеет строго определенное значение.
Главной целью проблемно-ориентированного проектирования является получение единственной, объединенной модели.
Предпосылками для успешного использования DDD для разработки программного обеспечения являются:
- Сложная предметная область;
- Опытная проектная команда, использующая ООП;
- Есть доступ к экспертам в данной проблемной области;
- Процесс разработки повторяем.
Рассмотрим основные аспекты Domain-Driven Design (рис. 10.16) [17]:
- Модель;
- Архитектура;
- Дизайн/проектирование.
Модель области должна обладать следующими свойствами:
- возможность реализации программного обеспечения по данной модели;
- модель должна быть достаточно проста для понимания;
- всеми членами команды должен использоваться общеупотребительный язык;
- всеми членами команды для определенных частей программной системы и проблемной области должен использоваться одинаковый контекст модели;
- модель должна постоянно подвергаться переработки с целью выявления базовых концепций и объединения фрагментированных "подмоделей" в единую модель.
В своей книге Эрик Эванс сосредотачивает внимание на описании уровня домена, который является одним из общих уровней в объектно-ориентированной системе с многослойной архитектурой, и описывает основные артефакты, которыми можно выразить, создать или восстановить модель предметной области [16]:
- Сущность (Entity) – это объект, который не определяется своими атрибутами (свойствами), а скорее является целостным и тождественным.
- Объект со значением (Value Object) – это объект, который имеет атрибуты, определяющие его свойства, но не имеет никакой концептуальной тождественности.
- Агрегат (Aggregate) – это коллекция объектов, которая связана корневым (составным) объектом. Составной объект гарантирует совместимость изменений, сделанных в пределах агрегата.
- Службы (Services) – если операция концептуально не принадлежит никакому объекту, ее можно реализовать с помощью служб. Концепцию служб называют шаблон проектирования "Искусственный" (Pure Fabrication) в GRASP.
- Репозитории (Repositories) – для того чтобы восстанавливать объекты домена, методы должны делегироваться специализированному объекту Репозиторий таким образом, чтобы можно было легко обмениваться альтернативными реализациями хранения.
- Фабрики (Factories) – для того, чтобы создавать объекты домена, методы должны делегироваться специализированному объекту Фабрика таким образом, чтобы можно было легко обмениваться альтернативными реализациями.
DDD не привязан к конкретной технологии, однако соблюдать DDD будет не так просто, без наличия хороших средств и практик в арсенале, таких как: TDD-фреймворк, ORM, возможность реализации Persistence Ignorance, IoC-контейнер (Inversion of Control) и возможностей AOP (Аспектно-Ориентированного Программирования). Практичная ценность этих средств заключается в том, что они позволять изолировать модель предметной области, что является ключевой целью DDD.
В промышленных приложениях Domain-Driven Design использует ряд шаблонов, часть которых описана в книге Эрика Эвенса, но, это не отменяет применение объектно-ориентированного подхода, включающего GoF-шаблоны, шаблоны Мартина Фаулера, описанные в его PoEAA, шаблоны GRASP и т.д.
Проблемно-ориентированное проектирование ни в коем случае не отрицает применения практик разработки, таких как:
- Объектно-ориентированное проектирование;
- Шаблоны проектирования (Design Patterns);
- Принципы проектирования S.O.L.I.D.;
- Разработка TDD.
DDD лишь дополняет их. Поиск подходящей модели и абстракций в сложных сценариях требует значительных знаний в сфере объектно-ориентированного подхода, и достаточного опыта применения различных принципов, шаблонов и практик, а не просто DDD, как может показаться.
C DDD так же тесно связана такая тема, как DDDD: Distributed Domain Driven Design (Распределенный DDD ) [18]. DDDD – это Domain-Driven Design в распределенных сценариях. В настоящее время существует не так много ресурсов, посвященных DDDD. Если коротко, то DDDD покрывает проблему реализации сообщений и DDD, разделение команд и запросов (Command Query Separation (CQS)), помогает реализовать данный подход.
Найти хорошие примеры реализации систем с применением Проблемно-ориентированного проектирования очень сложно, потому что DDD реализуется в приложениях, используемых в довольно сложных областях, и, как правило, являющихся коммерческими проектами. Однако можно найти несколько неплохих проектов, в которых можно проследить некоторые идеи реализации шаблонов [18]:
- Проект Тима Маккарти, описанный в деталях в его книге [19]. Он описывает не только применение шаблонов, но так же акцентирует внимание в разработке модели предметной области с точки зрения . Проект так же интересен тем, что построен на .NET 3.5 и демонстрирует силу современного подхода связывания данных с моделью предметной области (data binding, реализация шаблона MVVM). Так же его стиль примечателен умением выделять абстракции и повторно используемый код.
- Billy McCafferty разрабатывает open source фреймворк, сфокусированный на , под названием S#arp Architecture [20]. У него есть очень хорошее описание, включающее в себя шаблоны и подходы, используемые в фреймворке. Фреймворк нацелен на разработку ASP.NET MVC приложений с применением NHibernate.
- C# Domain-Driven Design sample application (ndddsample), это приложение, разрабатываемое Джимми Нильссоном, демонстрирует разбиение приложения на ключевые слои с точки зрения [21]. Так же демонстрируется практическое применение шаблонов building block в предметной области перевозки грузов, описанной в его книге [22]. Этот проект основан на совместной работе компании Эрика Эвенса "Domain Language" и шведской консалтинговой компании "Citerus".
10.2.2. Ключевые термины
10.3. Краткие итоги
Language Integrated Query ( LINQ ) – проект Microsoft по добавлению синтаксиса языка запросов, напоминающего SQL, в языки программирования платформы .NET Framework.
LINQ представляет собой набор расширений языка, поддерживающий формирование запросов данных способом, безопасным по типам. Запрашиваемые данные могут быть
Все операции запроса LINQ состоят из трех различных действий.
- получение источника данных;
- создание запроса;
- выполнение запроса.
Запросы LINQ основаны на универсальных типах, которые впервые были представлены в .NET Framework версии 2.0.
Основные операции запроса в LINQ:
- получение источника данных;
- фильтрация;
- упорядочение;
- группировка;
- соединение;
- выбор (Проецирование).
LINQ используется не только для извлечения данных. Это также мощное средство для преобразования данных:
- соединение нескольких входных последовательностей в одну выходную;
- выбор подмножества каждого исходного элемента;
- преобразование находящихся в памяти объектов в XML;
- выполнение операций над исходными элементами.
Операции запросов LINQ строго типизированы в источнике данных, в самом запросе и при выполнении запроса.
В LINQ можно использовать синтаксис запроса или синтаксис метода:
- методы расширения стандартных операторов запросов;
- лямбда-выражения;
- возможность компоновки запросов.
LINQ to SQL – простая, но достаточно мощная система объектно-реляционного отображения (ORM).
LINQ to SQL поддерживает все основные возможности, необходимые для разработчиков на SQL. Можно запрашивать данные, вставлять, обновлять и удалять сведения из таблиц.
Можно выделить три способа создания объектной модели для базы данных:
- объектно-реляционный конструктор;
- средство создания кода SQLMetal;
- редактор кода.
ADO.NET Entity Framework (EF) – объектно-ориентированная технология доступа к данным, является object-relational mapping (ORM) решением для .NET Framework от Microsoft.
Платформа Entity Framework содержит следующие компоненты:
- Модель Entity Data Model (EDM).
- Компонент Object Services.
- Компонент LINQ to Entities
- Язык Entity SQL
- Поставщик EntityClient
- Компонент метаданных ADO.NET
- Набор средств, которые создают сопоставления и разделяемые классы, представляющие сущности концептуальной модели.
- Поставщик данных SqlClient
Проблемно-ориентированное проектирование (Domain-Driven Design, ) является подходом к разработке программного обеспечения для решения сложных задач, глубоко связывающем реализацию программной систем с развивающейся моделью бизнес-концепций.
Основными определениями являются:
- Домен.
- Модель.
- Общеупотребительный язык.
- Контекст.