Linq Expressions. Equivalent of greater than for strings

2.2k views Asked by At

I have followed several threads on SO and other websites and am still having difficulty.

I have the following code:

public static IQueryable<TSource> Between<TSource, TKey>(this IQueryable<TSource> source, Expression<Func<TSource, TKey>> keySelector, TKey low, TKey high, bool inclusive = true) where TKey : IComparable<TKey>
    {
        var key = Expression.Invoke(keySelector, keySelector.Parameters.ToArray());

        var intLow = int.Parse(low.ToString());
        var intHigh = int.Parse(high.ToString());

        var lowerBound = (inclusive)
                  ? Expression.GreaterThanOrEqual(key, Expression.Constant(intLow, typeof(int)))
                  : Expression.GreaterThan(key, Expression.Constant(intLow, typeof(int)));

        var upperBound = (inclusive)
                  ? Expression.LessThanOrEqual(key, Expression.Constant(intHigh, typeof(int)))
                  : Expression.LessThan(key, Expression.Constant(intHigh, typeof(int)));

        var and = Expression.AndAlso(lowerBound, upperBound);
        var lambda = Expression.Lambda<Func<TSource, bool>>(
                        and, keySelector.Parameters);

        return source.Where(lambda);
    }

If I call this function with the following code:

lowValue = 2;
highValue = 11;

var sampleData = BuildSampleEntityInt(1, 3, 10, 11, 12, 15);
var query = sampleData.Between(s => s.SampleSearchKey, lowValue, highValue, false);
Assert.AreEqual(2, query.Count());

It works. But If I use Strings instead like:

lowValueString = "3";
highValueString = "10";

var sampleData = BuildSampleEntityString(1, 3, 10, 11, 12, 15);
var query = sampleData.Between(s => s.SampleSearchKey, lowValueString , highValueString);
Assert.AreEqual(2, query.Count());

or convert the strings to integers first

lowValueString = "3";
highValueString = "10";
int.TryParse(lowValueString, out lowValue);
int.TryParse(highValueString, out highValue);


var sampleData = BuildSampleEntityString(1, 3, 10, 11, 12, 15);
var query = sampleData.Between(s => s.SampleSearchKey, lowValue, highValue);
Assert.AreEqual(2, query.Count());

I get the following error:

{"The binary operator GreaterThanOrEqual is not defined for the types 'System.String' and 'System.Int32'."}

When the following line runs.

var lowerBound = (inclusive)
             ? Expression.GreaterThanOrEqual(key, Expression.Constant(intLow, typeof(int)))
             : Expression.GreaterThan(key, Expression.Constant(intLow, typeof(int)));

Can anyone point out what needs to be changed?

I have added the following:

 private IQueryable<SampleEntityInt> BuildSampleEntityInt(params int[] values)
    {
        return values.Select(
               value =>
               new SampleEntityInt() { SampleSearchKey = value }).AsQueryable();
    }
1

There are 1 answers

0
Maksim Simkin On

The problem here is that you define two generic type attributes: TSource and TKey, but actually you have three types of them. It works, if TKey is the same type as lowValue and highValue (both are int), but it doesn't work if lowValue and highValue are of the type string and TKey is still int.

Code you provided works fine, if i add the third generic parameter TLowHigh, that's a type of your low/hight arguments :

public static IQueryable<TSource> Between<TSource, TKey, TLowHigh>(
      this IQueryable<TSource> source, 
      Expression<Func<TSource, TKey>> keySelector, 
      TLowHigh low, 
      TLowHigh high,
      bool inclusive = true) 
          where TKey : IComparable<TKey>
{
    var key = Expression.Invoke(keySelector, keySelector.Parameters.ToArray());

    var intLow = int.Parse(low.ToString());
    var intHigh = int.Parse(high.ToString());

    var lowerBound = (inclusive)
            ? Expression.GreaterThanOrEqual(key, Expression.Constant(intLow, typeof(int)))
            : Expression.GreaterThan(key, Expression.Constant(intLow, typeof(int)));

    var upperBound = (inclusive)
            ? Expression.LessThanOrEqual(key, Expression.Constant(intHigh, typeof(int)))
            : Expression.LessThan(key, Expression.Constant(intHigh, typeof(int)));

    var and = Expression.AndAlso(lowerBound, upperBound);
    var lambda = Expression.Lambda<Func<TSource, bool>>(
                    and, keySelector.Parameters);

    return source.Where(lambda);
}