製品がある場合。
var p = new Product { Price = 30 };
そして、私は次のlinqクエリを持っています。
var q = repo.Products().Where(x=>x.Price == p.Price).ToList()
IQueryableプロバイダーで、定数式を含むp.PriceのMemberExpressionを取得しますが、値 "30"を取得できないようです。
更新これを試しましたが、うまくいかないようです。
var memberExpression = (MemberExpression)GetRootConstantExpression(m);
var fi = (PropertyInfo)memberExpression.Member;
var val = fi.GetValue(((ConstantExpression)memberExpression.Expression).Value, null);
乾杯。
ボディがメンバーアクセスであるラムダ式をコンパイルして呼び出すことができます。
private object GetValue(MemberExpression member)
{
var objectMember = Expression.Convert(member, typeof(object));
var getterLambda = Expression.Lambda<Func<object>>(objectMember);
var getter = getterLambda.Compile();
return getter();
}
ローカル評価は、式ツリーを解析する際の一般的な手法です。 LINQ to SQLは、非常に多くの場所でこの正確なことを行います。
MemberExpression right = (MemberExpression)((BinaryExpression)p.Body).Right;
Expression.Lambda(right).Compile().DynamicInvoke();
定数式は、コンパイラーによって生成されたキャプチャークラスを指します。決定点などは含めていませんが、そこから30を取得する方法は次のとおりです。
var p = new Product { Price = 30 };
Expression<Func<Product, bool>> predicate = x => x.Price == p.Price;
BinaryExpression eq = (BinaryExpression)predicate.Body;
MemberExpression productToPrice = (MemberExpression)eq.Right;
MemberExpression captureToProduct = (MemberExpression)productToPrice.Expression;
ConstantExpression captureConst = (ConstantExpression)captureToProduct.Expression;
object product = ((FieldInfo)captureToProduct.Member).GetValue(captureConst.Value);
object price = ((PropertyInfo)productToPrice.Member).GetValue(product, null);
price
は30
。 Price
がプロパティであると仮定していることに注意してくださいが、実際には、プロパティ/フィールドを処理するGetValue
メソッドを記述します。
q
はタイプList<Product>
。リストには価格プロパティはありません-個々の製品のみです。
最初または最後の製品には価格があります。
q.First().Price
q.Last().Price
コレクションに1つしかない場合は、Singleを使用してフラット化することもできます
q.Single().Price
次のものを使用できますか?
var price = p.Price;
var q = repo.Products().Where(x=>x.Price == price).ToList()
Expression.Lambda(myParameterlessExpression).Compile().Invoke()
を使用すると、いくつかの欠点があります。
.Compile()
はslowです。小さな式のフラグメントであっても、完了するまでに数ミリ秒かかる場合があります。 Invoke
- callはその後非常に高速で、単純な算術式またはメンバーアクセスに数ナノ秒しかかかりません。.Compile()
は、MSILコードを生成(発行)します。それは完璧に聞こえるかもしれません(そして優れた実行速度を説明します)が、問題は次のとおりです:そのコードはメモリを占有します。これはアプリケーションが終了する前に解放できません 、GCがデリゲート参照を収集した場合でも!Compile()
を完全に回避してこれらの問題を回避するか、コンパイルされたデリゲートをキャッシュして再利用できます。 これ 私の小さなライブラリはExpressions
の解釈とキャッシュされたコンパイルの両方を提供します、式のすべての定数とクロージャーが追加のパラメーターに自動的に置き換えられます。これらのパラメーターはクロージャーに再挿入され、ユーザーに返されます。両方のプロセスは十分にテストされ、本番環境で使用されており、長所と短所がありますが、Compile()
よりも100倍以上高速であり、メモリリークを回避します。
そして、あなたは正確に何を達成しようとしていますか?
Price
の値にアクセスするには、次のようなことをする必要があります。
var valueOfPrice = q[0].Price;
クラスがある場合:
public class Item
{
public int Id { get; set; }
}
およびオブジェクトのインスタンス:
var myItem = new Item { Id = 7 };
次のコードを使用して、式を使用してIdの値を取得できます。
Expression<Func<Item, int>> exp = x => x.Id;
var me = exp.Body as MemberExpression;
var propInfo = me.Member as PropertyInfo;
var value = propInfo.GetValue(myItem, null);
値には「7」が含まれます