式ツリーを取り、それを文字列表現などの他の形式に変換することはかなり一般的です(たとえばこの質問そしてこの質問そして、私はLinq2Sqlが同様のことをしていると思う。
多くの場合、おそらくほとんどの場合でも、式ツリーの変換は常に同じになります。つまり、関数がある場合
public string GenerateSomeSql(Expression<Func<TResult, TProperty>> expression)
同じ引数を指定した呼び出しは、常に同じ結果を返します。次に例を示します。
GenerateSomeSql(x => x.Age) //suppose this will always return "select Age from Person"
GenerateSomeSql(x => x.Ssn) //suppose this will always return "select Ssn from Person"
したがって、本質的には、実行時に時間を無駄に再計算し続けることを除けば、特定の引数を使用した関数呼び出しは実際には単なる定数です。
議論のために、変換が目立ったパフォーマンスヒットを引き起こすほど十分に複雑であったと仮定すると、関数呼び出しを実際の定数にプリコンパイルする方法はありますか?
編集するC#自体の中でこれを正確に行う方法はないようです。あなたがおそらくc#の中に入ることができる最も近いのは、受け入れられた答えです(もちろん、あなたはキャッシング自体が再生するより遅くないことを確かめたいと思うでしょう)。実際に真の定数に変換するには、いくらかの作業で、コンパイル後のバイトコードを変更するためにmono-cecilのようなものを使用することができると思います。
優秀なLINQ IQueryableツールキットプロジェクトはあなたが説明したことと同じようなことをするクエリーキャッシュを持っています。それは含まれていますExpressionComparer
2つの式の階層をたどり、それらが等価かどうかを判定するクラス。この手法は、パラメータ化や冗長結合の削除のために共通プロパティへの参照を収集するためにも使用されます。
あなたがする必要があるのはあなたが将来の再利用に備えて辞書にあなたの処理された表現の結果を保存することができるように表現ハッシュ戦略を思いつくことです。
あなたの方法は、このようになります。
private readonly IDictionary<Expression, string> _cache
= new Dictionary<Expression, string>(new ExpressionEqualityComparer());
public string GenerateSomeSql(Expression<Func<TResult, TProperty>> expression)
{
string sql;
if (!_cache.TryGetValue(expression, out sql))
{
//process expression
_cache.Add(expression, sql);
}
return sql;
}
class ExpressionEqualityComparer : IEqualityComparer<Expression>
{
public bool Equals(Expression x, Expression y)
{
return ExpressionComparer.AreEqual(x, y);
}
public int GetHashCode(Expression obj)
{
return ExpressionHasher.GetHash(obj);
}
}
まず第一に、パフォーマンスヒットを引き起こす式をコンパイルすることについてのあなたの仮定は実際には実際にはうまくいかないと思います。私の経験によると、通常の「良い」コードで問題が発生する前に、パフォーマンスのボトルネックを引き起こす要因は他にもたくさんあります(データベースアクセス、ネットワーク遅延、非常に貧弱なアルゴリズム)。時期尚早の最適化はすべての悪の根源です。そのため、アプリケーションを構築してストレステストを実行し、実際のパフォーマンスのボトルネックを見つけることができます。
そうは言っても、私は事前コンパイルはExpressionが何に変換されているかにかかっていると思います。 LINQ to SQLを使えばあなたが呼び出すことができることを私は知っていますDataContext.GetCommand(式)そして取得するDBCommand
これをキャッシュして再利用できます。