Доступ к значению выражения члена



Если у меня есть продукт.



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);


ваше здоровье.

542   8  

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

    Ничего не найдено.