Т.е. несчастный программист должен писать что-то вроде
using (var ctx = new ShopEntities()) { foreach (var u in ctx.Users) { ctx.Users.Remove(u); } ctx.SaveChanges(); }
Но с выходом пакета EntityFramework.Extended ситуация в корне меняется.
Итак, устанавливаем пакет из репозитория командой “Install-Package EntityFramework.Extended”. Далее подключаем пространство имен “EntityFramework.Extensions”.
И начинается магия.
Теперь удаление выглядит вот так.
using (var ctx = new ShopEntities()) { var itemsDeleted = ctx.Users.Delete(u => u.Orders.Count > 10); //Осторожно, запрос уже улетел в БД //и, несмотря на отсутствие вызова ctx.SaveChanges(), данные были удалены Console.WriteLine("{0} users were deleted", itemsDeleted); }
Кстати, не лишним будет посмотреть, что полетело на сервер.
Это был вот такой запрос
DELETE [dbo].[Users] FROM [dbo].[Users] AS j0 INNER JOIN ( SELECT [Project1].[ID] AS [ID] FROM ( SELECT [Extent1].[ID] AS [ID], (SELECT COUNT(1) AS [A1] FROM [dbo].[Orders] AS [Extent2] WHERE [Extent1].[ID] = [Extent2].[UserID]) AS [C1] FROM [dbo].[Users] AS [Extent1] ) AS [Project1] WHERE [Project1].[C1] > 10 ) AS j1 ON (j0.[ID] = j1.[ID]) go
Как мы видим, это честный (хотя и корявый) запрос на групповое удаление с условием.
Аналогично с обновлением записей. Больше не нужно читать данные из БД перед обновлением. При этом мы можем использовать существующие данные в записях, а не ограничены лишь константами.
using (var ctx = new ShopEntities()) { var itemsUpdated = ctx.Users.Where(u => u.Orders.Count > 0).Update(u => new User { BonusCount = u.BonusCount + 1 }); //Осторожно, запрос уже улетел в БД //и, несмотря на отсутствие вызова ctx.SaveChanges(), данные были обновлены Console.WriteLine("{0} users were updated", itemsUpdated); }
Смотрим SQL запрос в профайлере.
UPDATE [dbo].[Users] SET [BonusCount] = [BonusCount] + 1 FROM [dbo].[Users] AS j0 INNER JOIN ( SELECT [Project1].[ID] AS [ID] FROM ( SELECT [Extent1].[ID] AS [ID], (SELECT COUNT(1) AS [A1] FROM [dbo].[Orders] AS [Extent2] WHERE [Extent1].[ID] = [Extent2].[UserID]) AS [C1] FROM [dbo].[Users] AS [Extent1] ) AS [Project1] WHERE [Project1].[C1] > 0 ) AS j1 ON (j0.[ID] = j1.[ID]) go
Это две основные функции из-за которых стоит установить данный пакет расширений.
Но есть и ещё сахар. Создатель пакета предлагает нам копить запросы на выборку, чтобы потом выполнять их за один подход. Для этого мы до материализации маркируем данные как Future() и потом, при материализации любого из объектов, остальные будут материализованы автоматически.
using (var ctx = new ShopEntities()) { var alexUsers = ctx.Users.Where(u => u.Name == "Alex").Future(); var usersWithOrders = ctx.Users.Where(c => c.Orders.Any()).Future(); foreach (var item in alexUsers) //материализация всех данных была выполнена в этот момент за один round-trip к серверу. { Console.WriteLine("{0} {1}", item.ID, item.Name); } foreach (var item in usersWithOrders) //здесь нет выполнения SQL { Console.WriteLine("{0} {1}", item.ID, item.Name); } }
А вот такой был SQL запрос
-- Query #1 SELECT [Extent1].[ID] AS [ID], [Extent1].[Name] AS [Name], [Extent1].[IsTop10] AS [IsTop10], [Extent1].[BonusCount] AS [BonusCount] FROM [dbo].[Users] AS [Extent1] WHERE (N'Alex' = [Extent1].[Name]) AND ([Extent1].[Name] IS NOT NULL); -- Query #2 SELECT [Extent1].[ID] AS [ID], [Extent1].[Name] AS [Name], [Extent1].[IsTop10] AS [IsTop10], [Extent1].[BonusCount] AS [BonusCount] FROM [dbo].[Users] AS [Extent1] WHERE EXISTS (SELECT 1 AS [C1] FROM [dbo].[Orders] AS [Extent2] WHERE [Extent1].[ID] = [Extent2].[UserID] ); go
Кроме расширения Future, так же доступны FutureCount, FutureFirstOrDefault, FutureValue.
Но и это ещё не всё. Представьте, что у вас есть модуль, который обрабатывает частые запросы к редко изменяемым данным. Например, авторизация пользователя. Хочется закешировать результаты? Пожалуйста. Как видно из кода, кэш не ограничивается контекстом, а остаётся актуальным даже после его повторного создания.
for (int i = 0; i < 2; i++) { using (var ctx = new ShopEntities()) { var alexUsers = ctx.Users.Where(u => u.Name == "Alex").FromCache(); foreach (var item in alexUsers) //i == 0 запрос в БД ушёл, i == 1 запроса в БД не было { Console.WriteLine("{0} {1}", item.ID, item.Name); } } }
У метода FromCache есть перегрузка для точного указания времени кеширования.
Таким образом установка и использование EntityFramework.Extended позволяет не только устранить детские болезни EntityFramework, но и ускорить его в местах высокой нагрузки не переходя на нижний уровень хранимых процедур.
ссылка на оригинал статьи http://habrahabr.ru/post/203214/
Добавить комментарий