dotnet / efcore

EF Core is a modern object-database mapper for .NET. It supports LINQ queries, change tracking, updates, and schema migrations.
https://docs.microsoft.com/ef/
MIT License
13.76k stars 3.18k forks source link

Bug when using Take() #19207

Closed gldfdp closed 4 years ago

gldfdp commented 4 years ago

When I use the following query everything is ok :

            var usersQuery = this.DataContext.Users.Select(u => new
            {
                User = u,
                RoleName = u.UserRoles.Select(ur => ur.Role.Name),
                u.Team
            }).Take(20);

            var users = await usersQuery.ToArrayAsync();

But when I want to take only the 20 first results with Take() :

            var usersQuery = this.DataContext.Users.Select(u => new
            {
                User = u,
                RoleName = u.UserRoles.Select(ur => ur.Role.Name),
                u.Team
            }).Take(20); // <-- the change is here

            var users = await usersQuery.ToArrayAsync();

The following exception occurs :

ArgumentNullException: Value cannot be null. (Parameter 'key') System.Collections.Generic.Dictionary<TKey, TValue>.FindEntry(TKey key) System.Collections.Generic.Dictionary<TKey, TValue>.get_Item(TKey key) Microsoft.EntityFrameworkCore.Query.SqlExpressions.SelectExpression.GetMappedProjection(ProjectionMember projectionMember) Microsoft.EntityFrameworkCore.Query.Internal.RelationalProjectionBindingExpressionVisitor.VisitExtension(Expression extensionExpression) System.Linq.Expressions.Expression.Accept(ExpressionVisitor visitor) Microsoft.EntityFrameworkCore.Query.Internal.RelationalProjectionBindingExpressionVisitor.Visit(Expression expression) Microsoft.EntityFrameworkCore.Query.Internal.RelationalProjectionBindingExpressionVisitor.VisitNew(NewExpression newExpression) System.Linq.Expressions.NewExpression.Accept(ExpressionVisitor visitor) Microsoft.EntityFrameworkCore.Query.Internal.RelationalProjectionBindingExpressionVisitor.Visit(Expression expression) Microsoft.EntityFrameworkCore.Query.Internal.RelationalProjectionBindingExpressionVisitor.Translate(SelectExpression selectExpression, Expression expression) Microsoft.EntityFrameworkCore.Query.RelationalQueryableMethodTranslatingExpressionVisitor.TranslateSelect(ShapedQueryExpression source, LambdaExpression selector) Microsoft.EntityFrameworkCore.Query.QueryableMethodTranslatingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression) Microsoft.EntityFrameworkCore.Query.RelationalQueryableMethodTranslatingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression) System.Linq.Expressions.MethodCallExpression.Accept(ExpressionVisitor visitor) Microsoft.EntityFrameworkCore.Query.QueryCompilationContext.CreateQueryExecutor(Expression query) Microsoft.EntityFrameworkCore.Storage.Database.CompileQuery(Expression query, bool async) Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.CompileQueryCore(IDatabase database, Expression query, IModel model, bool async) Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler+<>cDisplayClass12_0.b0() Microsoft.EntityFrameworkCore.Query.Internal.CompiledQueryCache.GetOrAddQueryCore(object cacheKey, Func<Func<QueryContext, TFunc>> compiler) Microsoft.EntityFrameworkCore.Query.Internal.CompiledQueryCache.GetOrAddQuery(object cacheKey, Func<Func<QueryContext, TResult>> compiler) Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.ExecuteAsync(Expression query, CancellationToken cancellationToken) Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryProvider.ExecuteAsync(Expression expression, CancellationToken cancellationToken) Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable.GetAsyncEnumerator(CancellationToken cancellationToken) System.Runtime.CompilerServices.ConfiguredCancelableAsyncEnumerable.GetAsyncEnumerator() Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.ToListAsync(IQueryable source, CancellationToken cancellationToken) Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.ToArrayAsync(IQueryable source, CancellationToken cancellationToken) Web.Controllers.UsersController.GetUsers() in UsersController.cs + var users = await usersQuery.ToArrayAsync(); lambda_method(Closure , object ) Microsoft.Extensions.Internal.ObjectMethodExecutorAwaitable+Awaiter.GetResult() Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor+AwaitableObjectResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, object controller, object[] arguments) System.Threading.Tasks.ValueTask.get_Result() System.Runtime.CompilerServices.ValueTaskAwaiter.GetResult() Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.gAwaited|12_0(ControllerActionInvoker invoker, ValueTask actionResultValueTask) Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.gAwaited|10_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, object state, bool isCompleted) Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context) Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(ref State next, ref Scope scope, ref object state, ref bool isCompleted) Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeInnerFilterAsync() Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Awaited|24_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, object state, bool isCompleted) Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResourceExecutedContextSealed context) Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(ref State next, ref Scope scope, ref object state, ref bool isCompleted) Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeFilterPipelineAsync() Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Logged|17_1(ResourceInvoker invoker) Microsoft.AspNetCore.Builder.RouterMiddleware.Invoke(HttpContext httpContext) Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context) Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context) Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)

Steps to reproduce

ApplicationUserRole

    public class ApplicationUserRole : IdentityUserRole<string>
    {

        public ApplicationUser User { get; set; }

        public IdentityRole Role { get; set; }
    }

DataContext

public class DataContext : IdentityDbContext<ApplicationUser, IdentityRole, string,
        IdentityUserClaim<string>, ApplicationUserRole, IdentityUserLogin<string>, IdentityRoleClaim<string>, IdentityUserToken<string>>,
        IDataContextWithEmails<Email>
    {
        public DataContext(DbContextOptions<DataContext> options)
          : base(options)
        {

        }

        public DbSet<Email> Emails { get; set; }

        public DbSet<Team> Teams { get; set; }

        protected override void OnModelCreating(ModelBuilder builder)
        {
            base.OnModelCreating(builder);

            builder.Entity<ApplicationUserRole>()
                .HasOne(a => a.User).WithMany(u => u.UserRoles).HasForeignKey(ur => ur.UserId);

            builder.Entity<ApplicationUserRole>().HasKey(aur => new { aur.UserId, aur.RoleId });
            builder.Entity<ApplicationUserRole>()
                .HasOne(a => a.Role).WithMany().HasForeignKey(ur => ur.RoleId);

            builder.Entity<Team>().HasIndex(t => t.Name).IsUnique();
        }
    }

Further technical details

EF Core version: 3.1.0 Database provider: Microsoft.EntityFrameworkCore.SqlServer Target framework: netcoreapp3.0 Operating system: Windows 10 pro IDE: Microsoft Visual Studio Professional 2019 - Version 16.4.0

sam-wheat commented 4 years ago

May I please ask why this is has to wait till 5.0.0 and is not a hotfix?
This seems like a very common use case. I am planning on moving to 3.1 in a few weeks and this surely will cause me grief.

Thank you.

smitpatel commented 4 years ago

@sam-wheat - Move take before Select and it should work fine.