Reputation: 34248
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?
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);
}
Upvotes: 1
Views: 612
Reputation: 1265
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.)
Upvotes: 2