Search code examples
c#entity-frameworklinqexpression-trees

I need to search strings ignoring case of the strings using Expression Trees


I want to build an expression tree with an equal that behaves as if it were in SQL. That is to say, I want it to ignore case of the strings e.g.:

"ThIs Is A tEsT", "tHiS iS a TeSt", "this is a test", and "THIS IS A TEST" should all match to "this is a test".

public class Datum
{
    public string Value { get; set; }
}

void Main()
{
    var data = new List<Datum> {
        new Datum { Value = "ThIs Is A tEsT" },
        new Datum { Value = "tHiS iS a TeSt" },
        new Datum { Value = "this is a test" },
        new Datum { Value = "THIS IS A TEST" }
    };

    var queryableData = data.AsQueryable<Datum>();
    var pe = Expression.Parameter(typeof(Datum), "Value");
    var right = Expression.Constant("this is a test", typeof(string));
    var e = Expression.Equal(Expression.Property(pe, "Value"), right);
    var whereCallExpression = Expression.Call(
        typeof(Queryable),
        "Where",
        new Type[] { queryableData.ElementType },
        queryableData.Expression,
        Expression.Lambda<Func<Datum, bool>>(e, pe));   
    var results = queryableData.Provider.CreateQuery<Datum>(whereCallExpression);
    results.Dump();
}

I have tried several things and nothing seems to be working.


Solution

  • I found the most direct way to accomplish this task is to Invoke a custom method like this:

    public class Datum
    {
        public string Value { get; set; }
    }
    
    void Main()
    {
        var data = new List<Datum> {
            new Datum { Value = "ThIs Is A tEsT" },
            new Datum { Value = "tHiS iS a TeSt" },
            new Datum { Value = "this is a test" },
            new Datum { Value = "THIS IS A TEST" }
        };
    
        var queryableData = data.AsQueryable<Datum>();
        var pe = Expression.Parameter(typeof(Datum), "Value");
        var right = Expression.Constant("this is a test", typeof(string));
    
        // * First build a func that will use the method you want.  In this case, it is going to be any equals that ignores case.
        // * Next Invoke that method instead of Expression.Equal.
        // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
        Expression<Func<string, string, bool>> IgnoreCase = (op1, op2) => op1.Equals(op2, StringComparison.OrdinalIgnoreCase);
        var e = Expression.Invoke(IgnoreCase, Expression.Property(pe, "Value"), right);
    
        //var e = Expression.Equal(Expression.Property(pe, "Value"), right);
        var whereCallExpression = Expression.Call(
            typeof(Queryable),
            "Where",
            new Type[] { queryableData.ElementType },
            queryableData.Expression,
            Expression.Lambda<Func<Datum, bool>>(e, pe));   
        var results = queryableData.Provider.CreateQuery<Datum>(whereCallExpression);
        results.Dump();
    }