1

I need to build custom string expressions based on input and provided operators. However these comparisons are always case sentitive. I need to do the comparison while ignoring case.

static Expression<Func<T, bool>> GetStringExpression<T>(string field, string compareOperator, string value)
    {
        var parameterExp = Expression.Parameter(typeof(T), "type");
        var propertyExp = Expression.Property(parameterExp, field);
        MethodInfo method = typeof(string).GetMethod(compareOperator, new[] { typeof(string) });
        var someValue = Expression.Constant(value, typeof(string));
        var containsMethodExp = Expression.Call(propertyExp, method, someValue);

        return Expression.Lambda<Func<T, bool>>(containsMethodExp, parameterExp);
    }

usage of current code:

var expression = GetStringExpression<MyClass>("MyProperty", "Contains", "testValue");

var mylist = QueryableList.Where(expression).ToList();

This comparison will not return true when comparing "test" with "TEST", that case should also be true.

JMan
  • 2,611
  • 3
  • 30
  • 51
  • edited question, comparing ignoring the string case – JMan Nov 29 '15 at 14:41
  • 1
    Can you give an example of a possible `compareOperator`? Your question is confusing - comparisons usually return `int` rather than `bool`. Also, `compareOperator` must be a name of one `String` type methods which seems out of context in your code. – shay__ Nov 29 '15 at 14:43
  • 1
    You'll have to `switch` on `compareOperator` to determine the appropriate overload and the appropriate type to pass (`StringComparison`, `StringComparer` etc). What are you trying to accomplish anyway? – haim770 Nov 29 '15 at 14:45
  • added usage code, it is to pass in a list of filters and dynamically create an expression to execute on my list – JMan Nov 29 '15 at 14:48
  • You're letting the consumer of the method to pass in arbitrary method name (as `compareOperator` argument) and that's prone to errors and not very friendly. You better create an `enum` that will represent the methods you're going to support then `switch` on the value to determine the correct method overload and parameters. Also, why are you generating an `Expression` and not simply a generic delegate? Is there going to be some LINQ provider that will interpret those expressions? – haim770 Nov 29 '15 at 14:51
  • JMan: You may want to read this post, as I think it is relevant to what you're trying to achieve: https://stackoverflow.com/questions/600978/how-to-do-template-specialization-in-c-sharp – srm Nov 29 '15 at 15:11

2 Answers2

0

There is no method on class String that does case insensitive comparison unless you pass additional parameters. There are so many varieties of ways for you to do string comparison (cultural aware, OS aware, case (in)sensitive) that you pretty much need to use parameters every time you're going to compare two strings. I've worked on codebases where even the default exact comparison had to be explicitly stated, just to make sure developers didn't overlook thinking about which one they wanted.

GIVEN THAT: I would suggest you may want to stop using fields of type "string". Instead, create a new class that wraps a private "string" field and provides the functionality that you want... that new class can have a Contains method that does not need extra parameters. You would write that Contains method using the existing string API to operate on the private field. By using a new class, whenever you do your type reflection, you'll get the functionality that you're looking for.

srm
  • 3,062
  • 16
  • 30
-2

What about the easy way?

    static Expression<Func<T, bool>> GetStringExpression<T>(string field, string compareOperator, string value)
    {
        var parameterExp = Expression.Parameter(typeof(T), "type");
        var propertyExp = Expression.Property(parameterExp, field);
        MethodInfo method = typeof(string).GetMethod(compareOperator, new[] { typeof(string) });
        var someValue = Expression.Constant(value.ToLower(), typeof(string)); //mind the ToLower
        var containsMethodExp = Expression.Call(propertyExp, method, someValue);
        //and one more:
        var toStringMethod = typeof(object).GetMethod("ToString", Type.EmptyTypes);
        var toStringExp = Expression.Call(containsMethodExp, toStringMethod);
        var toLowerMethod = typeof(string).GetMethod("ToLower", Type.EmptyTypes);
        var toLowerExp = Expression.Call(toStringExp, toLowerMethod);

        return Expression.Lambda<Func<T, bool>>(toLowerExp, parameterExp);
    }

I did not test it, but the idea is very simple - just perform ToLower() on both values...

shay__
  • 3,815
  • 17
  • 34
  • Calling ToLower assumes that the type is actually a string. Notice that the "value" parameter is being used to construct some other type: var someValue = Expression.Constant(value, typeof(string)); So using any other method on the value property does not work. – srm Nov 29 '15 at 15:04
  • You are talking about `field`, not `value` (which is a string argument), and you are correct. Simply needs to add a call to `ToString` before the call to `ToLower`. Nice catch! :) – shay__ Nov 29 '15 at 15:07
  • Right, but I think the post wants to do a case-insensitive compare on "someValue" -- i.e. if T happens to be string, do something special, otherwise, do exactly what the code does now. – srm Nov 29 '15 at 15:09
  • `typeof(object).GetMethod("ToLower")` would throw because there is one more overload – haim770 Nov 29 '15 at 15:11
  • @srm BTW I think you were wrong about your comment - T is just the base type parameter while `field` is the name of one of it's fields on which the comparison is made. – shay__ Nov 29 '15 at 15:20
  • @shay : he's comparing "value" made into a constant against the value of the property. His comment says "This comparison will not return true when comparing "test" with "TEST", that case should also be true." That's the actual value of the field, not the name of the field. If it were the name of the field, he'd be asking about "MyPROPERTY" vs "myProPerTy". – srm Nov 30 '15 at 02:46