Efcore: Add support for dynamically extended queries as per LinqKit

Created on 9 May 2019  路  5Comments  路  Source: dotnet/efcore

The package LINQKit, a solution for making queries dynamically extendable at runtime, currently sits at half a million downloads for EF Core alone, with much more for traditional EF.

The old uservoice forums had an issue with 114 upvotes, which puts it on the frontpage of that now shuttered forum.

Dynamic queries like PredicateBuilder currently only exist as an external solution (as part of LINQKit). It would be preferable to have this functionality be part of EF Core.
There are a dozen advantages for including this upstream:

  • No breakage of LINQKit with new EF Core Versions
  • Functionality should be part of the Core anyway, since what it offers fits the use cases attempted by EF Core pretty well
  • The maintainers themselves link to the uservoice forum, asking people to upvote inclusion in upstream
  • some parts of LINQKit could be optimized since then not everything would need to be extension methods, there are a lot of possibilities with API and Code Generation
area-query customer-reported needs-design type-enhancement

Most helpful comment

We mostly use LINQKit for the reuse of .Where(....) for special business logic that is reused in allot of places that might need to be changed in the future (or just have a simple\easier name for it),
and .Select(.....) for selecting some common models that are reused in a bunch of places

if only that feature would make it into EF Core itself i would be very happy

also i have noticed that EF Core is not able to optimize the sql generated when a select\where (expression) has been invoked using LINQKit (we have a bunch of heavy queries so i open sql profiler allot and look at sql being generated)

All 5 comments

Note from triage: we should investigate what makes sense in this space. For example, that might be making sure EF Core works well with LINQKit and making it easier for the two to keep working across releases.

We mostly use LINQKit for the reuse of .Where(....) for special business logic that is reused in allot of places that might need to be changed in the future (or just have a simple\easier name for it),
and .Select(.....) for selecting some common models that are reused in a bunch of places

if only that feature would make it into EF Core itself i would be very happy

also i have noticed that EF Core is not able to optimize the sql generated when a select\where (expression) has been invoked using LINQKit (we have a bunch of heavy queries so i open sql profiler allot and look at sql being generated)

And now i'm blocked from upgrading to EF Core 3.0 #https://github.com/scottksmith95/LINQKit/issues/95

Official support for the feature i meantioned above would remove my dependency on LINQKit (I really like the library but built in support would be better of course :smile: )

well I think PredicateBuilder is something that people want. But most functionality of that is already in EFCore. you can AND where parts together when writing code like that:

var query = ...
foreach (...)
{
query = query.Where(...)
}

However OR'ing is completly missing. Probably because it would need a good api design to not confuse users.

WhereOr Expression can be simulated like that:

    internal class RangeMemberExpressionModifier : ExpressionVisitor
    {
        private readonly ParameterExpression _parameterExpression;

        public RangeMemberExpressionModifier(ParameterExpression parameterExpression)
        {
            _parameterExpression = parameterExpression;
        }

        public Expression Modify(Expression expression)
        {
            return Visit(expression);
        }

        protected override Expression VisitParameter(ParameterExpression node)
        {
            return _parameterExpression;
        }
    }
    public class QueryPartOrBuilder<TSource>
    {
        internal readonly List<Expression<Func<TSource, bool>>> PredicateLists = new List<Expression<Func<TSource, bool>>>();

        public void Add(Expression<Func<TSource, bool>> predicate)
        {
            PredicateLists.Add(predicate);
        }
    }

        public static IQueryable<TSource> WhereOr<TSource>(this IQueryable<TSource> query, QueryPartOrBuilder<TSource> orBuilder)
        {
            var predicates = orBuilder.PredicateLists;

            if (predicates.Count == 0)
            {
                return query;
            }

            if (predicates.Count == 1)
            {
                return query.Where(predicates[0]);
            }

            var parameter = predicates.FirstOrDefault()?.Parameters.FirstOrDefault();
            if (parameter == null)
            {
                throw new ArgumentException("invalid parameter name");
            }

            var modifier = new RangeMemberExpressionModifier(parameter);
            var aggregatedExpressions = predicates.Aggregate<Expression>((first, second) =>
            {
                Expression me1;
                switch (first)
                {
                    case LambdaExpression le:
                        me1 = modifier.Modify(le.Body as BinaryExpression);
                        break;
                    case BinaryExpression be:
                        me1 = be;
                        break;
                    default:
                        throw new ArgumentException("arguments should not be null");
                }

                var body2 = (second as LambdaExpression)?.Body;
                if (body2 == null)
                {
                    throw new ArgumentException("arguments should not be null");
                }

                var me2 = modifier.Modify(body2 as BinaryExpression);
                return Expression.OrElse(me1, me2);
            });

            return query.Where(Expression.Lambda<Func<TSource, bool>>(aggregatedExpressions, parameter));
        }

Schmitch's code works perfectly! It would it be nice to have a similar WhereOr function in EF Core.

Was this page helpful?
0 / 5 - 0 ratings