Подтвердить что ты не робот

Добавить в выражение

Я следил за этой темой: текст ссылки

Джейсон приводит пример:

public static Expression<TDelegate> AndAlso<TDelegate>(this Expression<TDelegate> left, Expression<TDelegate> right)
{
    return Expression.Lambda<TDelegate>(Expression.AndAlso(left, right), left.Parameters);
}

и его использование как таковое:

Expression<Func<Client, bool>> clientWhere = c => true;
if (filterByClientFName)
{
    clientWhere = clientWhere.AndAlso(c => c.ClientFName == searchForClientFName);
}
if (filterByClientLName)
{
    clientWhere = clientWhere.AndAlso(c => c.ClientLName == searchForClientLName);
}

У меня есть таблица заказов, и я следовал приведенному выше примеру, меняя имена столбцов, и получаю аналогичную ошибку, которую создатель сообщения имел

Двоичный оператор AndAlso не определен для типов "System.Func 2[Models.Order,System.Boolean]' and 'System.Func 2 [Models.Order, System.Boolean]".

У кого-нибудь есть мысли о том, чего я не хватает?

ОБНОВЛЕНО:

Эрик, я далее следил за тем, что спрашивал пользователь предыдущего сообщения, здесь текст ссылки

У пользователя есть этот

Expression<Func<Client, bool>> clientWhere = c => true;
Expression<Func<Order, bool>> orderWhere = o => true;
Expression<Func<Product, bool>> productWhere = p => true;

if (filterByClient)
{
    clientWhere = c => c.ClientID == searchForClientID;
}

Теперь, если у него будут различные условия в filterByClient, скажем, у него либо есть clientid, и/или какое-то другое имя столбца, как бы построить выражение clientWhere?

4b9b3361

Ответ 1

Вы пытаетесь построить дерево выражений, которое представляет это:

c => true && c.ClientFName == searchForClientFName

Фактически вы создаете дерево выражений, которое представляет это:

c => c=> true && c => c.ClientFName == searchForClientFName

что не имеет никакого смысла.

Теперь вы можете наивно подумать, что это сработает:

public static Expression<TDelegate> AndAlso<TDelegate>(this Expression<TDelegate> left, Expression<TDelegate> right) 
{ 
// NOTICE: Combining BODIES:
    return Expression.Lambda<TDelegate>(Expression.AndAlso(left.Body, right.Body), left.Parameters); 
} 

Это создаст в вашем случае что-то, представляющее

c => true && c.ClientFName == searchForClientFName

Что выглядит правильно. Но на самом деле это хрупкое. Предположим, вы имели

... d => d.City == "London" ...
... c => c.ClientName == "Fred Smith" ...

и вы использовали этот метод для их объединения. Вы получите объект, представляющий

c => d.City == "London" && c.ClientName == "Fred Smith"

Что, черт возьми, делает там?

Кроме того, параметры сопоставляются идентификатором объекта, а не именем параметра. Если вы сделаете это

... c => c.City == "London" ...
... c => c.ClientName == "Fred Smith" ...

и объединить их в

c => c.City == "London" && c.ClientName == "Fred Smith"

ты в одной лодке; "c" в "c.City" отличается от c, чем два других.

То, что вам действительно нужно сделать, это сделать третий объект параметра, подставить его в телах lambdas для каждого появления их параметров, а затем создать новое дерево выражений лямбда из полученных замещенных тел.

Вы можете создать механизм подстановки, написав посетителя, который проходит над телом дерева выражений, переписывая его, когда он идет.

Ответ 2

Мне было трудно понять hvd answer, поэтому я создал некоторый код, чтобы объяснить его по-другому. hvd должен получить кредит за предложение ExpressionVisitor. Я просто не мог понять этот пример в контексте функций ввода Linq to X, которые я использовал.

Я надеюсь, что это поможет кому-то еще прийти к вопросу с этой точки зрения.

Кроме того, я создал комбинирующий код в качестве методов расширения, чтобы сделать его немного проще в использовании.


using System;
using System.Collections.Generic;
using System.Linq.Expressions;

namespace ConsoleApplication3
{

    class Program
    {

