1

I am trying to create my own custom operation which I can use in the database to find rows which are affected by a change in a value.

Im looking at Jon Skeets between operator example here: LINQ Between Operator but im having trouble as my operation contains multiple parameter inputs

    public static IQueryable<TSource> LeavingRange<TSource, TKey>(this IQueryable<TSource> source,
        Expression<Func<TSource, TKey>> lowKeySelector,
        Expression<Func<TSource, TKey>> highKeySelector,
        Nullable<TKey> oldValue,
        Nullable<TKey> newValue)
            where TKey : struct, IComparable<TKey>
    

As you can see I have 2 selectors, however I'm not too sure how to combine these correctly into the parameters for my Expression.Lambda call. I have tried just putting the parameters from both input expressions into the lambda as parameters but i think im missing something.

Expression.Lambda<Func<TSource, bool>>(isLeavingRange, lowKeySelector.Parameters[0], highKeySelector.Parameters[0]);

Doing this gives the following error:

Incorrect number of parameters supplied for lambda declaration

What is the correct way to combine the input parameters when constructing a Lambda?


Supporting Info

My full code is below, but I think the relevant bits are the two selectors and the Expression.Lambda call

    public static IQueryable<TSource> LeavingRange<TSource, TKey>(this IQueryable<TSource> source,
        Expression<Func<TSource, TKey>> lowKeySelector,
        Expression<Func<TSource, TKey>> highKeySelector,
        Nullable<TKey> oldValue,
        Nullable<TKey> newValue)
            where TKey : struct, IComparable<TKey>
    {
        Expression lowKey = Expression.Invoke(lowKeySelector, lowKeySelector.Parameters.ToArray());
        Expression highKey = Expression.Invoke(highKeySelector, highKeySelector.Parameters.ToArray());

        //is oldValue null which means it cant possibly be leaving
        var oldValueIsNotNull = Expression.NotEqual(Expression.Constant(oldValue, typeof(Nullable<TKey>)), Expression.Constant(null, typeof(Nullable<TKey>)));
        var newValueIsNull = Expression.Equal(Expression.Constant(newValue, typeof(Nullable<TKey>)), Expression.Constant(null, typeof(Nullable<TKey>)));
        var newValueIsNotNull = Expression.Not(newValueIsNull);

        var oldValueIsBetweenRange = Between(Expression.Convert(Expression.Constant(oldValue), typeof(TKey)), lowKey, highKey);
        var newValueIsNotBetweenRange = Expression.Not(Between(Expression.Convert(Expression.Constant(newValue), typeof(TKey)), lowKey, highKey));

        //IE leaving because its going from in the range to null
        var newValueIsNullAndOldValueIsBetweenRange = Expression.AndAlso(newValueIsNull, oldValueIsBetweenRange);

        var oldValueIsInRangeAndNewValueIsNot = Expression.AndAlso(newValueIsNotNull, Expression.AndAlso(oldValueIsBetweenRange, newValueIsNotBetweenRange));
        var isLeavingRange = Expression.AndAlso(oldValueIsNotNull, Expression.Or(newValueIsNullAndOldValueIsBetweenRange, oldValueIsInRangeAndNewValueIsNot));

        var leavingRange = Expression.Lambda<Func<TSource, bool>>(isLeavingRange, lowKeySelector.Parameters[0], highKeySelector.Parameters[0]);
        
        return source.Where(leavingRange);
    }
Community
  • 1
  • 1
undefined
  • 33,537
  • 22
  • 129
  • 198
  • I believe that answerer Ripple has addressed your question. It looks like an appropriate answer to me. But if not, I will note that you did not post a [good, complete code example](https://stackoverflow.com/help/mcve), without which it's easy to misunderstand the context, or the question, or at least makes it very difficult to provide an answer that is clear and specific enough for your needs. – Peter Duniho Jan 20 '15 at 14:34

1 Answers1

2

A delegate passed to Where() takes each only one element in the collection as an argument, so you need to make the two expressions invoking lowKeySelector and highKeySelector take that same element (same instance of the ParameterExpression) as an argument and also need to build the lambda expression to use that as a parameter.

public static IQueryable<TSource> LeavingRange<TSource, TKey>(this IQueryable<TSource> source,
     Expression<Func<TSource, TKey>> lowKeySelector,
     Expression<Func<TSource, TKey>> highKeySelector,
     Nullable<TKey> oldValue,
     Nullable<TKey> newValue)
         where TKey : struct, IComparable<TKey>
{
    ParameterExpression paramOfWhereDelg = Expression.Parameter(typeof(TSource), "p");

    Expression lowKey = Expression.Invoke(lowKeySelector, paramOfWhereDelg);
    Expression highKey = Expression.Invoke(highKeySelector, paramOfWhereDelg);

    // Build your expression tree
    //  ...

    var leavingRange = Expression.Lambda<Func<TSource, bool>>(isLeavingRange, paramOfWhereDelg);

    return source.Where(leavingRange);
}

(Or you can use lowKeySelector.Parameters instead of paramOfWhereDelg, but I believe creating an another ParameterExpression would make it easier to understand.)

Ripple
  • 1,257
  • 1
  • 9
  • 15
  • That works perfectly, i didnt understand what that parameter was doing, it looks like all it is is the input into the lambda. Thanks – undefined Jan 20 '15 at 18:56