diff --git a/src/EFCore.Cosmos/Query/Internal/CosmosShapedQueryCompilingExpressionVisitor.CosmosProjectionBindingRemovingExpressionVisitorBase.cs b/src/EFCore.Cosmos/Query/Internal/CosmosShapedQueryCompilingExpressionVisitor.CosmosProjectionBindingRemovingExpressionVisitorBase.cs index 00736d76676..d9b7b6fa3e5 100644 --- a/src/EFCore.Cosmos/Query/Internal/CosmosShapedQueryCompilingExpressionVisitor.CosmosProjectionBindingRemovingExpressionVisitorBase.cs +++ b/src/EFCore.Cosmos/Query/Internal/CosmosShapedQueryCompilingExpressionVisitor.CosmosProjectionBindingRemovingExpressionVisitorBase.cs @@ -37,10 +37,6 @@ private static readonly MethodInfo CollectionAccessorAddMethodInfo = typeof(IClrCollectionAccessor).GetTypeInfo() .GetDeclaredMethod(nameof(IClrCollectionAccessor.Add)); - private static readonly MethodInfo CollectionAccessorGetOrCreateMethodInfo - = typeof(IClrCollectionAccessor).GetTypeInfo() - .GetDeclaredMethod(nameof(IClrCollectionAccessor.GetOrCreate)); - private readonly IDictionary _materializationContextBindings = new Dictionary(); @@ -402,7 +398,6 @@ private void AddInclude( var inverseNavigation = navigation.Inverse; var fixup = GenerateFixup( includingClrType, relatedEntityClrType, navigation, inverseNavigation); - var initialize = GenerateInitialize(includingClrType, navigation); var navigationExpression = Visit(includeExpression.NavigationExpression); @@ -421,7 +416,6 @@ private void AddInclude( Constant(navigation), Constant(inverseNavigation, typeof(INavigation)), Constant(fixup), - Constant(initialize, typeof(Action<>).MakeGenericType(includingClrType)), #pragma warning disable EF1001 // Internal EF Core API usage. Constant(includeExpression.SetLoaded)))); #pragma warning restore EF1001 // Internal EF Core API usage. @@ -441,8 +435,7 @@ private static void IncludeReference( INavigation navigation, INavigation inverseNavigation, Action fixup, - Action _, - bool __) + bool _) { if (entity == null || !navigation.DeclaringEntityType.IsAssignableFrom(entityType)) @@ -486,7 +479,6 @@ private static void IncludeCollection( INavigation navigation, INavigation inverseNavigation, Action fixup, - Action initialize, bool setLoaded) { if (entity == null @@ -495,6 +487,8 @@ private static void IncludeCollection( return; } + navigation.GetCollectionAccessor()!.GetOrCreate(entity, forMaterialization: true); + if (entry == null) { var includingEntity = (TIncludingEntity)entity; @@ -508,10 +502,6 @@ private static void IncludeCollection( inverseNavigation?.SetIsLoadedWhenNoTracking(relatedEntity); } } - else - { - initialize(includingEntity); - } } else { @@ -524,15 +514,12 @@ private static void IncludeCollection( if (relatedEntities != null) { + // Enumerator contains logic for tracking the entities, so we need to make sure to enumerate it using var enumerator = relatedEntities.GetEnumerator(); while (enumerator.MoveNext()) { } } - else - { - initialize((TIncludingEntity)entity); - } } } @@ -563,27 +550,6 @@ private static Delegate GenerateFixup( .Compile(); } - private static Delegate GenerateInitialize( - Type entityType, - INavigation navigation) - { - if (!navigation.IsCollection) - { - return null; - } - - var entityParameter = Parameter(entityType); - - var getOrCreateExpression = Call( - Constant(navigation.GetCollectionAccessor()), - CollectionAccessorGetOrCreateMethodInfo, - entityParameter, - Constant(true)); - - return Lambda(Block(typeof(void), getOrCreateExpression), entityParameter) - .Compile(); - } - private static Expression AssignReferenceNavigation( ParameterExpression entity, ParameterExpression relatedEntity, diff --git a/test/EFCore.Cosmos.FunctionalTests/Query/Associations/OwnedNavigations/OwnedNavigationsCosmosFixture.cs b/test/EFCore.Cosmos.FunctionalTests/Query/Associations/OwnedNavigations/OwnedNavigationsCosmosFixture.cs index 75ea4719072..b07fc014f60 100644 --- a/test/EFCore.Cosmos.FunctionalTests/Query/Associations/OwnedNavigations/OwnedNavigationsCosmosFixture.cs +++ b/test/EFCore.Cosmos.FunctionalTests/Query/Associations/OwnedNavigations/OwnedNavigationsCosmosFixture.cs @@ -31,82 +31,4 @@ protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext con .ToContainer("RootEntities") .HasNoDiscriminator(); } - - // We need to override the following asserters because of #36577: - // the Cosmos provider incorrectly returns null for empty collections in some cases - protected override void AssertRootEntity(RootEntity e, RootEntity a) - { - Assert.Equal(e.Id, a.Id); - Assert.Equal(e.Name, a.Name); - - NullSafeAssert(e.RequiredAssociate, a.RequiredAssociate, AssertAssociate); - NullSafeAssert(e.OptionalAssociate, a.OptionalAssociate, AssertAssociate); - - if (e.AssociateCollection is not null && a.AssociateCollection is not null) - { - Assert.Equal(e.AssociateCollection.Count, a.AssociateCollection.Count); - - var (orderedExpected, orderedActual) = (e.AssociateCollection, a.AssociateCollection); - - for (var i = 0; i < e.AssociateCollection.Count; i++) - { - AssertAssociate(orderedExpected[i], orderedActual[i]); - } - } - else - { - // #36577: the Cosmos provider incorrectly returns null for empty collections in some cases - if (e.AssociateCollection is [] && a.AssociateCollection is null) - { - return; - } - - Assert.Equal(e.AssociateCollection, a.AssociateCollection); - } - } - - protected override void AssertAssociate(AssociateType e, AssociateType a) - { - Assert.Equal(e.Id, a.Id); - Assert.Equal(e.Name, a.Name); - - Assert.Equal(e.Int, a.Int); - Assert.Equal(e.String, a.String); - - NullSafeAssert(e.RequiredNestedAssociate, a.RequiredNestedAssociate, AssertNestedAssociate); - NullSafeAssert(e.OptionalNestedAssociate, a.OptionalNestedAssociate, AssertNestedAssociate); - - if (e.NestedCollection is not null && a.NestedCollection != null) - { - Assert.Equal(e.NestedCollection.Count, a.NestedCollection.Count); - - var (orderedExpected, orderedActual) = (e.NestedCollection, a.NestedCollection); - - for (var i = 0; i < e.NestedCollection.Count; i++) - { - AssertNestedAssociate(orderedExpected[i], orderedActual[i]); - } - } - else - { - // #36577: the Cosmos provider incorrectly returns null for empty collections in some cases - if (e.NestedCollection is [] && a.NestedCollection is null) - { - return; - } - - Assert.Equal(e.NestedCollection, a.NestedCollection); - } - } - - private static void NullSafeAssert(object? e, object? a, Action assertAction) - { - if (e is T ee && a is T aa) - { - assertAction(ee, aa); - return; - } - - Assert.Equal(e, a); - } }