        static void Main(string[] args)
        {

            var combined = TryCombiningExpressions(c => c.FirstName == "Dog", c => c.LastName == "Boy");

            Console.WriteLine("Dog Boy should be true: {0}", combined(new FullName { FirstName = "Dog", LastName = "Boy" }));
            Console.WriteLine("Cat Boy should be false: {0}", combined(new FullName { FirstName = "Cat", LastName = "Boy" }));

            Console.ReadLine();
        }

        public class FullName
        {
            public string FirstName { get; set; }
            public string LastName { get; set; }
        }

        public static Func<FullName, bool> TryCombiningExpressions(Expression<Func<FullName, bool>> func1, Expression<Func<FullName, bool>> func2)
        {
            return func1.CombineWithAndAlso(func2).Compile();
        }
    }

    public static class CombineExpressions
    {
        public static Expression<Func<TInput, bool>> CombineWithAndAlso<TInput>(this Expression<Func<TInput, bool>> func1, Expression<Func<TInput, bool>> func2)
        {
            return Expression.Lambda<Func<TInput, bool>>(
                Expression.AndAlso(
                    func1.Body, new ExpressionParameterReplacer(func2.Parameters, func1.Parameters).Visit(func2.Body)),
                func1.Parameters);
        }

        public static Expression<Func<TInput, bool>> CombineWithOrElse<TInput>(this Expression<Func<TInput, bool>> func1, Expression<Func<TInput, bool>> func2)
        {
            return Expression.Lambda<Func<TInput, bool>>(
                Expression.AndAlso(
                    func1.Body, new ExpressionParameterReplacer(func2.Parameters, func1.Parameters).Visit(func2.Body)),
                func1.Parameters);
        }

        private class ExpressionParameterReplacer : ExpressionVisitor
        {
            public ExpressionParameterReplacer(IList<ParameterExpression> fromParameters, IList<ParameterExpression> toParameters)
            {
                ParameterReplacements = new Dictionary<ParameterExpression, ParameterExpression>();
                for (int i = 0; i != fromParameters.Count && i != toParameters.Count; i++)
                    ParameterReplacements.Add(fromParameters[i], toParameters[i]);
            }

            private IDictionary<ParameterExpression, ParameterExpression> ParameterReplacements { get; set; }

            protected override Expression VisitParameter(ParameterExpression node)
            {
                ParameterExpression replacement;
                if (ParameterReplacements.TryGetValue(node, out replacement))
                    node = replacement;
                return base.VisitParameter(node);
            }
        }
    }
}

Ответ 3

Если вам это нужно, я создал небольшую свободную библиотеку для создания лямбда-функций на лету без прямого взаимодействия с System.Linq.Expressions. И он может легко справиться с такой ситуацией. Просто чтобы привести пример:

static void Main(string[] args)
{
  var firstNameCompare = ExpressionUtil.GetComparer<FullName>((a) => a.FirstName);
  var lastNameCompare = ExpressionUtil.GetComparer<FullName>((a) => a.LastName);

  Func<FullName, bool> combined = (a) => firstNameCompare(a, "Dog") && lastNameCompare(a, "Boy");

  var toCheck = new FullName {FirstName = "Dog", LastName = "Boy"};
  Console.WriteLine("Dog Boy should be true: {0}", combined(toCheck));
  toCheck = new FullName {FirstName = "Cat", LastName = "Boy"};
  Console.WriteLine("Cat Boy should be false: {0}", combined(toCheck));

  Console.ReadLine();
}

Методы GetComparer ищут свойство, переданное как выражение, и находят ho, чтобы получить его значение, затем он создает новое выражение, которое будет обрабатывать comparaison.

В конце вычисляются две функции, вызывающие "комбинированную" функцию.

Если вам нужно больше проверок, вы можете использовать массив и перебирать его внутри "комбинированной лямбды"

Код и документация для библиотеки находятся здесь: Kendar Expression Builder Хотя пакет nuget находится здесь: Nuget Expression Builder

Ответ 4

Я пытался реализовать такие вещи. Принял у меня день, чтобы узнать. Мое решение основано на фильтрах в цикле на основе массива предикатов. В качестве примечания, это полностью общее и основанное отражение, потому что единственная информация о классе и поле - это String. Чтобы сделать это простым, я вызываю непосредственно класс Model, но в проекте вы должны пойти с помощью контроллера, который вызывает модель.

