Skip to content

Count with Take or Skip throws InvalidCastException #18341

@Kukkimonsuta

Description

@Kukkimonsuta

Query throws InvalidCastException when Take or Skip is used after Select which includes ternary expression with join (in the sample the condition doesn't really make sense since Country doesn't have type hierarchy registered, but effect is the same).

This seems to be contination of #16722

context.Cities
    .Select(c => new
    {
        Name = c.Name,
        Country = c.Country is Country ?
            new
            {
                Name = c.Country.Name,
            } :
            null,
    })
    .Take(20)
    .Count();
Unhandled exception. System.InvalidCastException: Unable to cast object of type 'Microsoft.EntityFrameworkCore.Query.SqlExpressions.SqlFunctionExpression' to type 'System.Linq.Expressions.ConstantExpression'.
   at Microsoft.EntityFrameworkCore.Query.RelationalShapedQueryCompilingExpressionVisitor.RelationalProjectionBindingRemovingExpressionVisitor.GetProjectionIndex(ProjectionBindingExpression projectionBindingExpression)
   at Microsoft.EntityFrameworkCore.Query.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.VisitBinary(BinaryExpression node)
   at Microsoft.EntityFrameworkCore.Query.RelationalShapedQueryCompilingExpressionVisitor.RelationalProjectionBindingRemovingExpressionVisitor.VisitBinary(BinaryExpression binaryExpression)
   at System.Linq.Expressions.BinaryExpression.Accept(ExpressionVisitor visitor)
   at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
   at System.Dynamic.Utils.ExpressionVisitorUtils.VisitBlockExpressions(ExpressionVisitor visitor, BlockExpression block)
   at System.Linq.Expressions.ExpressionVisitor.VisitBlock(BlockExpression node)
   at System.Linq.Expressions.BlockExpression.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.Query.RelationalShapedQueryCompilingExpressionVisitor.VisitShapedQueryExpression(ShapedQueryExpression shapedQueryExpression)
   at Microsoft.EntityFrameworkCore.Query.ShapedQueryCompilingExpressionVisitor.VisitExtension(Expression extensionExpression)
   at System.Linq.Expressions.Expression.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 System.Linq.Queryable.Count[TSource](IQueryable`1 source)
   at test_efcore_count.Program.Main(String[] args) in D:\Projects\test-efcore-count\test-efcore-count\Program.cs:line 112
   at test_efcore_count.Program.<Main>(String[] args)

Steps to reproduce

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;

namespace test_efcore_count
{
    class Country
    {
        public int Id { get; set; }
        public string Name { get; set; }

        public ICollection<City> Cities { get; set; }
    }

    class City
    {
        public int Id { get; set; }
        public string Name { get; set; }

        public Country Country { get; set; }
        public int CountryId { get; set; }
    }

    class MapContext : DbContext
    {
        public MapContext(DbContextOptions<MapContext> options)
            : base(options)
        { }

        public DbSet<Country> Countries { get; set; }
        public DbSet<City> Cities { get; set; }
    }

    class Program
    {
        static async Task Main(string[] args)
        {
            var serviceCollection = new ServiceCollection();
            serviceCollection.AddLogging(x => x.AddConsole());
            serviceCollection.AddDbContext<MapContext>(dbContextBuilder =>
            {
                dbContextBuilder.UseSqlServer("Server=.;Initial Catalog=test-efcore-count;Integrated Security=True");
            });

            using var serviceProvider = serviceCollection.BuildServiceProvider();

            Console.WriteLine("Preparing database..");

            using (var scope = serviceProvider.CreateScope())
            using (var context = scope.ServiceProvider.GetRequiredService<MapContext>())
            {
                await context.Database.EnsureDeletedAsync();
                await context.Database.EnsureCreatedAsync();
            }

            Console.WriteLine("Inserting data..");

            using (var scope = serviceProvider.CreateScope())
            using (var context = scope.ServiceProvider.GetRequiredService<MapContext>())
            {
                context.Countries.Add(new Country()
                {
                    Name = "Czech Republic",
                    Cities = new[]
                    {
                        new City() { Name = "Prague" },
                        new City() { Name = "Brno" },
                    }
                });
                context.Countries.Add(new Country()
                {
                    Name = "Spain",
                    Cities = new[]
                    {
                        new City() { Name = "Madrid" },
                        new City() { Name = "Barcelona" },
                    }
                });

                await context.SaveChangesAsync();
            }

            Console.WriteLine("Printing data..");

            using (var scope = serviceProvider.CreateScope())
            using (var context = scope.ServiceProvider.GetRequiredService<MapContext>())
            {
                var query = context.Cities
                    .Select(c => new
                    {
                        Name = c.Name,
                        Country = c.Country is Country ?
                            new
                            {
                                Name = c.Country.Name,
                            } :
                            null,
                    });

                // works
                var list = query.Take(20).ToArray();
                foreach (var item in list)
                {
                    Console.WriteLine($"{item.Name} in {item.Country.Name}");
                }

                // error
                var total = query.Take(20).Count();
                Console.WriteLine($"Database contains {total} cities");
            }
        }
    }
}

Further technical details

EF Core version: 3.0.0
Database provider: Microsoft.EntityFrameworkCore.SqlServer
Target framework: .NET Core 3.0
Operating system: Windows 10 1903
IDE: Visual Studio 2019 16.3.4

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions