読者です 読者をやめる 読者になる 読者になる

ゆうなんとかさんの雑記帳的な。

Twitterで踊ったり音ゲーしたりしてるあの名前がよくわからない人が書いてるらしいよ。

LINQ to Entitiesではまったところとか

EntityFramework C#

今の会社とは違うとある案件でEntityFrameworkを使っているのですが、なぜかやたら遅くて、原因がわからずじまいでした。
今日はその原因と、そのほかはまりどころがわかったのでメモです。

発行されているクエリを確認する

これができないことには原因の調べようがありません。
とりあえずデバッグログに放流するには

using(var context = new DbContext())
{
    context.Database.Log = Debug;
}

という感じでDebugクラスを渡しておくとできます。

クエリが実行されると

Opened connection at 2015/03/03 20:20:38 +09:00
SELECT 
....

-- p__linq__0: '*******' (Type = String, Size = 4000)
-- Executing at 2015/03/03 20:20:38 +09:00
-- Completed in 52 ms with result: SqlDataReader

Closed connection at 2015/03/03 20:20:38 +09:00

といった風に

  • 接続した時刻
  • 発行されたクエリ
  • 変数(あれば)
  • クエリを実行した時刻
  • クエリのコンパイルに要した時間
  • 接続を閉じた時刻

が出力されるようになります。

今回ミスってた原因

ちなみに今回の原因はプロパティのgetterにそれ書いていたことのようです。
外部キーとかなかったので、せめてそんな感じでアクセスできたらなーというほんの出来心でした。

これにより、プロパティの思った通りのクエリが構築されておらず、getterの部分に書いてあった最低限の制約条件でSQLのクエリが発行されてしまい、その結果大量のレコードがやりとりされ、それらがメモリによっこいしょと載せられて、ごりごりとリソースを消費しながら計算・集計されていた、というわけです。
その辺に気を付けながらクエリ式を修正しつつちゃんとSQLコンパイルされるようにすると、3分かかってた処理が3秒になりました。メモリも数GB使っていたものが数十MBまで減りました。

あれが使いたいこれが使いたい

IN

`.List.Contains(T)` などを使ったときにコンパイルされるようです。私はToArrayやToList噛ませて使ってます。

LIKE '%hoge'

`String.StartWith(string)` や `String.EndWith(string)`

が使えます。 `String.Contains(string)` の場合、最初と最後に「/」を書くとそれが「%」になるようです。

MiN、MAX、COUNT

LINQのMin, Max, Countでどうぞ。

Left関数

今日初めて知ったのですが、渡した文字列の左から、指定した文字数だけの文字列を返すという関数です。
これをEntitieFrameworkで使うには、 `System.Data.Entity.DbFunctions.Left(string, left?)` を使います。