Доступ к значению выражения члена
Если у меня есть продукт.
var p = new Product { Price = 30 };
и у меня есть следующий запрос LINQ.
var q = repo.Products().Where(x=>x.Price == p.Price).ToList()
в IQueryable провайдере я получаю MemberExpression обратно для p. Price, который содержит постоянное выражение, однако я не могу получить значение "30" обратно от него.
обновление
Я пробовал это, но это, кажется, не работает.
var memberExpression = (MemberExpression)GetRootConstantExpression(m);
var fi = (PropertyInfo)memberExpression.Member;
var val = fi.GetValue(((ConstantExpression)memberExpression.Expression).Value, null);
ваше здоровье.
8 ответов:
вы можете скомпилировать и вызвать лямбда-выражение, тело которого является членом access:
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если вы знаете, что в коллекции есть только один, вы также можете сгладить его с помощью Single
q.Single().Price
вы можете использовать следующие:
var price = p.Price; var q = repo.Products().Where(x=>x.Price == price).ToList()
используя
Expression.Lambda(myParameterlessExpression).Compile().Invoke()имеет несколько недостатков:
.Compile()и медленно. Это может занять несколько миллисекунд, чтобы завершить даже для небольших фрагментов выражение. ЭлементInvoke- вызов супер-быстрый после этого, хотя, занимает всего несколько наносекунд для простых арифметических выражений или доступа к элементу..Compile()будет генерировать (испускать) код MSIL. Это может показаться идеальным (и объясняет отличную скорость выполнения), но проблема в том, что код занимает память,который не может быть освобожден до завершения приложения, даже когда GC собрал делегат-ссылку!можно либо избежать
Compile()в целом, чтобы избежать этих проблем или кэшировать скомпилированный делегатов для повторного их использования. этой моя маленькая библиотека предлагает и то и другое толкование наExpressionsа также кэшированные компиляции, где все константы и замыкания выражения заменяются на дополнительные параметры автоматически, которые затем повторно вставляются в закрытие, которое возвращается пользователю. Оба процесса хорошо протестированы, используются в производстве, оба имеют свои плюсы и минусы друг против друга, но значительно более 100 раз быстрее, чемCompile()- и избежать утечки памяти!
и что именно вы пытаетесь достичь?
потому что для доступа к значению
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"
Comments