diff --git a/src/EFCore.Cosmos/Query/Internal/CosmosShapedQueryCompilingExpressionVisitor.CosmosProjectionBindingRemovingExpressionVisitorBase.cs b/src/EFCore.Cosmos/Query/Internal/CosmosShapedQueryCompilingExpressionVisitor.CosmosProjectionBindingRemovingExpressionVisitorBase.cs index 97fd77c36fc..aec0acbebad 100644 --- a/src/EFCore.Cosmos/Query/Internal/CosmosShapedQueryCompilingExpressionVisitor.CosmosProjectionBindingRemovingExpressionVisitorBase.cs +++ b/src/EFCore.Cosmos/Query/Internal/CosmosShapedQueryCompilingExpressionVisitor.CosmosProjectionBindingRemovingExpressionVisitorBase.cs @@ -368,18 +368,23 @@ private void AddInclude( var navigationExpression = Visit(includeExpression.NavigationExpression); shaperExpressions.Add( - Expression.Call( - includeMethod.MakeGenericMethod(includingClrType, relatedEntityClrType), - entityEntryVariable, - instanceVariable, - concreteEntityTypeVariable, - navigationExpression, - Expression.Constant(navigation), - Expression.Constant(inverseNavigation, typeof(INavigation)), - Expression.Constant(fixup), - Expression.Constant(initialize, typeof(Action<>).MakeGenericType(includingClrType)), + Expression.IfThen( + Expression.Call( + Expression.Constant(navigation.DeclaringEntityType, typeof(IReadOnlyEntityType)), + IsAssignableFromMethodInfo, + Expression.Convert(concreteEntityTypeVariable, typeof(IReadOnlyEntityType))), + Expression.Call( + includeMethod.MakeGenericMethod(includingClrType, relatedEntityClrType), + entityEntryVariable, + instanceVariable, + concreteEntityTypeVariable, + navigationExpression, + Expression.Constant(navigation), + Expression.Constant(inverseNavigation, typeof(INavigation)), + Expression.Constant(fixup), + Expression.Constant(initialize, typeof(Action<>).MakeGenericType(includingClrType)), #pragma warning disable EF1001 // Internal EF Core API usage. - Expression.Constant(includeExpression.SetLoaded))); + Expression.Constant(includeExpression.SetLoaded)))); #pragma warning restore EF1001 // Internal EF Core API usage. } @@ -562,6 +567,9 @@ private static readonly MethodInfo PopulateCollectionMethodInfo = typeof(CosmosProjectionBindingRemovingExpressionVisitorBase).GetTypeInfo() .GetDeclaredMethod(nameof(PopulateCollection)); + private static readonly MethodInfo IsAssignableFromMethodInfo + = typeof(IReadOnlyEntityType).GetMethod(nameof(IReadOnlyEntityType.IsAssignableFrom), new[] { typeof(IReadOnlyEntityType) })!; + private static TCollection PopulateCollection( IClrCollectionAccessor accessor, IEnumerable entities) diff --git a/test/EFCore.Cosmos.FunctionalTests/Query/OwnedQueryCosmosTest.cs b/test/EFCore.Cosmos.FunctionalTests/Query/OwnedQueryCosmosTest.cs index 43a864c5642..7182672445e 100644 --- a/test/EFCore.Cosmos.FunctionalTests/Query/OwnedQueryCosmosTest.cs +++ b/test/EFCore.Cosmos.FunctionalTests/Query/OwnedQueryCosmosTest.cs @@ -829,6 +829,10 @@ protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext con modelBuilder.Entity().HasData( new { Id = 1, BartonId = 1 }); + + modelBuilder.Entity(); + modelBuilder.Entity().OwnsOne(e => e.Gas); + modelBuilder.Entity().OwnsOne(e => e.Gas); } } } diff --git a/test/EFCore.Specification.Tests/Query/OwnedQueryTestBase.cs b/test/EFCore.Specification.Tests/Query/OwnedQueryTestBase.cs index 85eb3469f4d..d9cbbd34565 100644 --- a/test/EFCore.Specification.Tests/Query/OwnedQueryTestBase.cs +++ b/test/EFCore.Specification.Tests/Query/OwnedQueryTestBase.cs @@ -15,6 +15,43 @@ protected OwnedQueryTestBase(TFixture fixture) fixture.ListLoggerFactory.Clear(); } + [ConditionalTheory] // Issue #26257 + [MemberData(nameof(IsAsyncData))] + public virtual async Task Can_query_owner_with_different_owned_types_having_same_property_name_in_hierarchy(bool async) + { + using (var context = CreateContext()) + { + context.Add(new HeliumBalloon + { + Id = Guid.NewGuid().ToString(), + Gas = new Helium(), + }); + + context.Add(new HydrogenBalloon + { + Id = Guid.NewGuid().ToString(), + Gas = new Hydrogen() + }); + + _ = async ? await context.SaveChangesAsync() : context.SaveChanges(); + } + + using (var context = CreateContext()) + { + var balloons = async + ? await context.Set().ToListAsync() + : context.Set().ToList(); + + Assert.NotEmpty(balloons); + var heliumBalloons = balloons.OfType().ToList(); + var hydrogenBalloons = balloons.OfType().ToList(); + Assert.Equal(heliumBalloons.Count, hydrogenBalloons.Count); + + Assert.All(heliumBalloons, b => Assert.IsType(b.Gas)); + Assert.All(hydrogenBalloons, b => Assert.IsType(b.Gas)); + } + } + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task Query_with_owned_entity_equality_operator(bool async) @@ -1465,6 +1502,10 @@ protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext con modelBuilder.Entity().HasData( new { Id = 1, BartonId = 1 }); + + modelBuilder.Entity(); + modelBuilder.Entity().OwnsOne(e => e.Gas); + modelBuilder.Entity().OwnsOne(e => e.Gas); } public override DbContextOptionsBuilder AddOptions(DbContextOptionsBuilder builder) @@ -1939,4 +1980,29 @@ protected class Throned public int Value { get; set; } public string Property { get; set; } } + + protected abstract class Balloon + { + public string Id { get; set; } + } + + protected class Helium + { + public int X { get; set; } + } + + protected class Hydrogen + { + public int Y { get; set; } + } + + protected class HeliumBalloon : Balloon + { + public Helium Gas { get; set; } + } + + protected class HydrogenBalloon : Balloon + { + public Hydrogen Gas { get; set; } + } } diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/TemporalOwnedQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/TemporalOwnedQuerySqlServerTest.cs index 6a073bfb66f..adb012050ac 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/TemporalOwnedQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/TemporalOwnedQuerySqlServerTest.cs @@ -1686,6 +1686,10 @@ protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext con modelBuilder.Entity() .ToTable(tb => tb.IsTemporal()) .HasData(new { Id = 1, BartonId = 1 }); + + modelBuilder.Entity(); + modelBuilder.Entity().OwnsOne(e => e.Gas); + modelBuilder.Entity().OwnsOne(e => e.Gas); } protected override void Seed(PoolableDbContext context)