Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Count() throws InvalidCastException in specific cases #16722

Closed
fschlaef opened this issue Jul 24, 2019 · 12 comments · Fixed by #18193
Closed

Count() throws InvalidCastException in specific cases #16722

fschlaef opened this issue Jul 24, 2019 · 12 comments · Fixed by #18193
Labels
area-test closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. customer-reported type-bug
Milestone

Comments

@fschlaef
Copy link

fschlaef commented Jul 24, 2019

When using .Count() following specific operations, the following exception is thrown :

System.InvalidCastException: Unable to cast object of type 'Microsoft.EntityFrameworkCore.Relational.Query.Pipeline.SqlExpressions.SqlFunctionExpression' to type 'System.Linq.Expressions.ConstantExpression'.
   at Microsoft.EntityFrameworkCore.Relational.Query.Pipeline.RelationalShapedQueryCompilingExpressionVisitor.RelationalProjectionBindingRemovingExpressionVisitor.GetProjectionIndex(ProjectionBindingExpression projectionBindingExpression)
   at Microsoft.EntityFrameworkCore.Relational.Query.Pipeline.RelationalShapedQueryCompilingExpressionVisitor.RelationalProjectionBindingRemovingExpressionVisitor.VisitExtension(Expression extensionExpression)
   at System.Linq.Expressions.Expression.Accept(ExpressionVisitor visitor)
   at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
   at System.Linq.Expressions.ExpressionVisitor.VisitLambda[T](Expression`1 node)
   at System.Linq.Expressions.Expression`1.Accept(ExpressionVisitor visitor)
   at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
   at Microsoft.EntityFrameworkCore.Relational.Query.Pipeline.RelationalShapedQueryCompilingExpressionVisitor.VisitShapedQueryExpression(ShapedQueryExpression shapedQueryExpression)
   at Microsoft.EntityFrameworkCore.Query.Pipeline.ShapedQueryCompilingExpressionVisitor.VisitExtension(Expression extensionExpression)
   at System.Linq.Expressions.Expression.Accept(ExpressionVisitor visitor)
   at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
   at Microsoft.EntityFrameworkCore.Query.Pipeline.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 System.Linq.Queryable.Count[TSource](IQueryable`1 source)
   at Software.Controllers.ApplicationStatusController.Crash() in C:\software\src\Software\Controllers\ApplicationStatusController.cs:line 142
   at lambda_method(Closure , Object , Object[] )
   at Microsoft.Extensions.Internal.ObjectMethodExecutor.Execute(Object target, Object[] parameters)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.SyncActionResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeActionMethodAsync()
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeNextActionFilterAsync()
--- End of stack trace from previous location where exception was thrown ---
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeInnerFilterAsync()
--- End of stack trace from previous location where exception was thrown ---
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResourceFilter>g__Awaited|24_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResourceExecutedContextSealed context)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeFilterPipelineAsync()
--- End of stack trace from previous location where exception was thrown ---
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Logged|17_1(ResourceInvoker invoker)
   at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
   at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.ResponseCompression.ResponseCompressionMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.Diagnostics.StatusCodePagesMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)

Steps to reproduce

There are multiple ways to reproduce this error :

_context.Client
    .Select(c => new
    {
        Id = c.Id.ToString("000000")
    })
    .Count();
_context.Client
    .Select(c => c.Id.ToString("000000"))
    .Count();
_context.Client
    .Select(c => new
    {
        Id = c.Id.CodeFormat()
    })
    .Count();

CodeFormat being simply this :

public static class NamingTools
{

    public static string CodeFormat(this int str)
    {
        return str.ToString();
    }
}

Weirdly enough, doing .ToList() doesn't throw.

_context.Client
    .Select(c => new
    {
        Id = c.Id.ToString("000000")
    })
    .ToList(); // This works

Further technical details

EF Core version: 3.0.0-preview7.19365.7
Database Provider: Microsoft.EntityFrameworkCore.SqlServer
Operating system: Windows 10 x64
IDE: Visual Studio 2019 16.2.0 Preview 4.0

@ajcvickers
Copy link
Member

@fschlaef All these are cases where the projection cannot (currently) be translated to SQL. In 3.0, we allow a final projection to be evaluated on the client, but then when the aggregate (Count) operator is applied the projection is no longer the final projection.

The plan for 3.0 is to throw an exception with a clearer message for this. (@smitpatel #16133 or is #15937 this case?)

For the future, putting this on the backlog to investigate silently dropping the projection when discarded by the final query anyway. However, we need to be careful about side-effects.

@ajcvickers ajcvickers added this to the Backlog milestone Jul 26, 2019
@smitpatel
Copy link
Member

#15937

@smitpatel
Copy link
Member

This has been fixed in my current work of Nav rewrite.

@ajcvickers
Copy link
Member

@smitpatel Merge before Monday?

:trollface: :trollface: :trollface: :trollface: :trollface: :trollface:

The mountain isn't going anywhere.... 😉

:trollface: :trollface: :trollface: :trollface: :trollface: :trollface:

@smitpatel
Copy link
Member

🌨 ⛈ 🌩 🌧 keep on moving...

@davidroth
Copy link
Contributor

davidroth commented Aug 22, 2019

This has been fixed in my current work of Nav rewrite.

@smitpatel @ajcvickers

What does this statement refer to? Is it fixed or not? According to the issue milestone it is not?

I will put another +1 on this issue because this will be a big problem for everyone implementing a basic paginated list.

Example:

var entries = 
(from c in context.Customers
select new CustomerListItem
{
  c.Id,
  c.Name,
  ClientEvalProperty = // Some client eval expression
});

var viewModel = new ViewModel
{
   Entries = entries.Skip(..).Take(..).ToList(),
   Total = entries.Count() // Boom => Crash!
};

@smitpatel
Copy link
Member

@davidroth - We believe that this issue has been fixed in latest daily builds.

@fschlaef
Copy link
Author

fschlaef commented Aug 23, 2019

@smitpatel Confirmed working with 3.0.0-rc1 👍

@BickelLukas
Copy link

@smitpatel Same here 👍 Works in 3.0.0-rc1.19424.9

@roji roji removed this from the Backlog milestone Aug 26, 2019
@ajcvickers
Copy link
Member

Need to add a test for this.

@ajcvickers ajcvickers added this to the Backlog milestone Aug 26, 2019
@ajcvickers ajcvickers added area-test and removed type-bug verify-fixed This issue is likely fixed in new query pipeline. labels Aug 26, 2019
@ajcvickers ajcvickers modified the milestones: Backlog, 3.1.0 Sep 4, 2019
@smitpatel smitpatel removed their assignment Sep 12, 2019
@maumar maumar added the closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. label Oct 2, 2019
@JohnGalt1717
Copy link

JohnGalt1717 commented Mar 18, 2020

This still fails: Unable to cast object of type 'Microsoft.EntityFrameworkCore.Query.SqlExpressions.SqlFunctionExpression' to type 'System.Linq.Expressions.ConstantExpression'.

Specifically with .Distinct() which obviously shouldn't be stopping any query from being executed properly. Or to put another way, if a query can execute fine and materialize as the last step without .Distinct, it certainly can with it since it's just SELECT DISTINCT ... instead of SELECT ...

@gitrndlab
Copy link

Hi, I confirm problem still exists - is Exception for Count with Select().Distinct() .... Please FIX IT!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-test closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. customer-reported type-bug
Projects
None yet
Development

Successfully merging a pull request may close this issue.

9 participants