Skip to content

SelectMany on ICollection property throws ArgumentException on EF Core 7 #701

@jbhelm

Description

@jbhelm

1. Description

When calling SelectMany() within a Dynamic LINQ expression for an ICollection<> property, an ArgumentException is thrown when executed on EF Core 7 LINQ to Entities. Note that it does work on LINQ to Objects, LINQ to Entities with EF 6 (classic), and on EF Core 7 if the properties are declared as IEnumerable<> instead of ICollection<>.

2. Exception

System.ArgumentException
  HResult=0x80070057
  Message=Expression of type 'System.Linq.Expressions.Expression`1[System.Func`2[DynamicLinqEfCoreExample.Program+Child,System.Collections.Generic.ICollection`1[DynamicLinqEfCoreExample.Program+Grandchild]]]' cannot be used for parameter of type 'System.Linq.Expressions.Expression`1[System.Func`2[DynamicLinqEfCoreExample.Program+Child,System.Collections.Generic.IEnumerable`1[DynamicLinqEfCoreExample.Program+Grandchild]]]' of method 'System.Linq.IQueryable`1[DynamicLinqEfCoreExample.Program+Grandchild] SelectMany[Child,Grandchild](System.Linq.IQueryable`1[DynamicLinqEfCoreExample.Program+Child], System.Linq.Expressions.Expression`1[System.Func`2[DynamicLinqEfCoreExample.Program+Child,System.Collections.Generic.IEnumerable`1[DynamicLinqEfCoreExample.Program+Grandchild]]])' (Parameter 'arg1')
  Source=System.Linq.Expressions
  StackTrace:
   at System.Dynamic.Utils.ExpressionUtils.ValidateOneArgument(MethodBase method, ExpressionType nodeKind, Expression arguments, ParameterInfo pi, String methodParamName, String argumentParamName, Int32 index)
   at System.Linq.Expressions.Expression.Call(Expression instance, MethodInfo method, Expression arg0, Expression arg1)
   at System.Linq.Expressions.Expression.Call(Expression instance, MethodInfo method, IEnumerable`1 arguments)
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryableMethodNormalizingExpressionVisitor.TryConvertEnumerableToQueryable(MethodCallExpression methodCallExpression)
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryableMethodNormalizingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
   at System.Linq.Expressions.ExpressionVisitor.VisitLambda[T](Expression`1 node)
   at System.Linq.Expressions.ExpressionVisitor.VisitUnary(UnaryExpression node)
   at System.Dynamic.Utils.ExpressionVisitorUtils.VisitArguments(ExpressionVisitor visitor, IArgumentProvider nodes)
   at System.Linq.Expressions.ExpressionVisitor.VisitMethodCall(MethodCallExpression node)
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryableMethodNormalizingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryableMethodNormalizingExpressionVisitor.Normalize(Expression expression)
   at Microsoft.EntityFrameworkCore.Query.QueryTranslationPreprocessor.NormalizeQueryableMethod(Expression expression)
   at Microsoft.EntityFrameworkCore.Query.QueryTranslationPreprocessor.Process(Expression query)
   at Microsoft.EntityFrameworkCore.InMemory.Query.Internal.InMemoryQueryTranslationPreprocessor.Process(Expression query)
   at Microsoft.EntityFrameworkCore.Query.QueryCompilationContext.CreateQueryExecutor[TResult](Expression query)
   at Microsoft.EntityFrameworkCore.Storage.Database.CompileQuery[TResult](Expression query, Boolean async)
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.CompileQueryCore[TResult](IDatabase database, Expression query, IModel model, Boolean async)
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.<>c__DisplayClass9_0`1.<Execute>b__0()
   at Microsoft.EntityFrameworkCore.Query.Internal.CompiledQueryCache.GetOrAddQuery[TResult](Object cacheKey, Func`1 compiler)
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.Execute[TResult](Expression query)
   at Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryProvider.Execute[TResult](Expression expression)
   at Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable`1.GetEnumerator()
   at System.Collections.Generic.LargeArrayBuilder`1.AddRange(IEnumerable`1 items)
   at System.Collections.Generic.EnumerableHelpers.ToArray[T](IEnumerable`1 source)
   at System.Linq.Dynamic.Core.DynamicEnumerableExtensions.CastToArray[T](IEnumerable source)
   at System.Linq.Dynamic.Core.DynamicEnumerableExtensions.ToDynamicArray(IEnumerable source)
   at DynamicLinqEfCoreExample.Program.Main(String[] args) in C:\Users\JoshuaHelm\dev\DynamicLinqEfCoreExample\Program.cs:line 40

3. Fiddle or Project

using Microsoft.EntityFrameworkCore;
using System.Linq.Dynamic.Core;

namespace DynamicLinqEfCoreExample
{
    internal class Program
    {
        static void Main(string[] args)
        {
            var options = new DbContextOptionsBuilder<ExampleContext>().UseInMemoryDatabase(databaseName: "Example").Options;

            using (var context = new ExampleContext(options))
            {
                Root root = new()
                {
                    Id = 1,
                    Children = new Child[] {
                        new() {
                            Id = 2,
                            Grandchildren = new Grandchild[]
                            {
                                new()
                                {
                                    Id = 3,
                                }
                            }
                        }
                    }
                };
                context.Roots.Add(root);
                context.SaveChanges();

                // LINQ to Objects (works)
                var data = new[] { root }.AsQueryable();
                var a = data.Select(r => r.Children.SelectMany(c => c.Grandchildren)).ToArray();    // <-- works
                var b = data.Select("Children.SelectMany(Grandchildren)").ToDynamicArray();         // <-- works

                // LINQ to Entities (fails)
                var c = context.Roots.Select(r => r.Children.SelectMany(c => c.Grandchildren)).ToArray();    // <-- works
                var d = context.Roots.Select("Children.SelectMany(Grandchildren)").ToDynamicArray();         // <-- throws error (works if Children and Grandchildren properties are defined as IEnumerable instead of ICollection)
            }

        }


        public class Root
        {
            public int Id { get; set; }
            public ICollection<Child> Children { get; set; } = new HashSet<Child>();    // <-- dynamic LINQ query works if this and Child.Grandchildren are IEnumerable instead of ICollection
        }

        public class Child
        {
            public int Id { get; set; }
            public ICollection<Grandchild> Grandchildren { get; set; } = new HashSet<Grandchild>(); // <-- dynamic LINQ query works if this and Root.Children are IEnumerable instead of ICollection
        }

        public class Grandchild
        {
            public int Id { get; set; }
        }


        public class ExampleContext : DbContext
        {
            public ExampleContext() : base()
            {
            }

            public ExampleContext(DbContextOptions<ExampleContext> options)
                : base(options)
            {
            }

            public DbSet<Root> Roots { get; set; }

        }
    }
}

4. Any further technical details

  • System.Linq.Dynamic.Core 1.3.2
  • Microsoft.EntityFrameworkCore 7.0.5
  • .NET 7.0

Thanks!

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions