diff --git a/src/EFCore.Cosmos/Query/Internal/CosmosShapedQueryCompilingExpressionVisitor.CosmosProjectionBindingRemovingExpressionVisitorBase.cs b/src/EFCore.Cosmos/Query/Internal/CosmosShapedQueryCompilingExpressionVisitor.CosmosProjectionBindingRemovingExpressionVisitorBase.cs index 2d27fee9c71..63d948da8e4 100644 --- a/src/EFCore.Cosmos/Query/Internal/CosmosShapedQueryCompilingExpressionVisitor.CosmosProjectionBindingRemovingExpressionVisitorBase.cs +++ b/src/EFCore.Cosmos/Query/Internal/CosmosShapedQueryCompilingExpressionVisitor.CosmosProjectionBindingRemovingExpressionVisitorBase.cs @@ -58,8 +58,8 @@ private readonly IDictionary _projectionBinding private readonly IDictionary _ownerMappings = new Dictionary(); - private readonly IDictionary _ordinalParameterBindings - = new Dictionary(); + private readonly IDictionary _ordinalParameterBindings + = new Dictionary(); private List _pendingIncludes = new List(); @@ -269,9 +269,10 @@ protected override Expression VisitExtension(Expression extensionExpression) var accessExpression = objectArrayProjection.InnerProjection.AccessExpression; _projectionBindings[accessExpression] = jObjectParameter; - _ownerMappings[accessExpression] = ( - objectArrayProjection.Navigation.DeclaringEntityType, objectArrayProjection.AccessExpression); - _ordinalParameterBindings[accessExpression] = ordinalParameter; + _ownerMappings[accessExpression] = + (objectArrayProjection.Navigation.DeclaringEntityType, objectArrayProjection.AccessExpression); + _ordinalParameterBindings[accessExpression] = Expression.Add( + ordinalParameter, Expression.Constant(1, typeof(int))); var innerShaper = (BlockExpression)Visit(collectionShaperExpression.InnerShaper); diff --git a/src/EFCore.Cosmos/Update/Internal/DocumentSource.cs b/src/EFCore.Cosmos/Update/Internal/DocumentSource.cs index 8bbc830ea0f..0e11938a965 100644 --- a/src/EFCore.Cosmos/Update/Internal/DocumentSource.cs +++ b/src/EFCore.Cosmos/Update/Internal/DocumentSource.cs @@ -120,7 +120,7 @@ public virtual JObject CreateDocument([NotNull] IUpdateEntry entry, int? ordinal } else { - var embeddedOrdinal = 0; + var embeddedOrdinal = 1; var array = new JArray(); foreach (var dependent in (IEnumerable)embeddedValue) { @@ -227,7 +227,7 @@ public virtual JObject UpdateDocument([NotNull] JObject document, [NotNull] IUpd } else { - var embeddedOrdinal = 0; + var embeddedOrdinal = 1; var ordinalKeyProperty = GetOrdinalKeyProperty(fk.DeclaringEntityType); if (ordinalKeyProperty != null) { @@ -273,7 +273,7 @@ public virtual JObject UpdateDocument([NotNull] JObject document, [NotNull] IUpd } } - embeddedOrdinal = 0; + embeddedOrdinal = 1; var array = new JArray(); foreach (var dependent in (IEnumerable)embeddedValue) { diff --git a/src/EFCore/ChangeTracking/Internal/ChangeDetector.cs b/src/EFCore/ChangeTracking/Internal/ChangeDetector.cs index 884dcf902b1..f04e841c716 100644 --- a/src/EFCore/ChangeTracking/Internal/ChangeDetector.cs +++ b/src/EFCore/ChangeTracking/Internal/ChangeDetector.cs @@ -93,10 +93,7 @@ public virtual void PropertyChanged(InternalEntityEntry entry, IPropertyBase pro ThrowIfKeyChanged(entry, property); } - if (property.GetRelationshipIndex() != -1) - { - DetectKeyChange(entry, property); - } + DetectKeyChange(entry, property); } else if (propertyBase.GetRelationshipIndex() != -1 && propertyBase is INavigationBase navigation) @@ -274,34 +271,36 @@ private void LogChangeDetected(InternalEntityEntry entry, IProperty property, ob private void DetectKeyChange(InternalEntityEntry entry, IProperty property) { - if (property.GetRelationshipIndex() >= 0) + if (property.GetRelationshipIndex() < 0) { - var snapshotValue = entry.GetRelationshipSnapshotValue(property); - var currentValue = entry[property]; + return; + } - var comparer = property.GetKeyValueComparer(); + var snapshotValue = entry.GetRelationshipSnapshotValue(property); + var currentValue = entry[property]; - // Note that mutation of a byte[] key is not supported or detected, but two different instances - // of byte[] with the same content must be detected as equal. - if (!(comparer?.Equals(currentValue, snapshotValue) - ?? StructuralComparisons.StructuralEqualityComparer.Equals(currentValue, snapshotValue))) - { - var keys = property.GetContainingKeys(); - var foreignKeys = property.GetContainingForeignKeys() - .Where(fk => fk.DeclaringEntityType.IsAssignableFrom(entry.EntityType)); + var comparer = property.GetKeyValueComparer(); - if (_loggingOptions.IsSensitiveDataLoggingEnabled) - { - _logger.ForeignKeyChangeDetectedSensitive(entry, property, snapshotValue, currentValue); - } - else - { - _logger.ForeignKeyChangeDetected(entry, property, snapshotValue, currentValue); - } + // Note that mutation of a byte[] key is not supported or detected, but two different instances + // of byte[] with the same content must be detected as equal. + if (!(comparer?.Equals(currentValue, snapshotValue) + ?? StructuralComparisons.StructuralEqualityComparer.Equals(currentValue, snapshotValue))) + { + var keys = property.GetContainingKeys(); + var foreignKeys = property.GetContainingForeignKeys() + .Where(fk => fk.DeclaringEntityType.IsAssignableFrom(entry.EntityType)); - entry.StateManager.InternalEntityEntryNotifier.KeyPropertyChanged( - entry, property, keys, foreignKeys, snapshotValue, currentValue); + if (_loggingOptions.IsSensitiveDataLoggingEnabled) + { + _logger.ForeignKeyChangeDetectedSensitive(entry, property, snapshotValue, currentValue); } + else + { + _logger.ForeignKeyChangeDetected(entry, property, snapshotValue, currentValue); + } + + entry.StateManager.InternalEntityEntryNotifier.KeyPropertyChanged( + entry, property, keys, foreignKeys, snapshotValue, currentValue); } } diff --git a/src/EFCore/ChangeTracking/Internal/InternalEntityEntry.cs b/src/EFCore/ChangeTracking/Internal/InternalEntityEntry.cs index c161a3fca10..cda7ba579f5 100644 --- a/src/EFCore/ChangeTracking/Internal/InternalEntityEntry.cs +++ b/src/EFCore/ChangeTracking/Internal/InternalEntityEntry.cs @@ -1045,8 +1045,6 @@ public object this[[NotNull] IPropertyBase propertyBase] // Intentionally non-vi { get { - var value = ReadPropertyValue(propertyBase); - var storeGeneratedIndex = propertyBase.GetStoreGeneratedIndex(); if (storeGeneratedIndex != -1) { @@ -1062,6 +1060,7 @@ public object this[[NotNull] IPropertyBase propertyBase] // Intentionally non-vi return generatedValue; } + var value = ReadPropertyValue(propertyBase); if (equals(value, defaultValue)) { if (_temporaryValues.TryGetValue(storeGeneratedIndex, out generatedValue) @@ -1070,9 +1069,11 @@ public object this[[NotNull] IPropertyBase propertyBase] // Intentionally non-vi return generatedValue; } } + + return value; } - return value; + return ReadPropertyValue(propertyBase); } [param: CanBeNull] set => SetProperty(propertyBase, value, isMaterialization: false); @@ -1178,12 +1179,24 @@ private void SetProperty( { WritePropertyValue(propertyBase, value, isMaterialization); - if (currentValueType != CurrentValueType.Normal - && !_temporaryValues.IsEmpty) + switch (currentValueType) { - var defaultValue = asProperty.ClrType.GetDefaultValue(); - var storeGeneratedIndex = asProperty.GetStoreGeneratedIndex(); - _temporaryValues.SetValue(asProperty, defaultValue, storeGeneratedIndex); + case CurrentValueType.StoreGenerated: + if (!_storeGeneratedValues.IsEmpty) + { + var defaultValue = asProperty.ClrType.GetDefaultValue(); + var storeGeneratedIndex = asProperty.GetStoreGeneratedIndex(); + _storeGeneratedValues.SetValue(asProperty, defaultValue, storeGeneratedIndex); + } + break; + case CurrentValueType.Temporary: + if (!_temporaryValues.IsEmpty) + { + var defaultValue = asProperty.ClrType.GetDefaultValue(); + var storeGeneratedIndex = asProperty.GetStoreGeneratedIndex(); + _temporaryValues.SetValue(asProperty, defaultValue, storeGeneratedIndex); + } + break; } } else diff --git a/test/EFCore.Relational.Specification.Tests/DataAnnotationRelationalTestBase.cs b/test/EFCore.Relational.Specification.Tests/DataAnnotationRelationalTestBase.cs index f5b40b35c04..0e9e8defa3c 100644 --- a/test/EFCore.Relational.Specification.Tests/DataAnnotationRelationalTestBase.cs +++ b/test/EFCore.Relational.Specification.Tests/DataAnnotationRelationalTestBase.cs @@ -47,8 +47,11 @@ public virtual void Table_can_configure_TPT_with_Owned() var dogType = model.FindEntityType(typeof(Dog)); Assert.Equal("Dogs", dogType.GetTableMappings().Last().Table.Name); - context.Set().Add( - new Cat { Key = 1, Species = "Felis catus", Tag = new PetTag { TagId = 2 } }); + var petFood = new PetFood() { FoodName = "Fish" }; + context.Add(petFood); + + context.Add( + new Cat { Species = "Felis catus", Tag = new PetTag { TagId = 2 }, FavoritePetFood = petFood }); context.SaveChanges(); }, @@ -66,10 +69,10 @@ protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext con { base.OnModelCreating(modelBuilder, context); - modelBuilder.Entity().ToTable("Animals").Property(x => x.Key).ValueGeneratedNever(); - modelBuilder.Entity().ToTable("Pets").HasOne(x => x.CatFriend); - modelBuilder.Entity().ToTable("Cats"); - modelBuilder.Entity().ToTable("Dogs"); + modelBuilder.Entity(); + modelBuilder.Entity(); + modelBuilder.Entity(); + modelBuilder.Entity(); } } @@ -86,10 +89,10 @@ protected class Pet : Animal { public string Name { get; set; } - [Column("CatFriend_Id")] - [ForeignKey(nameof(CatFriend))] - public int? CatFriendId { get; set; } - public Cat CatFriend { get; set; } + [Column("FavoritePetFood_Id")] + [ForeignKey(nameof(FavoritePetFood))] + public int? FavoritePetFoodId { get; set; } + public PetFood FavoritePetFood { get; set; } [Required] public PetTag Tag { get; set; } @@ -113,5 +116,15 @@ protected sealed class PetTag [Required] public uint? TagId { get; set; } } + + [Table("PetFoods")] + public sealed class PetFood + { + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + [Column("PetFoods_Id")] + public int PetFoodId { get; set; } + + public string FoodName { get; set; } + } } }