Классы EntityReference и EntityCollection
29LINQ --- LINQ to Entities --- Классы EntityReference и EntityCollection
»» В ДАННОЙ СТАТЬЕ ИСПОЛЬЗУЕТСЯ ИСХОДНЫЙ КОД ДЛЯ ПРИМЕРОВ
EntityReference
Класс EntityReference используется для манипуляций навигационными свойствами одиночного экземпляра между сущностными типами; подробности устройства навигационных свойств были описаны в предыдущей статье. Это не тот класс, с которым придется работать очень часто. Обычно проще использовать встречное свойство, создаваемое при генерации сущностного типа мастером Entity Data Model Wizard. Для полноты картины ниже приводятся описания ключевых членов.
Load
Метод Load используется при явной загрузке данных. В следующем коде демонстрируется явная загрузка сущностного объекта, ассоциированного с EntityReference. Обратите внимание, что для того, чтобы этот метод возымел действие, ленивая загрузка должна быть отключена:
NorthwindEntities context = new NorthwindEntities();
// Отключить ленивую загрузку
context.ContextOptions.LazyLoadingEnabled = false;
// Запросить заказ
Order ord = (from o in context.Orders
where o.CustomerID == "LAZYK"
select o).First();
// Получить ссылку на сущность
System.Data.Objects.DataClasses.EntityReference<Customer> customerRef
= ord.CustomerReference;
// Явно загрузить заказ
customerRef.Load();
Console.WriteLine("\n Имя заказчика: {0}", customerRef.Value.CompanyName);
Value
Свойство Value возвращает лежащий в основе сущностный тип, на который полагается EntityReference. В приведенном выше примере свойство Value вызывалось для получения сущностного объекта Customer, связанного с сущностным объектом Order, с которым выполнялась работа.
EntityCollection
Класс EntityCollection предназначен для хранения коллекций сущностных объектов, чаще всего на одном конце навигационного свойства. Например, в сущностном типе Customer базы данных Northwind свойство Orders — это EntityCollection<Order>, и оно используется для содержания объектов Order, относящихся к данному объекту Customer.
Наиболее распространенные способы использования EntityCollection заключаются в перечислении элементов в коллекции с помощью цикла foreach как основы запроса LINQ. Класс EntityCollection реализует интерфейсы, допускающие перечисление — IEnumerable<T> и IEnumerable, где T является сущностным типом, экземпляры которого нужно собирать.
Класс EntityCollection реализует некоторые другие полезные методы, описанные ниже:
Add()
Добавление сущностного типа в EntityCollection устанавливает с ним отношение внешнего ключа и делает его связанным объектом. Entity Framewrok самостоятельно установит значения полей внешнего ключа. Если это новый объект; то при вызове SaveChanges будет создана строка в базе данных. Если есть готовый сущностный объект; то при вызове SaveChanges отношение внешнего ключа будет обновлено.
Метод Add имеет один прототип, представленный ниже:
public void Add(Object entity);
Эффект от вызова метода Add немедленно отражается в кэшированных данных, поддерживаемых Entity Framework, но не отражается в базе данных до тех пор, пока не будет вызван метод SaveChanges в производном от ObjectContext классе.
В следующем примере создается новый сущностный тип Order и вызывается метод Add на Customer.Orders.EntityCollection для связывания Order с Customer:
NorthwindEntities context = new NorthwindEntities();
// Получить заказчика LAZYK
Customer cust = (from c in context.Customers
where c.CustomerID == "LAZYK"
select c).First();
Order ord = Order.CreateOrder(1234);
cust.Orders.Add(ord);
Console.WriteLine("\n CustomerID для заказа: {0}", ord.CustomerID);
После компиляции и запуска этого кода получится следующий результат, который демонстрирует, что между объектами Customer и Order установлено отношение внешнего ключа:
Обратите внимание, что метод SaveChanges не вызывался, а это значит, что модифицированы только кэшированные данные в Entity Framewrok, но никакие изменения в базу данных не вносились. Если вызвать Refresh для обновления кэшировании данных, то изменения будут утеряны.
С помощью метода Add можно изменить отношение между сущностными объектами. Ниже содержится пример:
NorthwindEntities context = new NorthwindEntities();
// Получить заказчика LAZYK
Customer cust1 = (from c in context.Customers
where c.CustomerID == "LAZYK"
select c).First();
// Получить заказчика AROUT
Customer cust2 = (from c in context.Customers
where c.CustomerID == "AROUT"
select c).First();
// Первый заказ для LAZYK
Order firstOrder = cust1.Orders.First();
Console.WriteLine("Первый заказ для LAZYK, идентификатор заказчика: {0}, идентификатор заказа: {1}",
firstOrder.CustomerID, firstOrder.OrderID);
// Добавить заказ LAZYK к набору заказов AROUT
cust2.Orders.Add(firstOrder);
Console.WriteLine("Первый заказ для AROUT, идентификатор заказчика: {0}, идентификатор заказа: {1}",
firstOrder.CustomerID, firstOrder.OrderID);
В этом примере запрашиваются два заказчика со значениями CustomerID, равными LAZYK и AROUT. Затем извлекается первый заказ для заказчика LAZYK и вызывается Add для добавления в EntityCollection объектов Order заказчика AROUT. На консодь выводятся значения полей CustomerID и OrderID объекта Order до и после вызова метода Add. Если скомпилировать и запустить код, получится следующий результат:
Как видите, Entity Framework интеллектуально обновляет отношение внешнего ключа, так что объект Order теперь связан с объектом Customer, имеющим идентификатор AROUT.
Remove()
Метод Remove() удаляет сущностный объект из коллекции и устанавливает поле внешнего ключа в null. Это значит, что объект не появится при перечислении EntityCollection и когда будет вызван метод SaveChanges. Строка в базе данных, соответствующая удаленному объекту, будет обновлена значением внешнего ключа NULL.
Метод Remove имеет один прототип, описанный ниже:
public bool Remove (Object entity);
Аргумент entity — это сущностный объект, который должен быть удален из коллекции. Метод Remove возвращает true, если сущностный объект был удален успешно, и false — в противном случае. Вызов метода Remove оказывает немедленный эффект на сущностные объекты, кэшированные Entity Framework, но не затрагивает базу данных, пока не будет вызван метод SaveChanges.
Если схема базы данных не допускает значений NULL в столбце внешнего ключа, то при использовании метода Remove следует соблюдать осторожность. В этом случае вызов метода SaveChanges приведет к генерации исключения.
Ниже демонстрируется применение метода Remove для удаления объекта Order из коллекции EntityCollection<Order> объекта Customer в сущностной модели базы данных Northwind:
NorthwindEntities context = new NorthwindEntities();
// Получить заказчика LAZYK
Customer cust = (from c in context.Customers
where c.CustomerID == "LAZYK"
select c).First();
// Получить первый заказ для LAZYK
Order order = cust.Orders.First();
Console.WriteLine("Заказ имеет следующий CustomerID - {0}", order.CustomerID);
// Удалить заказ из коллекции
Console.WriteLine("Удаление заказа с ID: {0}", order.OrderID);
cust.Orders.Remove(order);
Console.WriteLine("\nЗаказ имеет следующий CustomerID - {0}", (order.CustomerID == null ?
"NULL" : order.CustomerID));
В коде выполняется запрос LINQ to Entities для получения сущностного объекта Customer и извлечения первого экземпляра Order из коллекции, который передается в качестве аргумента методу Remove. Перед и после вызова метода Remove значение CustomerID объекта Order выводится на консоль, чтобы можно было видеть значение поля внешнего ключа. Ниже показан результат запуска кода:
Как видите, значение CustomerID объекта Order установлено в null. В рассмотренном примере вызывается метод SaveChanges для сохранения изменений, что приводит к обновлению строки таблицы, представляющей Order, значением поля CustomerID, равным NULL. Никакие исключения при сохранении данных не генерируются, потому что внешний ключ не навязывается схемой базы данных, но появляется "висячая" запись, которая более не ассоциирована с заказчиком.
Clear()
Метод Clear удаляет все сущностные объекты из EntityCollection и устанавливает их значения внешних ключей в null. Это эквивалентно удалению каждого объекта коллекции индивидуально с помощью метода Remove.
Метод Clear имеет один прототип:
public void Clear();
В следующем коде удаляются все объекты Order из EntityCollection<Order> для заданного объекта Customer из базы данных Northwind:
NorthwindEntities context = new NorthwindEntities();
// Получить заказчика LAZYK
Customer cust = (from c in context.Customers
where c.CustomerID == "LAZYK"
select c).First();
// Очистить коллекцию Orders
cust.Orders.Clear();
// Сохранить изменения
context.SaveChanges();
Как и с методами Add или Remove, изменения не сохраняются в базе данных, пока не будет вызван метод SaveChanges. В примере вызов метода SaveChanges обновит все строки таблицы Orders со значением CustomerID, равным LAZYK, установив в этом поле значение NULL.
Contains()
Метод Contains позволяет определить, содержит ли EntityCollection заданный сущностный объект.
Метод Contains имеет один прототип, в котором T — сущностный тип элементов EntityCollection<T>. Метод Contains возвращает true, если сущностный объект содержится в коллекции, и false — в противном случае.
public bool Contains(T entity);
В нижеследующем примере демонстрируется использование метода Contains. В коде запрашиваются объекты Customer базы данных Northwind, затем из коллекции EntityCollection<Order> извлекается первый объект Order. После этого вызывается метод Contains, объект Order удаляется из коллекции с помощью метода Remove и снова вызывается Contains, чтобы сравнить эффект:
NorthwindEntities context = new NorthwindEntities();
// Получить заказчика LAZYK
Customer cust = (from c in context.Customers
where c.CustomerID == "LAZYK"
select c).First();
// Получить первый заказ для LAZYK
Order order = cust.Orders.First();
// Воспользоваться методом Contains
Console.WriteLine("Содержат ли заказы определенный заказ: {0}",
cust.Orders.Contains(order) ? "да" : "нет");
// Удалить заказ из коллекции
Console.WriteLine("Удаление заказа");
cust.Orders.Remove(order);
Console.WriteLine("Содержат ли заказы определенный заказ: {0}",
cust.Orders.Contains(order) ? "да" : "нет");
Компиляция и запуск этого кода даст следующий результат:
Count
Свойство Count возвращает количество сущностных объектов в коллекции. Есть только один прототип свойства Count:
public int Count{get;};
В последнем примере этой статьи свойство Count применяется для определения количества объектов Orders, ассоциированных с определенным объектом Customer базы данных Northwind. Затем Order удаляется и Count вызывается опять:
NorthwindEntities context = new NorthwindEntities();
// Получить заказчика LAZYK
Customer cust = (from c in context.Customers
where c.CustomerID == "LAZYK"
select c).First();
// Подсчитать количество заказов
Console.WriteLine("Количество заказов: {0}", cust.Orders.Count);
// Получить и удалить первый заказ
Order ord = cust.Orders.First();
Console.WriteLine("Удаление заказа");
cust.Orders.Remove(ord);
// Снова подсчитать количество заказов
Console.WriteLine("Количество заказов: {0}", cust.Orders.Count);
Компиляция и запуск кода дает результат, показанный ниже. Как и следовало ожидать, удаление экземпляра из коллекции уменьшает значение, возвращаемое свойством Count: