Классы EntityReference и EntityCollection

29

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

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);
Использование метода Load класса EntityReference

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 установлено отношение внешнего ключа:

Использование метода Add для связывания объектов

Обратите внимание, что метод 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. Если скомпилировать и запустить код, получится следующий результат:

Использование метода 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 выводится на консоль, чтобы можно было видеть значение поля внешнего ключа. Ниже показан результат запуска кода:

Использование метода Remove для разрыва отношения внешнего ключа

Как видите, значение 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) ? "да" : "нет");

Компиляция и запуск этого кода даст следующий результат:

Использование метода Contains

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:

Подсчет количества сущностных объектов с помощью свойства Count
Пройди тесты
Лучший чат для C# программистов