How to Append to an expression

后端 未结 8 1516
清歌不尽
清歌不尽 2020-12-05 01:46

Based on my question from yesterday:

if I had to append to my existing \'where\' expression, how would i append?

Expression

        
相关标签:
8条回答
  • 2020-12-05 02:32

    I believe you can just do the following:

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

    If you need to keep everything in Expression-land (to use with IQueryable), you could also do the following:

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

    This can be made less verbose by defining this extension method:

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

    You can then use syntax like this:

    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);
    }
    
    0 讨论(0)
  • 2020-12-05 02:34

    I tried to implement this kind of stuff. Took me a day to find out. My solution is based on filter in a loop based on a Array of predicate. As a note, it s totally Generic and based Reflection because the only information about class and field are String. To make it simple, i call directly the Model class but in a project you should go by a controler who is calling the Model.

    So here we go : The Model part where T is a Generic in the class

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

    Now the LambdaExpression Builder, it's a base one(with String type or something else) , you can improve it with more functionnality :

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

    Now the Calling function :

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

    The argument are : String Entity : Entity class name. XMLContext : it s the unit of work of the repository, argument i use to initialize the Model class Hashtable FieldsNameToGet : Index/value of the list of the field i want to get back Hashtable FieldFilter : the key/Value with FieldName/Content used to make the Lambda expression

    Good Luck.

    0 讨论(0)
提交回复
热议问题