Skip to content

Commit

Permalink
Fix to #30266 - Linq select cannot project an entity containing both …
Browse files Browse the repository at this point in the history
…Json columns and ICollections

Problem was that when we projection collection of entities we use result coordinator and generate different pattern in shaper code. Json entity shaper code is generated inside `resultContext.Values == null` block and all the products should be referred to via resultContext.Values array access.
We were not doing that for json, so in the final projection code we would refer to parameters (storing the actual json entities) that were out of scope.

Fix is to use the correct result coordinator pattern, just like we do for non-json entities.

Fixes #30266
  • Loading branch information
maumar committed Mar 23, 2023
1 parent 8da7a4d commit d7ef5c6
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -484,7 +484,20 @@ protected override Expression VisitExtension(Expression extensionExpression)
var visitedShaperResultParameter = Expression.Parameter(visitedShaperResult.Type);
_variables.Add(visitedShaperResultParameter);
_jsonEntityExpressions.Add(Expression.Assign(visitedShaperResultParameter, visitedShaperResult));
accessor = visitedShaperResultParameter;

if (_containsCollectionMaterialization)
{
_valuesArrayInitializers!.Add(visitedShaperResultParameter);
accessor = Expression.Convert(
Expression.ArrayIndex(
_valuesArrayExpression!,
Expression.Constant(_valuesArrayInitializers.Count - 1)),
entityShaperExpression.Type);
}
else
{
accessor = visitedShaperResultParameter;
}
}
else
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1413,6 +1413,53 @@ public virtual Task Json_with_include_on_entity_collection_and_reference(bool as
new ExpectedInclude<JsonEntityBasic>(x => x.EntityCollection)),
entryCount: 44);

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task Json_with_projection_of_json_reference_leaf_and_entity_collection(bool async)
=> AssertQuery(
async,
ss => ss.Set<JsonEntityBasic>().Select(x => new { x.OwnedReferenceRoot.OwnedReferenceBranch.OwnedReferenceLeaf, x.EntityCollection }).AsNoTracking(),
elementAsserter: (e, a) =>
{
AssertEqual(e.OwnedReferenceLeaf, a.OwnedReferenceLeaf);
AssertCollection(e.EntityCollection, a.EntityCollection);
});

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task Json_with_projection_of_json_reference_and_entity_collection(bool async)
=> AssertQuery(
async,
ss => ss.Set<JsonEntityBasic>().Select(x => new { x.OwnedReferenceRoot, x.EntityCollection }).AsNoTracking(),
elementAsserter: (e, a) =>
{
AssertEqual(e.OwnedReferenceRoot, a.OwnedReferenceRoot);
AssertCollection(e.EntityCollection, a.EntityCollection);
});

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task Json_with_projection_of_multiple_json_references_and_entity_collection(bool async)
=> AssertQuery(
async,
ss => ss.Set<JsonEntityBasic>().Select(x => new
{
Reference1 = x.OwnedReferenceRoot,
Reference2 = x.OwnedCollectionRoot[0].OwnedReferenceBranch,
x.EntityCollection,
Reference3 = x.OwnedCollectionRoot[1].OwnedReferenceBranch.OwnedReferenceLeaf,
Reference4 = x.OwnedCollectionRoot[0].OwnedCollectionBranch[0].OwnedReferenceLeaf,

}).AsNoTracking(),
elementAsserter: (e, a) =>
{
AssertCollection(e.EntityCollection, a.EntityCollection);
AssertEqual(e.Reference1, a.Reference1);
AssertEqual(e.Reference2, a.Reference2);
AssertEqual(e.Reference3, a.Reference3);
AssertEqual(e.Reference4, a.Reference4);
});

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task Json_all_types_entity_projection(bool async)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1312,6 +1312,45 @@ FROM [JsonEntitiesBasic] AS [j]
""");
}

public override async Task Json_with_projection_of_json_reference_leaf_and_entity_collection(bool async)
{
await base.Json_with_projection_of_json_reference_leaf_and_entity_collection(async);

AssertSql(
"""
SELECT [j].[OwnedReferenceRoot], [j].[Id], [j0].[Id], [j0].[Name], [j0].[ParentId]
FROM [JsonEntitiesBasic] AS [j]
LEFT JOIN [JsonEntitiesBasicForCollection] AS [j0] ON [j].[Id] = [j0].[ParentId]
ORDER BY [j].[Id]
""");
}

public override async Task Json_with_projection_of_json_reference_and_entity_collection(bool async)
{
await base.Json_with_projection_of_json_reference_and_entity_collection(async);

AssertSql(
"""
SELECT [j].[OwnedReferenceRoot], [j].[Id], [j0].[Id], [j0].[Name], [j0].[ParentId]
FROM [JsonEntitiesBasic] AS [j]
LEFT JOIN [JsonEntitiesBasicForCollection] AS [j0] ON [j].[Id] = [j0].[ParentId]
ORDER BY [j].[Id]
""");
}

public override async Task Json_with_projection_of_multiple_json_references_and_entity_collection(bool async)
{
await base.Json_with_projection_of_multiple_json_references_and_entity_collection(async);

AssertSql(
"""
SELECT [j].[OwnedReferenceRoot], [j].[Id], JSON_QUERY([j].[OwnedCollectionRoot], '$[0].OwnedReferenceBranch'), [j0].[Id], [j0].[Name], [j0].[ParentId], JSON_QUERY([j].[OwnedCollectionRoot], '$[1].OwnedReferenceBranch.OwnedReferenceLeaf'), JSON_QUERY([j].[OwnedCollectionRoot], '$[0].OwnedCollectionBranch[0].OwnedReferenceLeaf')
FROM [JsonEntitiesBasic] AS [j]
LEFT JOIN [JsonEntitiesBasicForCollection] AS [j0] ON [j].[Id] = [j0].[ParentId]
ORDER BY [j].[Id]
""");
}

public override async Task Json_all_types_entity_projection(bool async)
{
await base.Json_all_types_entity_projection(async);
Expand Down

0 comments on commit d7ef5c6

Please sign in to comment.