Итак, идем: Модельная часть, где T является общим в классе

    public class DALXmlRepository<T> where T : class
    {
    public T GetItem(Array predicate)
    {
        IQueryable<T> QueryList = null;

        QueryList = ObjectList.AsQueryable<T>().Where((Expression<Func<T, bool>>)predicate.GetValue(0));
        for (int i = 1; i < predicate.GetLength(0); i++)
        {
            QueryList = QueryList.Where((Expression<Func<T, bool>>)predicate.GetValue(i));
        }

        if (QueryList.FirstOrDefault() == null)
            throw new InvalidOperationException(this.GetType().GetGenericArguments().First().Name + " not found.");
        return QueryList.FirstOrDefault();
    }
    }

Теперь LambdaExpression Builder, он базовый (со строковым типом или чем-то еще), вы можете улучшить его с большей функциональностью:

    private static Expression BuildLambdaExpression(Type GenericArgument, string FieldName, string FieldValue)
    {
        LambdaExpression lambda = null;

        Expression Criteria = null;

        Random r = new Random();
        ParameterExpression predParam = Expression.Parameter(GenericArgument, r.Next().ToString());

        if (GenericArgument.GetProperty(FieldName).PropertyType == typeof(string))
        {
            Expression left = Expression.PropertyOrField(predParam, FieldName);
            Expression LefttoUpper = Expression.Call(left, "ToUpper", null, null);
            //Type du champ recherché
            Type propType = GenericArgument.GetProperty(FieldName).PropertyType;
            Expression right = Expression.Constant(FieldValue, propType);
            Expression RighttoUpper = Expression.Call(right, "ToUpper", null, null);
            Criteria = Expression.Equal(LefttoUpper, RighttoUpper);
        }
        else
        {
            Expression left = Expression.PropertyOrField(predParam, FieldName);
            Type propType = GenericArgument.GetProperty(FieldName).PropertyType;
            Expression right = Expression.Constant(Convert.ChangeType(FieldValue, propType), propType);

            Criteria = Expression.Equal(left, right);
        }

        lambda = Expression.Lambda(Criteria, predParam);
        return lambda;
    }

Теперь функция вызова:

    public static Hashtable GetItemWithFilter(string Entity, XMLContext contextXML, Hashtable FieldsNameToGet, Hashtable FieldFilter)
    {
        //Get the type
        Type type = Type.GetType("JP.Model.BO." + Entity + ", JPModel");
        Type CtrlCommonType = typeof(CtrlCommon<>).MakeGenericType( type );
        //Making an instance DALXmlRepository<xxx> XMLInstance = new DALXmlRepository<xxx>(contextXML);
        ConstructorInfo ci = CtrlCommonType.GetConstructor(new Type[] { typeof(XMLContext), typeof(String) });
        IControleur DalInstance = (IControleur)ci.Invoke(new object[] { contextXML, null });

        //Building the string type Expression<func<T,bool>> to init the array
        Type FuncType = typeof(Func<,>).MakeGenericType( type ,typeof(bool));
        Type ExpressType = typeof(Expression<>).MakeGenericType(FuncType);
        Array lambda = Array.CreateInstance(ExpressType,FieldFilter.Count);

        MethodInfo method = DalInstance.GetType().GetMethod("GetItem", new Type[] { lambda.GetType() });

        if (method == null)
            throw new InvalidOperationException("GetItem(Array) doesn't exist for " + DalInstance.GetType().GetGenericArguments().First().Name);

        int j = 0;
        IDictionaryEnumerator criterias = FieldFilter.GetEnumerator();
        criterias.Reset();
        while (criterias.MoveNext())
        {
            if (!String.IsNullOrEmpty(criterias.Key.ToString()))
            {
                lambda.SetValue(BuildLambdaExpression(type, criterias.Key.ToString(), criterias.Value.ToString()),j);
            }
            else
            {
                throw new JPException(JPException.MessageKey.CONTROLER_PARAMFIELD_EMPTY, "GetItemWithFilter", criterias.Key.ToString());
            }
            j++;
        }

        Object item = method.Invoke(DalInstance, new object[] { lambda });
        }

Аргумент: String Entity: имя класса сущности. XMLContext: это единица работы репозитория, аргумент, который я использую для инициализации класса Model Hashtable FieldsNameToGet: индекс/значение списка поля, которое я хочу вернуть  Hashtable FieldFilter: ключ/значение с полем Name/Content, используемым для выражения Lambda

Удачи.