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

Query: UseInMemoryDatabase throw KeyNotFoundException when invoke include function #11593

Closed DillonHuang closed 4 years ago

DillonHuang commented 6 years ago

When the Include method is called,use "_" as the variable name in the lambda expression , throws KeyNotFoundException.

Exception message:
KeyNotFoundException: The given key was not present in the dictionary.
Stack trace:
System.ThrowHelper.ThrowKeyNotFoundException()
System.Collections.Generic.Dictionary.get_Item(TKey key)
Microsoft.EntityFrameworkCore.Query.ExpressionVisitors.Internal.DefaultQueryExpressionVisitor.GetParameterValue<T>(QueryContext queryContext, string parameterName)
lambda_method(Closure , AnonymousObject )
System.Linq.Lookup.CreateForJoin(IEnumerable<TElement> source, Func<TElement, TKey> keySelector, IEqualityComparer<TKey> comparer)
System.Linq.Enumerable+<JoinIterator>d__81.MoveNext()
System.Collections.Generic.EnumerableHelpers.ToArray<T>(IEnumerable<T> source, out int length)
System.Linq.Buffer..ctor(IEnumerable<TElement> source)
System.Linq.OrderedEnumerable+<GetEnumerator>d__3.MoveNext()
System.Linq.Enumerable+SelectIPartitionIterator.MoveNext()
Microsoft.EntityFrameworkCore.Query.Internal.QueryBuffer.IncludeCollection<TEntity, TRelated, TElement>(int includeId, INavigation navigation, INavigation inverseNavigation, IEntityType targetEntityType, IClrCollectionAccessor clrCollectionAccessor, IClrPropertySetter inverseClrPropertySetter, bool tracking, TEntity entity, Func<IEnumerable<TRelated>> relatedEntitiesFactory, Func<TEntity, TRelated, bool> joinPredicate)
lambda_method(Closure , QueryContext , Ticket , Object[] )
Microsoft.EntityFrameworkCore.Query.Internal.IncludeCompiler._Include<TEntity>(QueryContext queryContext, TEntity entity, Object[] included, Action<QueryContext, TEntity, Object[]> fixup)
lambda_method(Closure , Ticket )
System.Linq.Enumerable+SelectIPartitionIterator.MoveNext()
Microsoft.EntityFrameworkCore.Query.Internal.LinqOperatorProvider+<_TrackEntities>d__17.MoveNext()
Microsoft.EntityFrameworkCore.Query.Internal.LinqOperatorProvider+ExceptionInterceptor+EnumeratorExceptionInterceptor.MoveNext()
System.Linq.AsyncEnumerable+AsyncEnumerableAdapter.MoveNextCore(CancellationToken cancellationToken)
System.Linq.AsyncEnumerable+AsyncIterator+<MoveNext>d__10.MoveNext()
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
System.Runtime.CompilerServices.ConfiguredTaskAwaitable+ConfiguredTaskAwaiter.GetResult()
System.Collections.Generic.AsyncEnumerableHelpers+<ToArrayWithLength>d__1.MoveNext()
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
System.Runtime.CompilerServices.ConfiguredTaskAwaitable+ConfiguredTaskAwaiter.GetResult()
System.Collections.Generic.AsyncEnumerableHelpers+<ToArray>d__0.MoveNext()
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
System.Runtime.CompilerServices.TaskAwaiter.GetResult()
InMemoryDatabase.Controllers.ValuesController+<Get>d__3.MoveNext() in ValuesController.cs
+
            var result = await this._appDbContext.Set<Ticket>().Include(_ => _.Items).ToArrayAsync();
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
System.Runtime.CompilerServices.TaskAwaiter.GetResult()
Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker+<InvokeActionMethodAsync>d__12.MoveNext()
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker+<InvokeNextActionFilterAsync>d__10.MoveNext()
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Rethrow(ActionExecutedContext context)
Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Next(ref State next, ref Scope scope, ref object state, ref bool isCompleted)
Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker+<InvokeInnerFilterAsync>d__14.MoveNext()
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker+<InvokeNextResourceFilter>d__22.MoveNext()
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Rethrow(ResourceExecutedContext context)
Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Next(ref State next, ref Scope scope, ref object state, ref bool isCompleted)
Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker+<InvokeFilterPipelineAsync>d__17.MoveNext()
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker+<InvokeAsync>d__15.MoveNext()
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
Microsoft.AspNetCore.Builder.RouterMiddleware+<Invoke>d__4.MoveNext()
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware+<Invoke>d__7.MoveNext()

Steps to reproduce

namespace InMemoryDatabase
{
    using Microsoft.EntityFrameworkCore;
    using Microsoft.EntityFrameworkCore.Infrastructure;
    using Microsoft.EntityFrameworkCore.Infrastructure.Internal;

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

        public CoreOptionsExtension CoreOptions { get; set; }

        public InMemoryOptionsExtension InMemoryOptions { get; set; }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);
            modelBuilder.Entity<Ticket>().ToTable("Ticket").HasMany(_ => _.Items).WithOne(_ => _.Ticket)
                .HasForeignKey(_ => _.TicketId).OnDelete(DeleteBehavior.Cascade);

            modelBuilder.Entity<TicketItem>().ToTable("TicketItem");
        }
    }

 public class Ticket
    {
        public long Id { get; set; }

        public IEnumerable<TicketItem> Items { get; set; }
    }

 public class TicketItem
    {
        public long Id { get; set; }

        public long TicketId { get; set; }

        public Ticket Ticket { get; set; }
    }
}
public class Startup
    {

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc();

            services.AddEntityFrameworkInMemoryDatabase();
            services.AddDbContext<AppDbContext>(
                opts => { opts.UseInMemoryDatabase("app-db-context"); });
        }
    }

Throw KeyNotFoundException

 this._appDbContext.Set<Ticket>().Add(new Ticket { Items = new[] { new TicketItem() } });
            await this._appDbContext.SaveChangesAsync();

            // OK
            var result = await this._appDbContext.Set<Ticket>().Include(t => t.Items).ToArrayAsync();

            // Throw KeyNotFoundException 
            result = await this._appDbContext.Set<Ticket>().Include(_ => _.Items).ToArrayAsync();

Further technical details

EF Core version: 2.1.0-preview1-final Database Provider: Microsoft.EntityFrameworkCore.InMemory Operating system: Win 10 IDE: Visual Studio 2017 15.6.5

smitpatel commented 6 years ago

Same happens with correlated subquery too.

This line causes https://github.com/aspnet/EntityFrameworkCore/blob/0e1d2ca3d7b4dc49b77368ede5ccb8b26a4fa1f3/src/EFCore/Query/Internal/IncludeCompiler.CollectionQueryModelRewritingExpressionVisitor.cs#L384

querySource item name to be __ which is reserved prefix for parameters in QCC.

ajcvickers commented 6 years ago

Putting on the backlog. Workaround is to not use "_".

thomaslevesque commented 5 years ago

That's a weird bug... I wouldn't have expected the name of the lambda parameter to have any effect. Will this be fixed for 3.0?

ajcvickers commented 5 years ago

@thomaslevesque This issue is in the Backlog milestone. This means that it is not going to happen for the 3.0 release. We will re-assess the backlog following the 3.0 release and consider this item at that time. However, keep in mind that there are many other high priority features with which it will be competing for resources.

thomaslevesque commented 5 years ago

@ajcvickers sure, I understand. It's not a very big issue anyway, and there's an easy workaround.

smitpatel commented 4 years ago

This is working in 3.1