Entity Framework Filter "Expression<Func<T, Bool>>"

Entity Framework Filter ExpressionFuncT, bool

Jon and Tim already explained why it doesn't work.

Assuming that the filter code inside Filter is not trivial, you could change Filter so that it returns an expression EF can translate.

Let's assume you have this code:

context.Table.Where(x => x.Name.Length > 500);

You can now create a method the returns this expression:

Expression<Func<YourEntity, bool>> FilterByNameLength(int length)
{
return x => x.Name.Length > length;
}

Usage would be like this:

context.Table.Where(FilterByNameLength(500));

The expression you build inside FilterByNameLength can be arbitrarily complex as long as you could pass it directly to Where.

Combine Expression (ExpressionFuncTIn,TOut with ExpressionFuncTOut, bool)

Using the common ExpressionVisitor for replacing one Expression with another, my standard Compose function (in the more common mathematical order, I think), substitutes the Body of one LambdaExpression for the parameter in another:

public static class ExpressionExt {
// Compose: (y => f(y)).Compose(x => g(x)) -> x => f(g(x))
/// <summary>
/// Composes two LambdaExpression into a new LambdaExpression
/// </summary>
/// <param name="Tpg">Type of parameter to gFn, and type of parameter to result lambda.</param>
/// <param name="Tpf">Type of result of gFn and type of parameter to fFn.</param>
/// <param name="TRes">Type of result of fFn and type of result of result lambda.</param>
/// <param name="fFn">The outer LambdaExpression.</param>
/// <param name="gFn">The inner LambdaExpression.</param>
/// <returns>LambdaExpression representing outer composed with inner</returns>
public static Expression<Func<Tpg, TRes>> Compose<Tpg, Tpf, TRes>(this Expression<Func<Tpf, TRes>> fFn, Expression<Func<Tpg, Tpf>> gFn) =>
Expression.Lambda<Func<Tpg, TRes>>(fFn.Body.Replace(fFn.Parameters[0], gFn.Body), gFn.Parameters[0]);

/// <summary>
/// Replaces an Expression (reference Equals) with another Expression
/// </summary>
/// <param name="orig">The original Expression.</param>
/// <param name="from">The from Expression.</param>
/// <param name="to">The to Expression.</param>
/// <returns>Expression with all occurrences of from replaced with to</returns>
public static Expression Replace(this Expression orig, Expression from, Expression to) => new ReplaceVisitor(from, to).Visit(orig);
}

/// <summary>
/// ExpressionVisitor to replace an Expression (that is Equals) with another Expression.
/// </summary>
public class ReplaceVisitor : ExpressionVisitor {
readonly Expression from;
readonly Expression to;

public ReplaceVisitor(Expression from, Expression to) {
this.from = from;
this.to = to;
}

public override Expression Visit(Expression node) => node == from ? to : base.Visit(node);
}

With this available, your example is straightforward:

Expression<Func<One, Two>> convert = p1 => new Two(p1);
Expression<Func<Two, bool>> predicate = p2 => p2 == new Two();
Expression<Func<One, bool>> filter = predicate.Compose(convert);

However, especially with EF expressions, it may be preferable to use my replacement for Invoke, which handles null propogation, in cases where your arguments may be null. It also uses the same Replace ExpressionVisitor as above:

public static class ExpressionExt2 {   
public static Expression PropagateNull(this Expression orig) => new NullVisitor().Visit(orig);

// Apply: (x => f).Apply(args)
/// <summary>
/// Substitutes an array of Expression args for the parameters of a lambda, returning a new Expression
/// </summary>
/// <param name="e">The original LambdaExpression to "call".</param>
/// <param name="args">The Expression[] of values to substitute for the parameters of e.</param>
/// <returns>Expression representing e.Body with args substituted in</returns>
public static Expression Apply(this LambdaExpression e, params Expression[] args) {
var b = e.Body;

foreach (var pa in e.Parameters.Zip(args, (p, a) => (p, a)))
b = b.Replace(pa.p, pa.a);

return b.PropagateNull();
}
}

