久しぶりにブログを書きます。
今、仕事でASP.NETを使ったWebアプリケーションの開発に取り組んでいます。
私自身、業務でWebアプリケーションを作るのは初めてなので、中々苦戦しています。
(昔のASPなら大学時代に作ってたけど・・・)
ASP.NETにはMVCの考え方が採用されています。
資料が無い状態では中々思うように開発が進まないので、『ASP.NET MVC 5 実践プログラミング』という書籍を参考にしています。
ASP.NETからデータベースを扱う場合、Entity Frameworkという技術を用いて、コードファーストな開発を行います。
Entity Frameworkやコードファーストについては以下を参考にしてみて下さい。
またタイトルにある LINQ to Entitiesとは、LINQを使ってEntity Framework経由でデータベースを操作する技術を言います。
LINQについての説明は省略しますが、C#プログラミングではLINQは必ずと言っていいほど利用します。
LINQ to Entitiesの処理に時間がかかっていると感じていた
サンプルを使って説明します。
//Bookクラス public class Book { public int Id { get; set; } public string Title { get; set; } public int Price { get; set; } } //DBコンテキストクラス public class TsubalogWebContext : DbContext { public DbSet<Book> Books { get; set; } }
上記のクラスの大量データに対して検索SQLを発行します。
なお先に紹介した書籍にあるように、リポジトリパターンを使ってみます。
例として、1000円以下の書籍を抽出してみます。
// リポジトリクラス public class TsubalogWebRepository : ITsubalogWebRepository { private TsubalogWebContext db = new TsubalogWebContext(); public IEnumerable<Book> GetAll() { return from b in db.Books select b; } public IEnumerable<Book> Filter() { return GetAll().Where(b => b.Price < 1000); } }
Filter()
メソッドを実行した時に、想像以上に処理時間がかかってるわけですよ。
そこでLINQ to Entitiesで発行されるSQLを確認したところ、次のようなSQLになっていました。
SELECT [Extent1].[Id] AS [Id], [Extent1].[Title] AS [Title], [Extent1].[Price] AS [Price] FROM [dbo].[Books] AS [Extent1]
SQL文のWhere句がありません。
「ちゃんと条件を指定しているのになぜ??」状態で小一時間悩みました。
このコードがどのような状態かと言うと、
ということになります。
これでは全件データがメモリに展開されてしまい、パフォーマンスが悪いことも納得です。
LINQ to Entitiesで勘違いしていたことは何か
先の問題の解決のヒントとなったのは、CodeZineの記事でした。
つまり、LINQ to Entitiesを使ってクエリを発行したいデータはIQueryable
インターフェイスでなければならない、ということでした。
GetAll()
メソッドの戻り値の型を、IQueryable<Book>
にしなければならなかったようです。
// リポジトリクラス public class TsubalogWebRepository : ITsubalogWebRepository { private TsubalogWebContext db = new TsubalogWebContext(); public IQueryable<Book> GetAll() { return from b in db.Books select b; } public IEnumerable<Book> Filter() { return GetAll().Where(b => b.Price < 1000); } }
修正したLINQ to Entitiesで再度SQLを発行してみると、きちんと条件が含まれていることが分かります。
SELECT [Extent1].[Id] AS [Id], [Extent1].[Title] AS [Title], [Extent1].[Price] AS [Price] FROM [dbo].[Books] AS [Extent1] WHERE [Extent1].[Price] < 1000
これまでコレクションは極力IEnumerable
で扱っていた自分にとって、型をきちんと意識することが大事だと再認識した問題でした。