データベース読み取りの「where」ステートメントとして渡す式を生成するコードがいくつかあり、少しスピードアップしようとしています。
次の例では、テーブルの PK と渡された値を照合する where ステートメントを作成します。
private Expression MakeWhereForPK(int id)
{
var paramExp = Expression.Parameter(typeof(Brand),"b");
//Expression to get value from the entity
var leftExp = Expression.Property(paramExp,"ID");
//Expression to state the value to match (from the passed in variable)
var rightExp = Expression.Constant(id,typeof(int));
//Expression to compare the two
var whereExp = Expression.Equal(leftExp,rightExp);
return Expression.Lambda<Func<Brand,bool>>(whereExp,paramExp);
}
上記は質問を簡略化したものです。実際には、テーブルを取得してクエリを実行し、その PK などを見つけるためのコードが含まれています。通常、コードで行うのと同じことを効果的に行っています。
ctx.Brands.Where(b => b.ID = id);
これは問題なく動作しますが、最適化のために少しテストを行っているときに、かなり遅いことがわかりました。上記の 1000000 回を実行するには、約 25 秒かかります。上記の最後の行を省略した方が良いでしょう (しかし、明らかにそれは役に立たない!)。そのため、約 2/3 の時間を取っているのは Expression.Lamba のように見えますが、残りもあまり良くありません。
すべてのクエリが一度に発生する場合は、それをIN
スタイル式に変換して一度生成できますが、残念ながらそれは不可能です。そのため、上記の生成のほとんどを節約し、生成されたものを再利用することを望んでいます式ですが、 の異なる値を渡しますid
。
これは Linq に渡されるため、呼び出し時に渡すことができる整数パラメーターを持つように式をコンパイルできないことに注意してください。これは式ツリーのままにしておく必要があります。
したがって、以下は、タイミングの演習を行うための単純なバージョンになる可能性があります。
Expression<Func<Brand,bool>> savedExp;
private Expression MakeWhereForPKWithCache(int id)
{
if (savedExp == null)
{
savedExp = MakeWhereForPK(id);
}
else
{
var body = (BinaryExpression)savedExp.Body;
var rightExp = (ConstantExpression)body.Right;
//At this point, value is readonly, so is there some otherway to "inject" id,
//and save on compilation?
rightExp.Value = id;
}
return savedExp;
}
id の値が異なるだけで、どのように式を再利用できますか?