efcore: ArgumentNullException with InMemory database when initializing new DTO in projection

When using a ternary if in combination with an inner .Select(...).ToList(), an ArgumentNullException is thrown. This seems to only happen with an InMemory database and did not appear with SqlServer.

This issue was originally found by testing some code using AutoMapper with nested entities, the original issue can be found here: AutoMapper/AutoMapper#3309

Possibly related to #9223.

Steps to reproduce

Within an InMemory database, execute a query that contains a ternary if which contains another expression using .Select(...).ToList(), like the following:

var alphaDtosQuery = dbContext.Alphas
    .Select(alpha => (alpha.Bravos.Count > 0) ? null : new AlphaDto
    {
        Bravos = alpha.Bravos
            .Select(bravo => new BravoDto())
            .ToList()
    });

var alphaDtos = alphaDtosQuery.ToList();
The following exception is thrown (click to open):
System.ArgumentNullException
  HResult=0x80004003
  Message=Value cannot be null. (Parameter 'bindings')
  Source=System.Linq.Expressions
  StackTrace:
   at System.Linq.Expressions.Expression.ValidateMemberInitArgs(Type type, ReadOnlyCollection`1 bindings)
   at System.Linq.Expressions.Expression.MemberInit(NewExpression newExpression, IEnumerable`1 bindings)
   at System.Linq.Expressions.MemberInitExpression.Update(NewExpression newExpression, IEnumerable`1 bindings)
   at System.Linq.Expressions.ExpressionVisitor.VisitMemberInit(MemberInitExpression node)
   at System.Linq.Expressions.MemberInitExpression.Accept(ExpressionVisitor visitor)
   at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
   at Microsoft.EntityFrameworkCore.InMemory.Query.Internal.InMemoryExpressionTranslatingExpressionVisitor.VisitConditional(ConditionalExpression conditionalExpression)
   at System.Linq.Expressions.ConditionalExpression.Accept(ExpressionVisitor visitor)
   at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
   at Microsoft.EntityFrameworkCore.InMemory.Query.Internal.InMemoryExpressionTranslatingExpressionVisitor.Translate(Expression expression)
   at Microsoft.EntityFrameworkCore.InMemory.Query.Internal.InMemoryProjectionBindingExpressionVisitor.Visit(Expression expression)
   at Microsoft.EntityFrameworkCore.InMemory.Query.Internal.InMemoryProjectionBindingExpressionVisitor.Translate(InMemoryQueryExpression queryExpression, Expression expression)
   at Microsoft.EntityFrameworkCore.InMemory.Query.Internal.InMemoryQueryableMethodTranslatingExpressionVisitor.TranslateSelect(ShapedQueryExpression source, LambdaExpression selector)
   at Microsoft.EntityFrameworkCore.Query.QueryableMethodTranslatingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
   at System.Linq.Expressions.MethodCallExpression.Accept(ExpressionVisitor visitor)
   at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
   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.GetOrAddQueryCore[TFunc](Object cacheKey, Func`1 compiler)
   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.List`1..ctor(IEnumerable`1 collection)
   at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
   at MinimalTest.MinimalTest.TestWithTernary() in D:\Some\File\Path\MinimalTest.cs:line 107

Remove the .Select(...) or the ternary if, and the query will be executed just fine.

A sub 100loc repro can be found here: https://gist.github.com/BaerMitUmlaut/a5feed7af2132b5e5f7d31cd4e7cc08b

Further technical details

EF Core version: 3.0.0 and 3.1.1 Database provider: Microsoft.EntityFrameworkCore.InMemory Target framework: .NET Core 3.0 Operating system: Windows 10 IDE: Visual Studio 2019 Professional 16.4.3

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Reactions: 2
  • Comments: 30 (15 by maintainers)

Commits related to this issue

Most upvoted comments

@JMadle - This issue tracks throwing better exception than ArgumentNullException. The query is not translatable without client eval of projection. Your query is failing not because of Select part, even if you remove it (which is covered in this issue), it will still throw error because p.Child.Id is null in database and you are asking us to assign it to int property, which is not possible. It worked in 3.x because it was a bug, now it is fixed in 5.0. You need to carefully think what do you want to assign as a non-nullable int property if null value was encountered. Since your issue is not relevant to this issue, please file a new issue if you want to continue further discussion. If you think this is bug in EF Core (apart from the part it was working before), then please post a runnable repro code with sample data and explain what is the expected results of the query.

@JMadle - It is not a bug, it is incorrect LINQ query causing it. If Child is null for the parent then p.Child will be null and Id on it will be undefined (or null in database). You want us to assign that to Id property in newly created Child which takes only int values. That value is not defined hence the error. You would either need ternary check before creating new child or define a default value when p.Child.Id is null to assign to Id property.

@yousiftouma From the original post, where AutoMapper has aleardy been removed:

Remove the .Select(…) or the ternary if, and the query will be executed just fine.

What if I cannot remove the ternary (i.e., I have a column in my DB that can be null), but I still have to use Select because I need to map my entities to DTOs?

@BaerMitUmlaut There is no 5.1 planned. The next release after EF Core 5.0 is planned to be EF Core 6.0 in November 2021.

Still in 5.0 rc1.

I get hit by this in unit tests every time I forget to add DoNotAllowNull() in the AutoMapper configuration option for a one-to-one relationship marked with [Required]. It works in SQL Server provider, but not in InMemory.

Any progress on this?