Efcore: Something Weird with LastOrDefault()

Created on 16 Dec 2018  路  2Comments  路  Source: dotnet/efcore

If just found Something Weird in a query.
I use .Net Core x64 SDK 2.1.500 on windows 10 x64

I'm trying to execute this request:

 List<TicketListDto> dtos = await _ctx.Tickets.Include(i => i.Elements).AsNoTracking()
                .Select(s => new TicketListDto
                {
                    Id = s.Id,
                    Content = s.Elements.First().Content,
                    CreationDate = s.CreationDate,
                    LastModificationDate = s.Elements.LastOrDefault().StartDate,
                    Priority = (TicketPriority)s.Priority,
                    Title = s.Title,
                    Type = (TicketType)s.Type
                })
                .ToListAsync();

I got this error:

System.InvalidOperationException
  HResult=0x80131509
  Message=Rewriting child expression from type 'System.DateTime' to type 'System.Collections.Generic.IAsyncEnumerable`1[System.DateTime]' is not allowed, because it would change the meaning of the operation. If this is intentional, override 'VisitUnary' and change it to allow this rewrite.
  Source=System.Linq.Expressions
  StackTrace:
   at System.Linq.Expressions.ExpressionVisitor.ValidateChildType(Type before, Type after, String methodName)
   at System.Linq.Expressions.ExpressionVisitor.ValidateUnary(UnaryExpression before, UnaryExpression after)
   at System.Linq.Expressions.ExpressionVisitor.VisitUnary(UnaryExpression node)
   at System.Linq.Expressions.UnaryExpression.Accept(ExpressionVisitor visitor)
   at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
   at Microsoft.EntityFrameworkCore.Query.ExpressionVisitors.RelationalProjectionExpressionVisitor.Visit(Expression expression)
   at System.Linq.Expressions.ExpressionVisitor.VisitMemberAssignment(MemberAssignment node)
   at System.Linq.Expressions.ExpressionVisitor.VisitMemberBinding(MemberBinding node)
   at System.Linq.Expressions.ExpressionVisitor.Visit[T](ReadOnlyCollection`1 nodes, Func`2 elementVisitor)
   at System.Linq.Expressions.ExpressionVisitor.VisitMemberInit(MemberInitExpression node)
   at Microsoft.EntityFrameworkCore.Query.ExpressionVisitors.RelationalProjectionExpressionVisitor.VisitMemberInit(MemberInitExpression memberInitExpression)
   at Microsoft.EntityFrameworkCore.Query.ExpressionVisitors.RelationalProjectionExpressionVisitor.Visit(Expression expression)
   at Microsoft.EntityFrameworkCore.Query.EntityQueryModelVisitor.VisitSelectClause(SelectClause selectClause, QueryModel queryModel)
   at Microsoft.EntityFrameworkCore.Query.RelationalQueryModelVisitor.VisitSelectClause(SelectClause selectClause, QueryModel queryModel)
   at Remotion.Linq.Clauses.SelectClause.Accept(IQueryModelVisitor visitor, QueryModel queryModel)
   at Remotion.Linq.QueryModelVisitorBase.VisitQueryModel(QueryModel queryModel)
   at Microsoft.EntityFrameworkCore.Query.EntityQueryModelVisitor.VisitQueryModel(QueryModel queryModel)
   at Microsoft.EntityFrameworkCore.Query.RelationalQueryModelVisitor.VisitQueryModel(QueryModel queryModel)
   at Microsoft.EntityFrameworkCore.Query.EntityQueryModelVisitor.CreateAsyncQueryExecutor[TResult](QueryModel queryModel)
   at Microsoft.EntityFrameworkCore.Storage.Database.CompileAsyncQuery[TResult](QueryModel queryModel)
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.CompileAsyncQueryCore[TResult](Expression query, IQueryModelGenerator queryModelGenerator, IDatabase database)
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.<>c__DisplayClass22_0`1.<CompileAsyncQuery>b__0()
   at Microsoft.EntityFrameworkCore.Query.Internal.CompiledQueryCache.GetOrAddQueryCore[TFunc](Object cacheKey, Func`1 compiler)
   at Microsoft.EntityFrameworkCore.Query.Internal.CompiledQueryCache.GetOrAddAsyncQuery[TResult](Object cacheKey, Func`1 compiler)
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.CompileAsyncQuery[TResult](Expression query)
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.ExecuteAsync[TResult](Expression query)
   at Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryProvider.ExecuteAsync[TResult](Expression expression)
   at Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable`1.System.Collections.Generic.IAsyncEnumerable<TResult>.GetEnumerator()
   at System.Linq.AsyncEnumerable.<Aggregate_>d__6`3.MoveNext()
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
   at ProjectManagement.Services.TicketService.<GetTicketListDtoAsync>d__2.MoveNext() in C:\Team Foundation\ProjectManagement\ProjectManagement.Services\TicketService.cs:line 29

but if I replace

LastModificationDate = s.Elements.LastOrDefault().StartDate,
or
LastModificationDate = s.Elements.Last().StartDate,

with

LastModificationDate = s.Elements.FirstOrDefault().StartDate,
or
LastModificationDate = s.Elements.First().StartDate,

It works fine.

Is someone have an idea ?

Thanks

closed-fixed customer-reported type-bug

Most helpful comment

@JulienM28, just tip. If you need last element, you have to know how they should be ordered. I assume that EF guys can transform your query to

LastModificationDate = s.Elements.OrderByDescending(e => e.SomeId).FirstOrDefault().StartDate,

It鈥檚 because of RDBMS nature. But ensure that this what you need. Without ordering FirstOrDefault or LastOrDefault requests have undefined behavior.

All 2 comments

@JulienM28, just tip. If you need last element, you have to know how they should be ordered. I assume that EF guys can transform your query to

LastModificationDate = s.Elements.OrderByDescending(e => e.SomeId).FirstOrDefault().StartDate,

It鈥檚 because of RDBMS nature. But ensure that this what you need. Without ordering FirstOrDefault or LastOrDefault requests have undefined behavior.

The main cause of error was client eval of LastOrDefault which happened because of lack of ordering.
In 3.0, we throw exception for above case as translation failure. If an order by is added before LastOrDefault then we translate it to server successfully.

Added test to cover later scenario in #17932

Was this page helpful?
0 / 5 - 0 ratings