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);
}