Динамическая компиляция запросов LINQ для проверки значения словаря - PullRequest
4 голосов
/ 21 ноября 2011

Давайте предположим, что нам нужно запросить список объектов, и мы не знаем критериев, который является довольно динамичным, и у объекта есть и словари, и простые поля внутри. Пусть это будет следующий объект - Адрес (я оставил только один свойство для простоты).

public class Address
{
    #region Public members

    /// <summary>
    /// The extra datafield values
    /// </summary>
    public IDictionary<string, string> DataFieldValues { get; set; }

    public string City { get; set; }

    #endregion
}

Теперь, если мы запросим фиксированное поле с именем Город , когда я получу реализацию:

   private static Expression<Func<Address, bool>> BuildLambdaForAQueryItem(string caption, string value)
        {
            ParameterExpression param = Expression.Parameter(typeof(Address), caption);
            BinaryExpression body = Expression.Equal(Expression.PropertyOrField(param, caption),
                                                     Expression.Constant(value,
                                                                         typeof(Address).GetProperty(
                                                                             caption).PropertyType));
            return Expression.Lambda<Func<Address, bool>>(body, param);
        }

Теперь, если я хочу запросить на DataFieldValue выборку, мне тоже нужно написать лямбда-аналог:

x => x.DataFieldValues.ContatinsKey (ключ) && DataFieldValues ​​[ключ] == значение То, что я получаю с помощью метода, приведенного ниже, почти похоже , но все равно оно не применяет фильтр правильно:

private static Expression<Func<Address, bool>> BuildLambdaForAnExtraField(PostedQueryItem queryItem)
{
    ParameterExpression dataFields = Expression.Parameter(typeof(Address), "x");
    var dictionaryExpression = Expression.PropertyOrField(dataFields, "DataFieldValues");
    var keyExists = Expression.Call(dictionaryExpression, "ContainsKey", null, Expression.Constant(queryItem.Caption));

    Expression dictionaryAccessExpr = Expression.Property(dictionaryExpression, "Item",
                                                           Expression.Constant(queryItem.Caption));
    var valueCorresponds = Expression.Equal(dictionaryAccessExpr, Expression.Constant(queryItem.Value));

    return Expression.Lambda<Func<Address, bool>>(keyExists, dataFields).And(
      Expression.Lambda<Func<Address, bool>>(valueCorresponds, dataFields));
}

1 Ответ

2 голосов
/ 21 ноября 2011

Я думаю, что вы хотите использовать Expression.AndAlso (короткое замыкание И) в двух рассматриваемых выражениях предикатов для построения тела дерева выражений.

var body = Expression.AndAlso(keyExists, valueCorresponds);
return Expression.Lambda<Func<Address, bool>>(body, dataFields);

РЕДАКТИРОВАТЬ : (Если вы хотите придерживаться существующей техники)

Я предполагаю, что ваш метод And является методом расширения из библиотеки LINQKit ,Если это так, обратите внимание, что это расширение включает в себя «вызов» выражения правой части с параметрами первого выражения как часть создания результата.Если это для вас неприемлемо (возможно, ограничения провайдера LINQ?), Вы можете использовать полезное расширение Expand, которое также входит в эту библиотеку, чтобы «встроить» вызываемое выражение.

return Expression.Lambda<Func<Address, bool>>(keyExists, dataFields)
                 .And(Expression.Lambda<Func<Address, bool>>(valueCorresponds, dataFields))
                 .Expand();

Нов данном случае это массивный перебор;Мой совет - пойти с моим первым образцом.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...