Доступ к значению выражения члена
Если у меня есть продукт.
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);
Приветствия.
Ответы
Ответ 1
Вы можете скомпилировать и вызвать выражение лямбда, тело которого является доступом к членству:
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 делает эту точную вещь в нескольких местах.
Ответ 2
MemberExpression right = (MemberExpression)((BinaryExpression)p.Body).Right;
Expression.Lambda(right).Compile().DynamicInvoke();
Ответ 3
Постоянное выражение будет указывать на класс захвата, сгенерированный компилятором. Я не включил точки принятия решений и т.д., Но вот как получить 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
, который обрабатывает свойство/field.
Ответ 4
q
имеет тип List<Product>
. В списке нет свойства Цена - только отдельные Продукты.
Первый или последний Продукт будет иметь цену.
q.First().Price
q.Last().Price
Если вы знаете, что в коллекции есть только один, вы можете сгладить его, используя Single
q.Single().Price
Ответ 5
Можете ли вы использовать следующее:
var price = p.Price;
var q = repo.Products().Where(x=>x.Price == price).ToList()
Ответ 6
Использование Expression.Lambda(myParameterlessExpression).Compile().Invoke()
имеет несколько недостатков:
-
.Compile()
работает медленно. Это может занять несколько миллисекунд, даже для небольших фрагментов выражений. Тем не менее, Invoke
-call является супер-быстрым, хотя для простых арифметических выражений или доступа членов требуется всего несколько наносекунд.
-
.Compile()
будет генерировать (испускать) код MSIL. Это может показаться отличным (и объясняет отличную скорость выполнения), но проблема в том, что этот код занимает память , которая не может быть освобождена до того, как приложение закончит, даже если GC собрал делегат-ссылку!
Можно вообще избегать Compile()
, чтобы избежать этих проблем или кэшировать скомпилированные делегаты для повторного использования. Эта небольшая небольшая библиотека предлагает как интерпретацию Expressions
, так и кэшированную компиляцию, где все константы и замыкания выражения заменяются дополнительными параметры, которые затем повторно вставлены в закрытие, которое возвращается пользователю. Оба процесса хорошо протестированы, используются в производстве, оба имеют свои плюсы и минусы друг против друга, но намного более 100 раз быстрее, чем Compile()
- и избегают утечки памяти!
Ответ 7
И что именно вы пытаетесь достичь?
Поскольку для доступа к значению Price
вам нужно сделать что-то вроде:
var valueOfPrice = q[0].Price;
Ответ 8
Если у вас есть класс:
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"