つまり 残念なプログラマーは
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
これらは、この拡張パッケージをインストールする価値があるため、2つの主要な機能です。
しかし、砂糖もあります。 パッケージの作成者は、サンプリングのリクエストを蓄積してから、それらを1つのアプローチで実行するように提案しています。 これを行うには、具体化する前にデータを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の子供時代の病気を排除するだけでなく、ストアドプロシージャの下位レベルに行かずに高負荷の場所でそれを加速させます。