C_Sharp_Language

C Sharp TutorialC Sharp .NET Compiler Platform (Roslyn)C Sharp 3.0 FeaturesC Sharp 4.0 FeaturesC Sharp 5.0 FeaturesC Sharp 6.0 FeaturesC Sharp 7.0 FeaturesC Sharp Access ModifiersC Sharp Access network shared folder with username and passwordC Sharp Accessing DatabasesC Sharp Action FiltersC Sharp Aliases of built-in typesC Sharp an overview of collectionsC Sharp Anonymous typesC Sharp ArraysC Sharp ASP.NET IdentityC Sharp AssemblyInfo.cs ExamplesC Sharp Async-AwaitC Sharp Async/await, Backgroundworker, Task and Thread ExamplesC Sharp Asynchronous SocketC Sharp AttributesC Sharp Authentication handlerC Sharp BackgroundWorkerC Sharp BigIntegerC Sharp Binary SerializationC Sharp BindingListC Sharp Built-in TypesC Sharp CachingC Sharp CastingC Sharp Checked and UncheckedC Sharp CLSCompliantAttributeC Sharp Code ContractsC Sharp Code Contracts and AssertionsC Sharp Collection InitializersC Sharp Comments and regionsC Sharp Common String OperationsC Sharp Conditional StatementsC Sharp Constructors and FinalizersC Sharp Creating Own MessageBox in Windows Form ApplicationC Sharp Creational Design PatternsC Sharp Cryptography (System.Security.Cryptography)C Sharp Data AnnotationC Sharp DateTime MethodsC Sharp DelegatesC Sharp Dependency InjectionC Sharp DiagnosticsC Sharp Dynamic typeC Sharp EnumC Sharp Equality OperatorC Sharp Equals and GetHashCodeC Sharp EventsC Sharp Exception HandlingC Sharp Expression TreesC Sharp Extension MethodsC Sharp File and Stream I/OC Sharp FileSystemWatcherC Sharp Func delegatesC Sharp Function with multiple return valuesC Sharp Functional ProgrammingC Sharp Garbage Collector in .NETC Sharp Generating Random NumbersC Sharp Generic Lambda Query BuilderC Sharp GenericsC Sharp Getting Started: Json with C SharpC Sharp GuidC Sharp Handling FormatException when converting string to other typesC Sharp Hash FunctionsC Sharp ICloneableC Sharp IComparableC Sharp IDisposable interfaceC Sharp IEnumerableC Sharp ILGeneratorC Sharp ImmutabilityC Sharp Implementing Decorator Design PatternC Sharp Implementing Flyweight Design PatternC Sharp Import Google ContactsC Sharp Including Font ResourcesC Sharp IndexerC Sharp InheritanceC Sharp Initializing PropertiesC Sharp INotifyPropertyChanged interfaceC Sharp InterfacesC Sharp InteroperabilityC Sharp IQueryable interfaceC Sharp IteratorsC Sharp KeywordsC Sharp Lambda expressionsC Sharp Lambda ExpressionsC Sharp LINQ QueriesC Sharp LINQ to Objects



C Sharp Generic Lambda Query Builder

From WikiOD

Remarks[edit | edit source]

The class is called ExpressionBuilder. It has three properties:

 private static readonly MethodInfo ContainsMethod = typeof(string).GetMethod("Contains", new[] { typeof(string) });
 private static readonly MethodInfo StartsWithMethod = typeof(string).GetMethod("StartsWith", new[] { typeof(string) });
 private static readonly MethodInfo EndsWithMethod = typeof(string).GetMethod("EndsWith", new[] { typeof(string) });

One public method GetExpression that returns the lambda expression, and three private methods:

  • Expression GetExpression<T>
  • BinaryExpression GetExpression<T>
  • ConstantExpression GetConstant

All the methods are explained in details in the examples.

QueryFilter class[edit | edit source]

This class holds predicate filters values.

public class QueryFilter
{
    public string PropertyName { get; set; }
    public string Value { get; set; }
    public Operator Operator { get; set; }

    // In the query {a => a.Name.Equals("Pedro")} 
    // Property name to filter - propertyName = "Name"
    // Filter value - value = "Pedro"
    // Operation to perform - operation = enum Operator.Equals
    public QueryFilter(string propertyName, string value, Operator operatorValue)
    {
        PropertyName = propertyName;
        Value = value;
        Operator = operatorValue;
    }       
}

Enum to hold the operations values:

 public enum Operator
{
    Contains,
    GreaterThan,
    GreaterThanOrEqual,
    LessThan,
    LessThanOrEqualTo,
    StartsWith,
    EndsWith,
    Equals,
    NotEqual
}

GetExpression Method[edit | edit source]