/// <summary>
/// ExpressionVisitor to replace a null.member Expression with a null
/// </summary>
public class NullVisitor : System.Linq.Expressions.ExpressionVisitor {
public override Expression Visit(Expression node) {
if (node is MemberExpression nme && nme.Expression is ConstantExpression nce && nce.Value == null)
return Expression.Constant(null, nce.Type.GetMember(nme.Member.Name).Single().GetMemberType());
else
return base.Visit(node);
}
}

public static class MeberInfoExt {
public static Type GetMemberType(this MemberInfo member) {
switch (member) {
case FieldInfo mfi:
return mfi.FieldType;
case PropertyInfo mpi:
return mpi.PropertyType;
case EventInfo mei:
return mei.EventHandlerType;
default:
throw new ArgumentException("MemberInfo must be if type FieldInfo, PropertyInfo or EventInfo", nameof(member));
}
}
}

Given Apply, your Compose is simply:

public static Expression<Func<T1, bool>> Compose<T1, T2>(this Expression<Func<T1, T2>> convertExpr, Expression<Func<T2, bool>> predicate)
=> Expression.Lambda<Func<T1, bool>>(predicate.Apply(convertExpr.Body), convertExpr.Parameters.First());

How to create dynamic entity framework filter expression like ExpressionFuncT, bool

Yes, you can. One approach I've used uses an object which is the same as your return type as a search filter. So if you want to search for the customer name of "Bill", then you set Order.Customer.Name to Bill. Passing that object to a method then applies all applicable searches.

To do this, start by defining a list of searchable fields:

Field<Order>[] Fields;

Fill these by declaring new fields:

var newField = new Field<Order>(o => o.Customer.Name, true, "Customer Name");

The "true" parameter means that it will act as the sort field for the results.

The Field object contains enough information to generate expressions later. It will look like this:

public class Field<T>
{
public Field(Expression<Func<T, object>> field, bool sortField = false, string displayName = null)
{
//get & validate member
MemberExp = field.Body is UnaryExpression ? ((UnaryExpression)field.Body).Operand as MemberExpression
: (MemberExpression)field.Body;

Field = MemberExp?.Member;
if (Field == null) throw new ArgumentException("Field expression is not a member.");

//set field type
switch (Field.MemberType)
{
case MemberTypes.Property:
PropertyInfo p = (PropertyInfo)Field;
FieldType = p.PropertyType;
break;
case MemberTypes.Field:
FieldInfo f = (FieldInfo)Field;
FieldType = f.FieldType;
break;
default:
throw new Exception("Unsupported member type detected.");
}

//store input values
FieldExpression = field;
SortField = sortField;
DisplayName = displayName ?? Field.Name;
}

public bool SortField { get; set; }
public string DisplayName { get; private set; }
public MemberExpression MemberExp { get; private set; }
public Expression<Func<T, object>> FieldExpression { get; private set; }
public Func<T, object> GetValue => FieldExpression.Compile();
public Type FieldType { get; set; }

/// <summary>
/// Gets the full field name, i.e o => o.Customer.CustomerName returns "Customer.CustomerName"
/// </summary>
public string UnqualifiedFieldName
{
get
{
var stringExp = MemberExp.ToString();
var paramEnd = stringExp.IndexOf('.') + 1;
return stringExp.Substring(paramEnd);
}
}
}

Once you have defined all the searchable fields, you will call a method to fetch the results of your search based on the search filters (T) you've collected from the user:

//get the results in ascending order, 10 items per page, first page
var results = GetSearchResults(searchFilters, "ASC", 10, 1);

The method will require you to have a queryable collection of data. I'll assume that you have some method such as context.GetCollection() that retrieves your data. The GetSearchResults method will look like this:

