diff --git a/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.cs b/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.cs index 174e54dfc6c..6cb57eeebf1 100644 --- a/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.cs +++ b/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.cs @@ -1333,6 +1333,15 @@ Expression CopyProjectionToOuter(SelectExpression innerSelectExpression, Express remappedConstant = Constant(newDictionary); } + else if (constantValue is ValueTuple>, string[]> tuple) + { + var newList = new List>(); + foreach (var item in tuple.Item2) + { + newList.Add((item.Item1, projectionIndexMap[item.Item2])); + } + remappedConstant = Constant((projectionIndexMap[tuple.Item1], newList, tuple.Item3)); + } else { remappedConstant = Constant(projectionIndexMap[(int)constantValue]); @@ -1362,7 +1371,7 @@ Expression CopyProjectionToOuter(SelectExpression innerSelectExpression, Express { result[projectionMember] = expression switch { - EntityProjectionExpression entityProjection => AddEntityProjection(entityProjection), + EntityProjectionExpression entityProjection => AddEntityProjection(entityProjection), JsonQueryExpression jsonQueryExpression => AddJsonProjection(jsonQueryExpression, jsonProjectionDeduplicationMap[jsonQueryExpression]), _ => Constant(AddToProjection((SqlExpression)expression, projectionMember.Last?.Name)) }; diff --git a/test/EFCore.Relational.Specification.Tests/Query/JsonQueryFixtureBase.cs b/test/EFCore.Relational.Specification.Tests/Query/JsonQueryFixtureBase.cs index d97451abc4f..c64f19a9e53 100644 --- a/test/EFCore.Relational.Specification.Tests/Query/JsonQueryFixtureBase.cs +++ b/test/EFCore.Relational.Specification.Tests/Query/JsonQueryFixtureBase.cs @@ -24,6 +24,7 @@ public virtual ISetSource GetExpectedData() public IReadOnlyDictionary EntitySorters { get; } = new Dictionary> { + { typeof(EntityBasic), e => ((EntityBasic)e)?.Id }, { typeof(JsonEntityBasic), e => ((JsonEntityBasic)e)?.Id }, { typeof(JsonEntityBasicForReference), e => ((JsonEntityBasicForReference)e)?.Id }, { typeof(JsonEntityBasicForCollection), e => ((JsonEntityBasicForCollection)e)?.Id }, @@ -36,6 +37,20 @@ public virtual ISetSource GetExpectedData() public IReadOnlyDictionary EntityAsserters { get; } = new Dictionary> { + { + typeof(EntityBasic), (e, a) => + { + Assert.Equal(e == null, a == null); + if (a != null) + { + var ee = (EntityBasic)e; + var aa = (EntityBasic)a; + + Assert.Equal(ee.Id, aa.Id); + Assert.Equal(ee.Name, aa.Name); + } + } + }, { typeof(JsonEntityBasic), (e, a) => { diff --git a/test/EFCore.Relational.Specification.Tests/Query/JsonQueryTestBase.cs b/test/EFCore.Relational.Specification.Tests/Query/JsonQueryTestBase.cs index 9a89f73d09b..8a6e03e47ff 100644 --- a/test/EFCore.Relational.Specification.Tests/Query/JsonQueryTestBase.cs +++ b/test/EFCore.Relational.Specification.Tests/Query/JsonQueryTestBase.cs @@ -389,18 +389,18 @@ public virtual Task Left_join_json_entities_complex_projection(bool async) => AssertQuery( async, ss => (from e1 in ss.Set() - join e2 in ss.Set() on e1.Id equals e2.Id into g - from e2 in g.DefaultIfEmpty() - select new - { - Id1 = e1.Id, - Id2 = (int?)e2.Id, - e2, - e2.OwnedReferenceRoot, - e2.OwnedReferenceRoot.OwnedReferenceBranch, - e2.OwnedReferenceRoot.OwnedReferenceBranch.OwnedReferenceLeaf, - e2.OwnedReferenceRoot.OwnedReferenceBranch.OwnedCollectionLeaf - }), + join e2 in ss.Set() on e1.Id equals e2.Id into g + from e2 in g.DefaultIfEmpty() + select new + { + Id1 = e1.Id, + Id2 = (int?)e2.Id, + e2, + e2.OwnedReferenceRoot, + e2.OwnedReferenceRoot.OwnedReferenceBranch, + e2.OwnedReferenceRoot.OwnedReferenceBranch.OwnedReferenceLeaf, + e2.OwnedReferenceRoot.OwnedReferenceBranch.OwnedCollectionLeaf + }), elementSorter: e => (e.Id1, e?.Id2), elementAsserter: (e, a) => { @@ -664,6 +664,17 @@ public virtual Task Json_with_include_on_entity_collection(bool async) new ExpectedInclude(x => x.EntityCollection)), entryCount: 43); + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Entity_including_collection_with_json(bool async) + => AssertQuery( + async, + ss => ss.Set().Include(e => e.JsonEntityBasics), + elementAsserter: (e, a) => AssertInclude( + e, a, + new ExpectedInclude(x => x.JsonEntityBasics)), + entryCount: 0); + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task Json_with_include_on_entity_collection_and_reference(bool async) diff --git a/test/EFCore.Relational.Specification.Tests/TestModels/JsonQuery/EntityBasic.cs b/test/EFCore.Relational.Specification.Tests/TestModels/JsonQuery/EntityBasic.cs new file mode 100644 index 00000000000..a7dc51d601f --- /dev/null +++ b/test/EFCore.Relational.Specification.Tests/TestModels/JsonQuery/EntityBasic.cs @@ -0,0 +1,12 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.EntityFrameworkCore.TestModels.JsonQuery +{ + public class EntityBasic + { + public int Id { get; set; } + public string Name { get; set; } + public List JsonEntityBasics { get; set; } + } +} diff --git a/test/EFCore.Relational.Specification.Tests/TestModels/JsonQuery/JsonQueryContext.cs b/test/EFCore.Relational.Specification.Tests/TestModels/JsonQuery/JsonQueryContext.cs index b23e0c0e310..741d2cc0296 100644 --- a/test/EFCore.Relational.Specification.Tests/TestModels/JsonQuery/JsonQueryContext.cs +++ b/test/EFCore.Relational.Specification.Tests/TestModels/JsonQuery/JsonQueryContext.cs @@ -10,6 +10,7 @@ public JsonQueryContext(DbContextOptions options) { } + public DbSet EntitiesBasic { get; set; } public DbSet JsonEntitiesBasic { get; set; } public DbSet JsonEntitiesBasicForReference { get; set; } public DbSet JsonEntitiesBasicForCollection { get; set; } diff --git a/test/EFCore.Relational.Specification.Tests/TestModels/JsonQuery/JsonQueryData.cs b/test/EFCore.Relational.Specification.Tests/TestModels/JsonQuery/JsonQueryData.cs index 3e4215c4fee..5f5b6c3a8d0 100644 --- a/test/EFCore.Relational.Specification.Tests/TestModels/JsonQuery/JsonQueryData.cs +++ b/test/EFCore.Relational.Specification.Tests/TestModels/JsonQuery/JsonQueryData.cs @@ -7,6 +7,7 @@ public class JsonQueryData : ISetSource { public JsonQueryData() { + EntitiesBasic = new List(); JsonEntitiesBasic = CreateJsonEntitiesBasic(); JsonEntitiesBasicForReference = CreateJsonEntitiesBasicForReference(); JsonEntitiesBasicForCollection = CreateJsonEntitiesBasicForCollection(); @@ -18,6 +19,7 @@ public JsonQueryData() JsonEntitiesAllTypes = CreateJsonEntitiesAllTypes(); } + public IReadOnlyList EntitiesBasic { get; } public IReadOnlyList JsonEntitiesBasic { get; } public IReadOnlyList JsonEntitiesBasicForReference { get; } public IReadOnlyList JsonEntitiesBasicForCollection { get; } @@ -759,6 +761,11 @@ public static IReadOnlyList CreateJsonEntitiesAllTypes() public IQueryable Set() where TEntity : class { + if (typeof(TEntity) == typeof(EntityBasic)) + { + return (IQueryable)EntitiesBasic.AsQueryable(); + } + if (typeof(TEntity) == typeof(JsonEntityBasic)) { return (IQueryable)JsonEntitiesBasic.AsQueryable(); diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/JsonQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/JsonQuerySqlServerTest.cs index 54a09c29c37..c191eb87487 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/JsonQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/JsonQuerySqlServerTest.cs @@ -583,6 +583,17 @@ FROM [JsonEntitiesBasic] AS [j] ORDER BY [j].[Id]"); } + public override async Task Entity_including_collection_with_json(bool async) + { + await base.Entity_including_collection_with_json(async); + + AssertSql( + @"SELECT [e].[Id], [e].[Name], [j].[Id], [j].[EntityBasicId], [j].[Name], JSON_QUERY([j].[OwnedCollectionRoot],'$'), JSON_QUERY([j].[OwnedReferenceRoot],'$') +FROM [EntitiesBasic] AS [e] +LEFT JOIN [JsonEntitiesBasic] AS [j] ON [e].[Id] = [j].[EntityBasicId] +ORDER BY [e].[Id]"); + } + public override async Task Json_with_include_on_entity_collection_and_reference(bool async) { await base.Json_with_include_on_entity_collection_and_reference(async);