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
- Query: Add regression test for #19726 Resolves #19726 — committed to dotnet/efcore by smitpatel 3 years ago
- Query: Add regression test for #19726 (#26002) Resolves #19726 — committed to dotnet/efcore by smitpatel 3 years ago
@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.Idis null in database and you are asking us to assign it tointproperty, 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
Childis null for the parent thenp.Childwill be null andIdon it will be undefined (or null in database). You want us to assign that toIdproperty in newly created Child which takes onlyintvalues. That value is not defined hence the error. You would either need ternary check before creating new child or define a default value whenp.Child.Idis null to assign to Id property.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?