diff --git a/src/EFCore.PG/Query/Expressions/Internal/PgTableValuedFunctionExpression.cs b/src/EFCore.PG/Query/Expressions/Internal/PgTableValuedFunctionExpression.cs
index 0d7f5c418..7638bb19a 100644
--- a/src/EFCore.PG/Query/Expressions/Internal/PgTableValuedFunctionExpression.cs
+++ b/src/EFCore.PG/Query/Expressions/Internal/PgTableValuedFunctionExpression.cs
@@ -89,7 +89,9 @@ public override TableExpressionBase Clone(string? alias, ExpressionVisitor cloni
arguments[i] = (SqlExpression)cloningExpressionVisitor.Visit(Arguments[i]);
}
- return new PgTableValuedFunctionExpression(Alias, Name, arguments, ColumnInfos, WithOrdinality);
+ // Without ColumnInfos (e.g. unnest), PostgreSQL ties the output column name to the table alias, so preserve it.
+ // With explicit ColumnInfos (e.g. jsonb_to_recordset), apply the clone alias to keep FROM references consistent.
+ return new PgTableValuedFunctionExpression(ColumnInfos is null ? Alias : alias!, Name, arguments, ColumnInfos, WithOrdinality);
}
///
diff --git a/test/EFCore.PG.FunctionalTests/Query/AdHocJsonQueryNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Query/AdHocJsonQueryNpgsqlTest.cs
index 056aab9d1..379234bc2 100644
--- a/test/EFCore.PG.FunctionalTests/Query/AdHocJsonQueryNpgsqlTest.cs
+++ b/test/EFCore.PG.FunctionalTests/Query/AdHocJsonQueryNpgsqlTest.cs
@@ -246,6 +246,47 @@ LIMIT 2
}
}
+ [ConditionalFact]
+ public virtual async Task GroupBy_with_json_collection_predicate_and_projecting_group_elements_works()
+ {
+ var contextFactory = await InitializeAsync(
+ seed: async context =>
+ {
+ context.Entities.AddRange(
+ new JsonEntity
+ {
+ Id = 1,
+ GroupKey = 10,
+ SortOrder = 1,
+ JsonCollection = [new JsonCollectionElement { Value = Guid.Parse("11111111-1111-1111-1111-111111111111") }]
+ },
+ new JsonEntity
+ {
+ Id = 2,
+ GroupKey = 10,
+ SortOrder = 2,
+ JsonCollection = [new JsonCollectionElement { Value = Guid.Parse("11111111-1111-1111-1111-111111111111") }]
+ });
+
+ await context.SaveChangesAsync();
+ });
+
+ await using var context = contextFactory.CreateContext();
+
+ var values = new[] { Guid.Parse("11111111-1111-1111-1111-111111111111") };
+
+ var result = await context.Entities
+ .Where(entity => entity.JsonCollection.Any(element => values.Contains(element.Value)))
+ .GroupBy(entity => entity.GroupKey)
+ .Select(g => new { GroupKey = g.Key, Elements = g.OrderBy(entity => entity.SortOrder).Take(1) }).ToListAsync();
+
+ var group = Assert.Single(result);
+ Assert.Equal(10, group.GroupKey);
+
+ var element = Assert.Single(group.Elements);
+ Assert.Equal(1, element.Id);
+ }
+
protected class TypesDbContext(DbContextOptions options) : DbContext(options)
{
public DbSet Entities { get; set; }
@@ -266,6 +307,30 @@ public class TypesJsonEntity
public TimeSpan Interval { get; set; }
}
+ protected class JsonEntitiesContext(DbContextOptions options) : DbContext(options)
+ {
+ public DbSet Entities { get; set; }
+
+ protected override void OnModelCreating(ModelBuilder modelBuilder)
+ {
+ modelBuilder.Entity().Property(x => x.Id).ValueGeneratedNever();
+ modelBuilder.Entity().OwnsMany(x => x.JsonCollection).ToJson();
+ }
+ }
+
+ public class JsonEntity
+ {
+ public int Id { get; set; }
+ public int GroupKey { get; set; }
+ public int SortOrder { get; set; }
+ public List JsonCollection { get; set; }
+ }
+
+ public class JsonCollectionElement
+ {
+ public Guid Value { get; set; }
+ }
+
protected void AssertSql(params string[] expected)
=> TestSqlLoggerFactory.AssertBaseline(expected);
}