diff --git a/src/EFCore.Relational/Update/ModificationCommand.cs b/src/EFCore.Relational/Update/ModificationCommand.cs index 62d6596ecf1..87d82de76a8 100644 --- a/src/EFCore.Relational/Update/ModificationCommand.cs +++ b/src/EFCore.Relational/Update/ModificationCommand.cs @@ -40,6 +40,9 @@ public class ModificationCommand : IModificationCommand, INonTrackedModification private static readonly bool UseOldBehavior37373 = AppContext.TryGetSwitch("Microsoft.EntityFrameworkCore.Issue37373", out var enabled) && enabled; + private static readonly bool UseOldBehavior37525 = + AppContext.TryGetSwitch("Microsoft.EntityFrameworkCore.Issue37525", out var enabled37525) && enabled37525; + /// /// Initializes a new instance. /// @@ -1256,7 +1259,11 @@ public bool TryPropagate(IColumnMappingBase mapping, IUpdateEntry entry) && ((!_originalValueInitialized && property.GetValueComparer().Equals( Update.ColumnModification.GetCurrentValue(entry, property), - property.Sentinel)) + property.Sentinel) + && (UseOldBehavior37525 + || !mapping.Column.ProviderValueComparer.Equals( + _currentValue, + Update.ColumnModification.GetCurrentProviderValue(entry, property)))) || (_originalValueInitialized && mapping.Column.ProviderValueComparer.Equals( Update.ColumnModification.GetCurrentProviderValue(entry, property), diff --git a/test/EFCore.Relational.Specification.Tests/Update/UpdatesRelationalTestBase.cs b/test/EFCore.Relational.Specification.Tests/Update/UpdatesRelationalTestBase.cs index 161445deb85..b6ddbbf7dbd 100644 --- a/test/EFCore.Relational.Specification.Tests/Update/UpdatesRelationalTestBase.cs +++ b/test/EFCore.Relational.Specification.Tests/Update/UpdatesRelationalTestBase.cs @@ -260,6 +260,30 @@ public virtual Task Update_non_indexed_values() }); } + [ConditionalTheory, InlineData(false), InlineData(true)] // Issue #37525 + public virtual async Task Can_save_owned_entity_with_default_values_in_TPH_with_shared_columns(bool async) + => await ExecuteWithStrategyInTransactionAsync( + async context => + { + var entity = new CrunchyNougat { Name = "Test" }; + context.Add(entity); + _ = async ? await context.SaveChangesAsync() : context.SaveChanges(); + }, + async context => + { + var entity = await context.Set().SingleAsync(); + Assert.Null(entity.Filling); + entity.Filling = new NougatFilling(); + _ = async ? await context.SaveChangesAsync() : context.SaveChanges(); + }, + async context => + { + var entity = await context.Set().SingleAsync(); + Assert.NotNull(entity.Filling); + Assert.Equal(NougatFillingKind.Unknown, entity.Filling.Kind); + Assert.False(entity.Filling.IsFresh); + }); + [ConditionalFact] public abstract void Identifiers_are_generated_correctly(); @@ -301,6 +325,23 @@ protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext con .HasColumnName("ZipCode"); }); + modelBuilder.Entity(b => + { + b.OwnsOne(e => e.Filling, ob => + { + ob.Property(o => o.Kind).HasColumnName("FillingKind"); + ob.Property(o => o.IsFresh).HasColumnName("FillingIsFresh"); + }); + }); + modelBuilder.Entity(b => + { + b.OwnsOne(e => e.Filling, ob => + { + ob.Property(o => o.Kind).HasColumnName("FillingKind"); + ob.Property(o => o.IsFresh).HasColumnName("FillingIsFresh"); + }); + }); + modelBuilder .Entity< LoginEntityTypeWithAnExtremelyLongAndOverlyConvolutedNameThatIsUsedToVerifyThatTheStoreIdentifierGenerationLengthLimitIsWorkingCorrectlyDetails diff --git a/test/EFCore.Specification.Tests/TestModels/UpdatesModel/Nougat.cs b/test/EFCore.Specification.Tests/TestModels/UpdatesModel/Nougat.cs new file mode 100644 index 00000000000..0c64ac27d73 --- /dev/null +++ b/test/EFCore.Specification.Tests/TestModels/UpdatesModel/Nougat.cs @@ -0,0 +1,35 @@ +// 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.UpdatesModel; + +#nullable disable + +public abstract class Nougat +{ + public int Id { get; set; } + public string Name { get; set; } +} + +public class CrunchyNougat : Nougat +{ + public NougatFilling Filling { get; set; } +} + +public class SoftNougat : Nougat +{ + public NougatFilling Filling { get; set; } +} + +public class NougatFilling +{ + public NougatFillingKind Kind { get; set; } + public bool IsFresh { get; set; } +} + +public enum NougatFillingKind +{ + Unknown = 0, + Peanut = 1, + Almond = 2, +} diff --git a/test/EFCore.Specification.Tests/TestModels/UpdatesModel/UpdatesContext.cs b/test/EFCore.Specification.Tests/TestModels/UpdatesModel/UpdatesContext.cs index 7213ae7709c..51c261b3f54 100644 --- a/test/EFCore.Specification.Tests/TestModels/UpdatesModel/UpdatesContext.cs +++ b/test/EFCore.Specification.Tests/TestModels/UpdatesModel/UpdatesContext.cs @@ -15,6 +15,8 @@ public class UpdatesContext(DbContextOptions options) : PoolableDbContext(option public DbSet ProductTable { get; set; } = null!; public DbSet ProductTableView { get; set; } = null!; public DbSet Trotters { get; set; } = null!; + public DbSet CrunchyNougats { get; set; } = null!; + public DbSet SoftNougats { get; set; } = null!; public static Task SeedAsync(UpdatesContext context) { diff --git a/test/EFCore.Specification.Tests/Update/UpdatesTestBase.cs b/test/EFCore.Specification.Tests/Update/UpdatesTestBase.cs index 2ed28894753..45a30aee83b 100644 --- a/test/EFCore.Specification.Tests/Update/UpdatesTestBase.cs +++ b/test/EFCore.Specification.Tests/Update/UpdatesTestBase.cs @@ -1042,6 +1042,22 @@ protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext con modelBuilder.Entity().HasOne().WithOne(x => x.Obscurer).HasForeignKey(e => e.LiftId); modelBuilder.Entity(); modelBuilder.Entity(); + + modelBuilder.Entity(); + modelBuilder.Entity(b => + { + b.OwnsOne(e => e.Filling, ob => + { + ob.Property(o => o.Kind).HasConversion(); + }); + }); + modelBuilder.Entity(b => + { + b.OwnsOne(e => e.Filling, ob => + { + ob.Property(o => o.Kind).HasConversion(); + }); + }); } protected override Task SeedAsync(UpdatesContext context)