//Returns a filtered dataset based on provided search filters
//searchFilters is an object T which contains the search filters entered.
private List<T> GetSearchResults(T searchFilters, string sortDir = "ASC", int pageSize, int currentPage)
{
IQueryable<T> searchResults = context.GetCollection(); //get your data context here

var filterExpressions = new List<Expression<Func<T, bool>>>();

//Add filters
foreach (var field in Fields)
{
//try to get the search value, ignoring null exceptions because it's much harder
//to check for null objects at multiple levels. Instead the exception tells us there's
//no search value
string searchValue = null;
try
{
searchValue = field.GetValue(searchFilters)?.ToString();
}
catch (NullReferenceException) { }
if (string.IsNullOrWhiteSpace(searchValue)) continue;

//shared expression setup
ParameterExpression param = field.FieldExpression.Parameters.First();
Expression left = field.FieldExpression.Body;
ConstantExpression right = Expression.Constant(searchValue);
Expression body = null;

//create expression for strings so we can use "contains" instead of "equals"
if (field.FieldType == typeof(string))
{
//build the expression body
MethodInfo method = typeof(string).GetMethod("Contains", new[] { typeof(string) });
body = Expression.Call(left, method, right);
}
else
{ //handle expression for all other types
body = Expression.Equal(left, right);
}

//finish expression
Expression<Func<T, bool>> lambda = Expression.Lambda<Func<T, bool>>(body, param);
filterExpressions.Add(lambda);
}

//apply the expressions
searchResults = filterExpressions.Aggregate(searchResults, (current, expression) => current.Where(expression));

//get sort field
Field<T> sortField = Fields.FirstOrDefault(f => f.SortField);
searchResults = searchResults.OrderBy($"{sortField.UnqualifiedFieldName} {sortDir}");

// Get the search results
int count = searchResults.Count();
int maxPage = count / pageSize;
if (maxPage * pageSize < count) maxPage++;
if (currentPage > maxPage) currentPage = maxPage;
int skip = Math.Max(0, (filters.page - 1) * pageSize);
int display = Math.Max(0, Math.Min(count - skip, pageSize));
return searchResults.Skip(skip).Take(display).ToList();
}

This method uses your Field[] array to build expressions for your criteria and apply them to the dataset.

I hope that helps! Let me know if you have any questions.

How to invoke ExpressionFuncEntity, bool against a collection

Try .AsQueryable() method:

return Customers.Values.AsQueryable().Where(expression);

Combining two expressions (ExpressionFuncT, bool)

Well, you can use Expression.AndAlso / OrElse etc to combine logical expressions, but the problem is the parameters; are you working with the same ParameterExpression in expr1 and expr2? If so, it is easier:

var body = Expression.AndAlso(expr1.Body, expr2.Body);
var lambda = Expression.Lambda<Func<T,bool>>(body, expr1.Parameters[0]);

This also works well to negate a single operation:

static Expression<Func<T, bool>> Not<T>(
this Expression<Func<T, bool>> expr)
{
return Expression.Lambda<Func<T, bool>>(
Expression.Not(expr.Body), expr.Parameters[0]);
}

Otherwise, depending on the LINQ provider, you might be able to combine them with Invoke:

// OrElse is very similar...
static Expression<Func<T, bool>> AndAlso<T>(
this Expression<Func<T, bool>> left,
Expression<Func<T, bool>> right)
{
var param = Expression.Parameter(typeof(T), "x");
var body = Expression.AndAlso(
Expression.Invoke(left, param),
Expression.Invoke(right, param)
);
var lambda = Expression.Lambda<Func<T, bool>>(body, param);
return lambda;
}