public static Expression<Func<T, bool>> GetExpression<T>(IList<QueryFilter> filters)
{
    Expression exp = null;

    // Represents a named parameter expression. {parm => parm.Name.Equals()}, it is the param part
    // To create a ParameterExpression need the type of the entity that the query is against an a name
    // The type is possible to find with the generic T and the name is fixed parm
    ParameterExpression param = Expression.Parameter(typeof(T), "parm");

    // It is good parctice never trust in the client, so it is wise to validate.
    if (filters.Count == 0)
        return null;

    // The expression creation differ if there is one, two or more filters.    
    if (filters.Count != 1)
    {
        if (filters.Count == 2)
            // It is result from direct call.
            // For simplicity sake the private overloads will be explained in another example.
            exp = GetExpression<T>(param, filters[0], filters[1]);
        else
        {
            // As there is no method for more than two filters, 
            // I iterate through all the filters and put I in the query two at a time
            while (filters.Count > 0)
            {
                // Retreive the first two filters
                var f1 = filters[0];
                var f2 = filters[1];

                // To build a expression with a conditional AND operation that evaluates 
                // the second operand only if the first operand evaluates to true.
                // It needed to use the BinaryExpression a Expression derived class 
                // That has the AndAlso method that join two expression together
                exp = exp == null ? GetExpression<T>(param, filters[0], filters[1]) : Expression.AndAlso(exp, GetExpression<T>(param, filters[0], filters[1]));

                // Remove the two just used filters, for the method in the next iteration finds the next filters
                filters.Remove(f1);
                filters.Remove(f2);

                // If it is that last filter, add the last one and remove it
                if (filters.Count == 1)
                {
                    exp = Expression.AndAlso(exp, GetExpression<T>(param, filters[0]));
                    filters.RemoveAt(0);
                }
            }
        }
    }
    else
        // It is result from direct call.
        exp = GetExpression<T>(param, filters[0]);

           // converts the Expression into Lambda and retuns the query
    return Expression.Lambda<Func<T, bool>>(exp, param);
}

GetExpression Private overload[edit | edit source]

For one filter:[edit | edit source]

Here is where the query is created, it receives a expression parameter and a filter.

private static Expression GetExpression<T>(ParameterExpression param, QueryFilter queryFilter)
{
    // Represents accessing a field or property, so here we are accessing for example:
    // the property "Name" of the entity
    MemberExpression member = Expression.Property(param, queryFilter.PropertyName);

    //Represents an expression that has a constant value, so here we are accessing for example:
    // the values of the Property "Name".
    // Also for clarity sake the GetConstant will be explained in another example.
    ConstantExpression constant = GetConstant(member.Type, queryFilter.Value);

    // With these two, now I can build the expression
    // every operator has it one way to call, so the switch will do.
    switch (queryFilter.Operator)
    {
        case Operator.Equals:
            return Expression.Equal(member, constant);

        case Operator.Contains:
            return Expression.Call(member, ContainsMethod, constant);

        case Operator.GreaterThan:
            return Expression.GreaterThan(member, constant);

        case Operator.GreaterThanOrEqual:
            return Expression.GreaterThanOrEqual(member, constant);

        case Operator.LessThan:
            return Expression.LessThan(member, constant);

        case Operator.LessThanOrEqualTo:
            return Expression.LessThanOrEqual(member, constant);

        case Operator.StartsWith:
            return Expression.Call(member, StartsWithMethod, constant);

        case Operator.EndsWith:
            return Expression.Call(member, EndsWithMethod, constant);
    }

    return null;
}

For two filters:[edit | edit source]

It returns the BinaryExpresion instance instead of the simple Expression.

private static BinaryExpression GetExpression<T>(ParameterExpression param, QueryFilter filter1, QueryFilter filter2)
{
    // Built two separated expression and join them after.
    Expression result1 = GetExpression<T>(param, filter1);
    Expression result2 = GetExpression<T>(param, filter2);
    return Expression.AndAlso(result1, result2);
}

ConstantExpression Method[edit | edit source]

ConstantExpression must be the same type of the MemberExpression. The value in this example is a string, which is converted before creating the ConstantExpression instance.

private static ConstantExpression GetConstant(Type type, string value)
{
    // Discover the type, convert it, and create ConstantExpression 
    ConstantExpression constant = null;
    if (type == typeof(int))
    {
        int num;
        int.TryParse(value, out num);
        constant = Expression.Constant(num);
    }
    else if(type == typeof(string))
    {
        constant = Expression.Constant(value);
    }
    else if (type == typeof(DateTime))
    {
        DateTime date;
        DateTime.TryParse(value, out date);
        constant = Expression.Constant(date);
    }
    else if (type == typeof(bool))
    {                
        bool flag;
        if (bool.TryParse(value, out flag))
        {
            flag = true;
        }
        constant = Expression.Constant(flag);
    }
    else if (type == typeof(decimal))
    {
        decimal number;
        decimal.TryParse(value, out number);
        constant = Expression.Constant(number);
    }
    return constant;
}

Usage[edit | edit source]

Collection filters = new List(); QueryFilter filter = new QueryFilter("Name", "Burger", Operator.StartsWith); filters.Add(filter);

 Expression<Func<Food, bool>> query = ExpressionBuilder.GetExpression<Food>(filters);

In this case, it is a query against the Food entity, that want to find all foods that start with "Burger" in the name.

Output:[edit | edit source]

query = {parm => a.parm.StartsWith("Burger")}

Expression<Func<T, bool>> GetExpression<T>(IList<QueryFilter> filters)

Credit:Stack_Overflow_Documentation