Обзор LINQ to Entities

95

»» В ДАННОЙ СТАТЬЕ ИСПОЛЬЗУЕТСЯ ИСХОДНЫЙ КОД ДЛЯ ПРИМЕРОВ

LINQ to SQL — это система объектно-реляционного отображения начального уровня. LINQ to Entities — это часть платформы ADO.NET Entity Framework, предоставляющая более высокую гибкость и больше средств, чем LINQ to SQL, но следующая за LINQ to SQL в отношении адаптации, из-за повышенной сложности и ранних выпусков, которым пока недостает ключевых средств.

API-интерфейс Entity Framework спроектирован для работы с любыми базами данных, поддерживающими ADO (а не только с SQL Server), и даже включает собственный диалект независимого от поставщика языка SQL, который можно применять в качестве альтернативы LINQ. Фактически Entity Framework обладает настолько широким набором средств, что для их описания понадобилась бы отдельная книга. Здесь будет показано, как запустить и использовать только важнейшие части Entity Framework, относящиеся к LINQ to Entities.

Имеется некоторая путаница с терминами. API-интерфейсы LINQ to SQL и Entity Framework делают нечто схожее, а потому не удивительно, что в них используется общая терминология.

Подобно LINQ to SQL, LINQ to Entities позволяет работать с объектами, которые представляют информацию из базы данных, выполнять LINQ-запросы, изменять значения, добавлять и удалять объекты. И так же, как LINQ to SQL, первый шаг в направлении использования этих средств предусматривает генерацию классов, отображающих содержимое базы данных на объекты — то, что делается при создании сущностной модели данных (entity data model — EDM). Модель EDM состоит из набора объектов и свойств, которые используются для взаимодействия с данными.

Например, в большинстве последующих примеров создается экземпляр класса NorthwindEntities. Этот класс унаследован от класса System.Data.Objects.ObjectContext, который подробно рассматривается позже. Это точка входа в EDM, похожая на класс DataContext для LINQ to SQL. Класс NorthwindEntities устанавливает соединение с базой данных при создании его нового экземпляра и берет на себя ответственность за сохранение изменений при вызове метода SaveChanges.

Давайте рассмотрим простейший пример:

 // Создать ObjectContext
            NorthwindEntities context = new NorthwindEntities();

            // Извлечь заказчика LAZYK
            Customer cust = (from c in context.Customers
                             where c.CustomerID == "LAZYK"
                             select c).Single<Customer>();

            // Изменить контактное имя
            cust.ContactName = "Alex Erohin";

            // Сохранить изменения
            try
            {
                context.SaveChanges();
            }
            catch (OptimisticConcurrencyException)
            {
                context.Refresh(refreshMode: RefreshMode.ClientWins, 
                     collection: context.Customers);
                context.SaveChanges();
            }

            Console.WriteLine("\n  Имя заказчика изменено на: " +  cust.ContactName);

Здесь из базы данных извлекается одиночный заказчик, который помещается в объект Customer. Объект Customer — это экземпляр класса Customer, являющегося частью сущностной модели данных. Позже будет показано, как генерировать EDM для базы данных Northwind.

После извлечения Customer обновляется одно из его свойств и вызывается метод SaveChanges для сохранения изменений в базе данных. Вызов метода SaveChanges помещен в блок try/catch, чтобы можно было разрешить любые потенциальные конфликты, связанные с параллелизмом.

Простой пример использования LINQ to Entities

Как это делалось с LINQ to SQL, начнем с обзора ключевых частей LINQ to Entities. Кое-что из того, что будет рассказано о LINQ to Entities, излагается в форме сравнения с LINQ to SQL.

В первом примере, использовался класс-наследник ObjectContext по имени NorthwindEntities, сущностный класс Customer, средства обнаружения и разрешения конфликтов, а также обновление базы данных через метод SaveChanges. Для начала рассмотрим некоторые основы этих компонентов, чтобы обеспечить базовое понимание основ LINQ to Entities и ADO.NET Entity Framework в целом.

ObjectContext

Класс ObjectContext — это ключ для доступа к сущностной модели данных и эквивалент класса DataContext из LINQ to SQL. Класс ObjectContext отвечает за создание и управление соединением с базой данных, отслеживает изменения и управляет постоянством. Подробности будут изложены позднее, а пока достаточно знать, что именно класс ObjectContext соединяет с базой данных, когда создается новый экземпляр NorthwindEntities, и этот же класс отслеживает изменения, которые вносятся в объект Customer, а также транслирует их в оператор SQL, сохраняющий изменения по вызову метода SaveChanges.

Обычно используется класс, унаследованный от ObjectContext, который создается при генерации EDM из базы данных. Далее будет показано, как это делается для базы данных Northwind. Имя класса выбирается на основе имени базы данных — в форме [База данных]Entities. В приведенном выше коде для базы данных Northwind предусмотрен класс NorthwindEntities.

Производный класс [База_данных]Entities для каждой таблицы базы, выбранной при создании EDM, будет иметь свойство ObjectSet<T>, представляющее таблицу, причем T здесь — это тип сущностного класса, созданного для представления записи в таблице. Например, класс NorthwindEntities, использованный в предыдущем примере, имеет общедоступное свойство Customers типа ObjectSet<Customer>. Оно применялось для выполнения запроса LINQ к множеству заказчиков.

Сущностные классы Entity

Сущностные классы в Entity Framework имеют много общего с классами LINQ to SQL. Это типы .NET, которые представляют собой отображение на реляционную структуру базы данных.

Платформа Entity Framework позволяет выполнять очень сложные отображения между сущностными классами и реляционными данными, которые могут охватывать разные базы данных и быть абстрагированы различными интересными способами. Здесь все эти тонкости не рассматриваются, потому что внимание сосредоточено на аспектах LINQ, но если нужны развитые средства ORM, то определенно следует обратиться к Entity Framework.

Сущностные классы в примерах обнаруживаются по наличию классов и объектов, имеющих имена таблиц базы данных в форме существительного единственного числа. Например, в приведенном примере используется класс по имени Customer. Поскольку Customer — форма единственного числа от Customers, и в базе данных Northwind есть таблица Customers, это намек на то, что Customer является сущностным классом, который представляет записи из таблицы Customer базы данных Northwind.

Мастер построения сущностной модели имеет опцию поддержки имен таблиц во множественном числе при создании сущностных классов, поэтому, когда он находит таблицу базы под названием Customers, то создает сущностный класс по имени Customer для представления элемента таблицы. Тот же подход обеспечивается опцией /pluralize утилиты SQLMetal и это обеспечивает значительное повышение читабельности кода.

Ассоциации Entity

Ассоциация — термин, применяемый для обозначения отношения первичного ключа к внешнему ключу между двумя сущностными классами. При отношении "один ко многим" результатом ассоциации является то, что родительский класс, включающий в себя первичный ключ, содержит коллекцию дочерних классов, имеющих внешний ключ.

Коллекция сохраняется в EntityCollection<T>, где T — тип дочернего сущностного класса. В отношении "многие ко многим" каждый сущностный класс поддерживает EntityCollection<T>, где T — тип противоположной сущности в отношении.

Коллекции сущностей доступны через общедоступные свойства по имени внешнего ключа. Поэтому, например, чтобы добраться к заказам, ассоциированным с заказчиком в базе данных Northwind, следует обратиться к свойству Customer.Orders, которое вернет EntityCollection<Order>.

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

Пройди тесты
Лучший чат для C# программистов