Somewhere, I have got some code that re-writes an expression-tree replacing nodes to remove the need for Invoke, but it is quite lengthy (and I can't remember where I left it...)


Generalized version that picks the simplest route:

static Expression<Func<T, bool>> AndAlso<T>(
this Expression<Func<T, bool>> expr1,
Expression<Func<T, bool>> expr2)
{
// need to detect whether they use the same
// parameter instance; if not, they need fixing
ParameterExpression param = expr1.Parameters[0];
if (ReferenceEquals(param, expr2.Parameters[0]))
{
// simple version
return Expression.Lambda<Func<T, bool>>(
Expression.AndAlso(expr1.Body, expr2.Body), param);
}
// otherwise, keep expr1 "as is" and invoke expr2
return Expression.Lambda<Func<T, bool>>(
Expression.AndAlso(
expr1.Body,
Expression.Invoke(expr2, param)), param);
}

Starting from .NET 4.0, there is the ExpressionVisitor class which allows you to build expressions that are EF safe.

    public static Expression<Func<T, bool>> AndAlso<T>(
this Expression<Func<T, bool>> expr1,
Expression<Func<T, bool>> expr2)
{
var parameter = Expression.Parameter(typeof (T));

var leftVisitor = new ReplaceExpressionVisitor(expr1.Parameters[0], parameter);
var left = leftVisitor.Visit(expr1.Body);

var rightVisitor = new ReplaceExpressionVisitor(expr2.Parameters[0], parameter);
var right = rightVisitor.Visit(expr2.Body);

return Expression.Lambda<Func<T, bool>>(
Expression.AndAlso(left, right), parameter);
}

private class ReplaceExpressionVisitor
: ExpressionVisitor
{
private readonly Expression _oldValue;
private readonly Expression _newValue;

public ReplaceExpressionVisitor(Expression oldValue, Expression newValue)
{
_oldValue = oldValue;
_newValue = newValue;
}

public override Expression Visit(Expression node)
{
if (node == _oldValue)
return _newValue;
return base.Visit(node);
}
}

How to write Repository method for .ThenInclude in EF Core 2

I found this repository method online and it does exactly what I wanted. Yared's answer was good, but not all the way there.

/// <summary>
/// Gets the first or default entity based on a predicate, orderby delegate and include delegate. This method default no-tracking query.
/// </summary>
/// <param name="selector">The selector for projection.</param>
/// <param name="predicate">A function to test each element for a condition.</param>
/// <param name="orderBy">A function to order elements.</param>
/// <param name="include">A function to include navigation properties</param>
/// <param name="disableTracking"><c>True</c> to disable changing tracking; otherwise, <c>false</c>. Default to <c>true</c>.</param>
/// <returns>An <see cref="IPagedList{TEntity}"/> that contains elements that satisfy the condition specified by <paramref name="predicate"/>.</returns>
/// <remarks>This method default no-tracking query.</remarks>
public TResult GetFirstOrDefault<TResult>(Expression<Func<TEntity, TResult>> selector,
Expression<Func<TEntity, bool>> predicate = null,
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
Func<IQueryable<TEntity>, IIncludableQueryable<TEntity, object>> include = null,
bool disableTracking = true)
{
IQueryable<TEntity> query = _dbSet;
if (disableTracking)
{
query = query.AsNoTracking();
}

if (include != null)
{
query = include(query);
}

if (predicate != null)
{
query = query.Where(predicate);
}

if (orderBy != null)
{
return orderBy(query).Select(selector).FirstOrDefault();
}
else
{
return query.Select(selector).FirstOrDefault();
}
}

Usage:

var affiliate = await affiliateRepository.GetFirstOrDefaultAsync(
predicate: b => b.Id == id,
include: source => source
.Include(a => a.Branches)
.ThenInclude(a => a.Emails)
.Include(a => a.Branches)
.ThenInclude(a => a.Phones));

EF comparing ExpressionFuncT, bool with T

You might use the following

Edited based on the comment below

var students = context.Students.Where(t=>context.Books                               
.Where(exp).Contains(t.Book));

Hope this will help you

Complex edit of a body ExpressionFuncT,bool

From how I see it, what you need is ExpressionVisitor to traverse and modify ExpressionTree. One thing I would change is the way you call Any.
Instead of

e.Entity != null && 
((e.Entity.ListA != null && e.Entity.ListA.Any(...))
|| (e.Entity.ListB != null && e.Entity.ListB.Any(...)))

I'd go for

(
e.Entity != null && e.Entity.ListA != null && e.Entity.ListB != null
? e.Entity.ListA.Union(e.Entity.ListB)
: e.Entity != null && e.Entity.ListA != null
? e.Entity.ListA
: e.Entity.ListB != null
? e.Entity.ListB
: new Entity[0]
).Any(...)

I find it easier to construct ExpressionTree and the outcome will be the same.

Example code:

public class OptionalCallFix : ExpressionVisitor
{
private readonly List<Expression> _conditionalExpressions = new List<Expression>();
private readonly Type _contextType;
private readonly Type _entityType;

private OptionalCallFix(Type contextType, Type entityType)
{
this._contextType = contextType;
this._entityType = entityType;
}

protected override Expression VisitMethodCall(MethodCallExpression node)
{
// Replace Queryable.Union(left, right) call with:
// left == null && right == null ? new Entity[0] : (left == null ? right : (right == null ? left : Queryable.Union(left, right)))
if (node.Method.DeclaringType == typeof(Queryable) && node.Method.Name == nameof(Queryable.Union))
{
Expression left = this.Visit(node.Arguments[0]);
Expression right = this.Visit(node.Arguments[1]);

// left == null
Expression leftIsNull = Expression.Equal(left, Expression.Constant(null, left.Type));

// right == null
Expression rightIsNull = Expression.Equal(right, Expression.Constant(null, right.Type));

// new Entity[0].AsQueryable()
Expression emptyArray = Expression.Call
(
typeof(Queryable),
nameof(Queryable.AsQueryable),
new [] { this._entityType },
Expression.NewArrayInit(this._entityType, new Expression[0])
);

// left == null && right == null ? new Entity[0] : (left == null ? right : (right == null ? left : Queryable.Union(left, right)))
return Expression.Condition
(
Expression.AndAlso(leftIsNull, rightIsNull),
emptyArray,
Expression.Condition
(
leftIsNull,
right,
Expression.Condition
(
rightIsNull,
left,
Expression.Call
(
typeof(Queryable),
nameof(Queryable.Union),
new [] { this._entityType },
left,
Expression.Convert(right, typeof(IEnumerable<>).MakeGenericType(this._entityType))
)
)
)
);
}

return base.VisitMethodCall(node);
}

protected override Expression VisitMember(MemberExpression node)
{
Expression expression = this.Visit(node.Expression);

// Check if expression should be fixed
if (this._conditionalExpressions.Contains(expression))
{
// replace e.XXX with e == null ? null : e.XXX
ConditionalExpression condition = Expression.Condition
(
Expression.Equal(expression, Expression.Constant(null, expression.Type)),
Expression.Constant(null, node.Type),
Expression.MakeMemberAccess(expression, node.Member)
);

// Add fixed expression to the _conditionalExpressions list
this._conditionalExpressions.Add(condition);

return condition;
}

return base.VisitMember(node);
}

protected override Expression VisitParameter(ParameterExpression node)
{
if (node.Type == this._contextType)
{
// Add ParameterExpression to the _conditionalExpressions list
// It is used in VisitMember method to check if expression should be fixed this way
this._conditionalExpressions.Add(node);
}

return base.VisitParameter(node);
}

public static IQueryable<TEntity> Fix<TContext, TEntity>(TContext context, in Expression<Func<TContext, IQueryable<TEntity>>> method)
{
return ((Expression<Func<TContext, IQueryable<TEntity>>>)new OptionalCallFix(typeof(TContext), typeof(TEntity)).Visit(method)).Compile().Invoke(context);
}
}

You can call it like this:

OptionalCallFix.Fix(context, ctx => ctx.Entity.ListA.Union(ctx.ListB));


Related Topics



Leave a reply



Submit