Answers for "Advanced Dynamic LINQ Expression"

C#
0

Advanced Dynamic LINQ Expression

// sample query to generate dynamically 
_people.Where(x =>
    (x.Age > 60 && x.Address.City == "Paoli") ||
        (x.Address.Street == "Market Street"));

// The following code represents the use of the class in creating the query dynamically

var lambda = new ExpressionCriteria<Person>()
    .Add("Age", 60, ExpressionType.GreaterThan)
    And()
    .Add("Address.City", "Paoli", ExpressionType.Equal)
    Or()
    .Add("Address.Street", "Market Street", ExpressionType.Equal)
    .GetLambda().Compile();

var result = _people.Where(lambda);

//There are three critical elements of a criterion element:

//Property Name: The property of the Expression Criteria's type that will be used as the basis of a criterion element.
//Value: The value used, in conjunction with an expression type to compare to the property.
//Operator: The Expression Type used to compare the Property Name with the Value.
//The following is an example of the Property, Value, Operator relationship:

//x.Age > 60
//Property: Age
//Value: 60
//Operator: Greater Than
//When the Add() method is invoked, the current state of the _andOr variable is applied as well. By default, the value is set to And.

//Each criterion has four attributes:

//Property Name
//Value
//Expression Type
//AndOr
//When an Expression Criterion Expression is created, the property name is validated against the specified type.

// Class used for the above codes
public class ExpressionCriterion<T>
    {
        public ExpressionCriterion(string propertyName, object value, ExpressionType op, string andOr = "And")
        {
            AndOr = andOr;
            PropertyName = propertyName;
            Value = value;
            Operator = op;
            validateProperty(typeof(T), propertyName);
        }
        PropertyInfo validateProperty(Type type, string propertyName)
        {
            string[] parts = propertyName.Split('.');

            var info = (parts.Length > 1)
            ? validateProperty(
                type.GetProperty(
                    parts[0]).PropertyType,
                    parts.Skip(1).Aggregate((a, i) =>
                        a + "." + i)) : type.GetProperty(propertyName);
            if (info == null)
                throw new ArgumentException(propertyName,
                    $"Property {propertyName} is not a member of {type.Name}");
                return info;
        }
        public string PropertyName { get; }
        public object Value { get; }
        public ExpressionType Operator { get; }
        public string AndOr { get; }
    }

    public class ExpressionCriteria<T>
    {
        List<ExpressionCriterion<T>> _expressionCriterion = new List<ExpressionCriterion<T>>();

        private string _andOr = "And";

        public ExpressionCriteria<T> And()
        {
            _andOr = "And";
            return this;
        }
        public ExpressionCriteria<T> Or()
        {
            _andOr = "Or";
            return this;
        }

        public ExpressionCriteria<T> Add(string propertyName, object value, ExpressionType op)
        {
            var newCriterion = new ExpressionCriterion<T>(propertyName, value, op, _andOr);
            _expressionCriterion.Add(newCriterion);
            return this;
        }

        // The first step in creating a lambda expression is to first create an expression:
        public BinaryExpression GetExpression(ParameterExpression parameter, ExpressionCriterion<T> expressionCriteria)
        {
            Expression expression = parameter;
            foreach (var member in expressionCriteria.PropertyName.Split('.'))
            {
                expression = Expression.PropertyOrField(expression, member);
            }
            return Expression.MakeBinary(expressionCriteria.Operator, expression, Expression.Constant(expressionCriteria.Value));
        }


        // Once the expression is created, a lambda expression can be created:
        public Expression<Func<T, bool>> GetLambda()
        {
            Expression expression = null;
            var parameterExpression = Expression.Parameter(typeof(T), typeof(T).Name.ToLower());
            foreach (var item in _expressionCriterion)
            {
                if (expression == null)
                {
                    expression = GetExpression(parameterExpression, item);
                }
                else
                {
                    expression = item.AndOr == "And" ? Expression.And(expression, GetExpression(parameterExpression, item)) :
                        Expression.Or(expression, GetExpression(parameterExpression, item));
                }
            }
            return expression != null ?
                Expression.Lambda<Func<T, bool>>(expression, parameterExpression) : null;
        }

    }
Posted by: Guest on August-24-2021

C# Answers by Framework

Browse Popular Code Answers by Language