The following Linq-to-Entities query works fine:
var query = repository.Where(r => r.YearProp1.HasValue &&
r.YearProp1 >= minYear &&
r.YearProp1 <= maxYear);
My database has a dozen or so columns that all report year-related information (short?
data type). I want to reuse the same Linq-to-Entities logic for all these columns. Something like:
Func<RepoEntity, short?> fx = GetYearPropertyFunction();
var query = repository.Where(r => fx(r).HasValue &&
fx(r) >= minYear &&
fx(r) <= maxYear);
This results in the error:
LINQ to Entities does not recognize the method 'System.Nullable`1[System.Int16] fx(RepoEntity)' method, and this method cannot be translated into a store expression.
I understand why I am getting the error, but am wondering if there is a workaround that doesn't involve duplicating code a dozen times just to change the property on which the SQL query is operating.
I would be reusing the function in more than one query, so I guess the general version of my question is: Is there a way to convert a simple property-getter lambda function to an Expression that can be consumed by Linq-to-Entities?
Building off of Raphaël Althaus' answer, but adding the generic selector you were originally looking for:
public static class Examples
{
public static Expression<Func<MyEntity, short?>> SelectPropertyOne()
{
return x => x.PropertyOne;
}
public static Expression<Func<MyEntity, short?>> SelectPropertyTwo()
{
return x => x.PropertyTwo;
}
public static Expression<Func<TEntity, bool>> BetweenNullable<TEntity, TNull>(Expression<Func<TEntity, Nullable<TNull>>> selector, Nullable<TNull> minRange, Nullable<TNull> maxRange) where TNull : struct
{
var param = Expression.Parameter(typeof(TEntity), "entity");
var member = Expression.Invoke(selector, param);
Expression hasValue = Expression.Property(member, "HasValue");
Expression greaterThanMinRange = Expression.GreaterThanOrEqual(member,
Expression.Convert(Expression.Constant(minRange), typeof(Nullable<TNull>)));
Expression lessThanMaxRange = Expression.LessThanOrEqual(member,
Expression.Convert(Expression.Constant(maxRange), typeof(Nullable<TNull>)));
Expression body = Expression.AndAlso(hasValue,
Expression.AndAlso(greaterThanMinRange, lessThanMaxRange));
return Expression.Lambda<Func<TEntity, bool>>(body, param);
}
}
Could be used somewhat like the original query you were looking for:
Expression<Func<MyEntity, short?>> whatToSelect = Examples.SelectPropertyOne;
var query = Context
.MyEntities
.Where(Examples.BetweenNullable<MyEntity, short>(whatToSelect, 0, 30));
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With