I am really wondering if anyone can help me out. We have a very generic approach to build our Expressions with LinqKit. With internal Lists everything is working fine, but when using EF Core we got some Errors.
System.InvalidOperationException: The LINQ expression 'DbSet<Entity>()
.Where(k => False || __propertyInfo_0.GetValue(k) != null && __propertyInfo_0.GetValue(k).ToString().Contains(__GenericTableOptions_BaseQueryParameters_Search_1) && True)' could not be translated.
Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to 'AsEnumerable', 'AsAsyncEnumerable', 'ToList', or 'ToListAsync'. See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.
The Generic Expression Builder looks like this
private Expression<Func<TEntity, bool>> BuildGenericSearchExpression()
{
Expression<Func<TEntity, bool>> expression = x => false;
var searchFields = GenericTableOptions.SearchColumns;
var propertyInfos = GetColumnsAsProperties(typeof(TEntity), searchFields).ToList();
if (propertyInfos.Count == 0)
{
// Returning true to allow AND chaining of multiple conditions
return x => true;
}
foreach (var propertyInfo in propertyInfos)
{
expression = expression.Or(x =>
propertyInfo.GetValue(x) != null &&
propertyInfo.GetValue(x)!.ToString()!.Contains(GenericTableOptions.BaseQueryParameters.Search));
}
return expression;
}
Anyone has an idea to sort this out?
CodePudding user response:
You cannot use expression.Or(...)
here, you have to build LamdaExpression dynamically.
Below is corrected code, also removed Null check, it is not needed for SQL translation.
private Expression<Func<TEntity, bool>> BuildGenericSearchExpression()
{
var searchFields = GenericTableOptions.SearchColumns;
var propertyInfos = GetColumnsAsProperties(typeof(TEntity), searchFields).ToList();
if (propertyInfos.Count == 0)
{
// Returning true to allow AND chaining of multiple conditions
return x => true;
}
Expression? predicate = null;
var param = Expression.Parameter(typeof(TEntity), "e");
var searchExpr = Expression.Constant(GenericTableOptions.BaseQueryParameters.Search);
foreach (var propertyInfo in propertyInfos)
{
var memberAccess = EnsureString(Expression.MakeMemberAccess(param, propertyInfo));
var condition = Expression.Call(memberAccess, nameof(string.Contains), Type.EmptyTypes, searchExpr);
predicate = predicate == null ? condition : Expression.OrElse(predicate, condition);
}
var predicateLambda = Expression.Lambda<Func<TEntity, bool>>(predicate!, param);
return predicateLambda;
}
// Helper method
private static Expression EnsureString(Expression expression)
{
if (expression.Type == typeof(string))
return expression;
if (expression.Type != typeof(object))
expression = Expression.Convert(expression, typeof(object));
expression = Expression.Call(_toStringMethod, expression);
return expression;
}
private static MethodInfo _toStringMethod = typeof(Convert).GetMethods()
.Single(m =>
m.Name == nameof(Convert.ToString) && m.GetParameters().Length == 1 &&
m.GetParameters()[0].ParameterType == typeof(object)
);