0

This question related to my previous

In short I want to create my custom expression for IQueryable. To achieve this goal I wrote next code.

public static IQueryable<T> FilterEquals<T, TValue>(this IQueryable<T> qSource, Expression<Func<T, TValue>> field, TValue value)
{
    var fieldFilter = ExpressionClosureFactory.GetField(value);
    var constraint = Expression.Equal(field.Body, fieldFilter);

    return ApplyWhere(qSource, constraint, field.Parameters);
}

private static IQueryable<T> ApplyWhere<T>(IQueryable<T> qSource, Expression constraint, ReadOnlyCollection<ParameterExpression> parameters)
{
    var predicateFrom = Expression.Lambda<Func<T, bool>>(constraint, parameters);
    return qSource.Where(predicateFrom);
}

public class ExpressionClosureFactory
{
        public static MemberExpression GetField<TValue>(TValue value)
        {
            var closure = new ExpressionClosureField<TValue>
            {
                ValueProperty = value
            };

            return Expression.Field(Expression.Constant(closure), "ValueProperty");
        }            

        class ExpressionClosureField<T>
        {
            public T ValueProperty;
        }
}

It works fine and generate parameterized SQL query. But has some problems with nullable types. Next code

int testFilter = 1;
var testExtension = Context.Set<User>()
      .FilterEquals(u => u.IntNullable, testFilter);

Translated into

SELECT 
[Extent1].[Id] AS [Id],
[Extent1].[IntNullable] AS [IntNullable]
FROM [dbo].[User] AS [Extent1]
WHERE ([Extent1].[IntNullable] = @p__linq__0) OR (([Extent1].[IntNullable] IS NULL) AND (@p__linq__0 IS NULL))

And code without extension

int testFilter = 1;
var testUsual = Context.Set<User>()
      .Where(u => u.IntNullable == testFilter);

Translated into

SELECT 
[Extent1].[Id] AS [Id],
[Extent1].[IntNullable] AS [IntNullable]
FROM [dbo].[User] AS [Extent1]
WHERE [Extent1].[IntNullable] = @p__linq__0

As we can see in standard scenario linq does not generate unnecessary OR clause. So how can I achieve same in my code?

Update

I changed FilterEquals and it now works as expected, but this code looks like hack

public static IQueryable<T> FilterEquals<T, TValue>(this IQueryable<T> qSource, Expression<Func<T, TValue>> field, TValue value)
{
    Expression fieldFilter;
    var underlyingType = Nullable.GetUnderlyingType(field.Body.Type);
    if (underlyingType != null)
    {
        var convertedToUnderline = 
            Expression.Convert(ExpressionClosureFactory.GetField(value), underlyingType);

        fieldFilter = Expression.Convert(convertedToUnderline, field.Body.Type);
    }
    else
    {
        fieldFilter = ExpressionClosureFactory.GetField(value);
    }
    var constraint = Expression.Equal(field.Body, fieldFilter);

    return ApplyWhere(qSource, constraint, field.Parameters);
}
Community
  • 1
  • 1
YuriyP
  • 4,210
  • 4
  • 25
  • 35
  • “in standard scenario linq does not generate unnecessary OR clause” Is it actually unnecessary? In other words, are you 100 % sure that `value` won't be `null`? Also, why does this matter to you? Have you actually measured that the query with OR is slower than without it? Because I don't think “the code that most likely won't be seen by anyone is ugly” is a good reason. – svick Aug 02 '14 at 22:03
  • I am 100% sure that `value` won't be `null` because i am handling `null` in my code (here I posted short version for simplify reasons). You can see full code [on github](https://github.com/YuriyPavlishin/QueryableFilters). About OR performance and parameters [here](http://stackoverflow.com/questions/1936685/sql-server-query-optimization-where-col-col-or-col-null) – YuriyP Aug 05 '14 at 08:37

0 Answers0