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.63k stars 3.15k forks source link

Cosmos: KeyNotFoundException for queries doing SelectMany on owned collection #34349

Open maumar opened 1 month ago

maumar commented 1 month ago

Query:

public virtual Task Json_collection_SelectMany(bool async)
    => AssertQuery(
        async,
        ss => ss.Set<JsonEntityBasic>()
            .SelectMany(x => x.OwnedCollectionRoot)
            .AsNoTracking(),
        elementSorter: e => (e.Number, e.Name));

exception

Message: 
System.Collections.Generic.KeyNotFoundException : The given key 'o' was not present in the dictionary.

  Stack Trace: 
Dictionary`2.get_Item(TKey key)
CosmosProjectionBindingRemovingExpressionVisitorBase.CreateGetValueExpression(Expression jTokenExpression, IProperty property, Type type) line 628
CosmosProjectionBindingRemovingExpressionVisitorBase.VisitMethodCall(MethodCallExpression methodCallExpression) line 207
ExpressionVisitor.VisitBinary(BinaryExpression node)
CosmosProjectionBindingRemovingExpressionVisitorBase.VisitBinary(BinaryExpression binaryExpression) line 182
ExpressionVisitor.VisitBinary(BinaryExpression node)
CosmosProjectionBindingRemovingExpressionVisitorBase.VisitBinary(BinaryExpression binaryExpression) line 182
ExpressionVisitor.VisitConditional(ConditionalExpression node)
ExpressionVisitorUtils.VisitBlockExpressions(ExpressionVisitor visitor, BlockExpression block)
ExpressionVisitor.VisitBlock(BlockExpression node)
ExpressionVisitor.VisitConditional(ConditionalExpression node)
ExpressionVisitorUtils.VisitBlockExpressions(ExpressionVisitor visitor, BlockExpression block)
ExpressionVisitor.VisitBlock(BlockExpression node)
CosmosProjectionBindingRemovingExpressionVisitorBase.VisitExtension(Expression extensionExpression) line 310
CosmosProjectionBindingRemovingExpressionVisitorBase.VisitExtension(Expression extensionExpression) line 310
CosmosShapedQueryCompilingExpressionVisitor.VisitShapedQuery(ShapedQueryExpression shapedQueryExpression) line 71
ShapedQueryCompilingExpressionVisitor.VisitExtension(Expression extensionExpression) line 96
QueryCompilationContext.CreateQueryExecutorExpression[TResult](Expression query) line 234
QueryCompilationContext.CreateQueryExecutor[TResult](Expression query) line 199
Database.CompileQuery[TResult](Expression query, Boolean async) line 68
QueryCompiler.CompileQueryCore[TResult](IDatabase database, Expression query, IModel model, Boolean async) line 127
<>c__DisplayClass11_0`1.<ExecuteCore>b__0() line 83
CompiledQueryCache.GetOrAddQuery[TResult](Object cacheKey, Func`1 compiler) line 66
QueryCompiler.ExecuteCore[TResult](Expression query, Boolean async, CancellationToken cancellationToken) line 79
QueryCompiler.ExecuteAsync[TResult](Expression query, CancellationToken cancellationToken) line 69
EntityQueryProvider.ExecuteAsync[TResult](Expression expression, CancellationToken cancellationToken) line 83
EntityQueryable`1.GetAsyncEnumerator(CancellationToken cancellationToken) line 96
ConfiguredCancelableAsyncEnumerable`1.GetAsyncEnumerator()

SelectMany should be supported (https://github.com/dotnet/efcore/pull/34013)

roji commented 1 month ago

~Note: we already have OwnedQueryCosmosTest.SelectMany_on_owned_collection which tests exactly this query - except for the AsNoTracking... We also have PrimitiveCollectionsQueryCosmosTest.Column_collection_SelectMany for the scalar (non-entity) version.~ The projected collection isn't actually owned in that test (otherwise we don't support projecting out owned things in a tracking query).

roji commented 1 month ago

Test: JsonQueryTestBase.Json_collection_SelectMany

roji commented 2 weeks ago

@ajcvickers I took a brief look, and this looks to me to be a Cosmos shaper issue, related to our switch to VALUE projections - I think the shaper generation erroneously still thinks that the collection of projected owned types is inside an object, but it isn't (any more).