From 2ef2e24919f5f56a12629bd279d07a10475a901d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 26 Nov 2025 01:39:16 +0000 Subject: [PATCH 1/3] Initial plan From 529b31196467aff3f393dfdedd96274ee2f961c7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 26 Nov 2025 01:55:55 +0000 Subject: [PATCH 2/3] Fix #37143: Always generate HasDiscriminator with generic type argument and property name Co-authored-by: AndriySvyryd <6539701+AndriySvyryd@users.noreply.github.com> --- .../Design/CSharpSnapshotGenerator.cs | 6 +- ...rpMigrationsGeneratorTest.ModelSnapshot.cs | 75 +++++++++++++++++-- 2 files changed, 70 insertions(+), 11 deletions(-) diff --git a/src/EFCore.Design/Migrations/Design/CSharpSnapshotGenerator.cs b/src/EFCore.Design/Migrations/Design/CSharpSnapshotGenerator.cs index 04a6940a970..d3bfe6092e3 100644 --- a/src/EFCore.Design/Migrations/Design/CSharpSnapshotGenerator.cs +++ b/src/EFCore.Design/Migrations/Design/CSharpSnapshotGenerator.cs @@ -644,8 +644,7 @@ protected virtual void GenerateComplexPropertyAnnotations( .Append('.') .Append("HasDiscriminator"); - if (discriminatorProperty.DeclaringType == property.ComplexType - && discriminatorProperty.Name != "Discriminator") + if (discriminatorProperty.DeclaringType == property.ComplexType) { var propertyClrType = FindValueConverter(discriminatorProperty)?.ProviderClrType .MakeNullable(discriminatorProperty.IsNullable) @@ -953,8 +952,7 @@ protected virtual void GenerateEntityTypeAnnotations( .Append('.') .Append("HasDiscriminator"); - if (discriminatorProperty.DeclaringType == entityType - && discriminatorProperty.Name != "Discriminator") + if (discriminatorProperty.DeclaringType == entityType) { var propertyClrType = FindValueConverter(discriminatorProperty)?.ProviderClrType .MakeNullable(discriminatorProperty.IsNullable) diff --git a/test/EFCore.Design.Tests/Migrations/Design/CSharpMigrationsGeneratorTest.ModelSnapshot.cs b/test/EFCore.Design.Tests/Migrations/Design/CSharpMigrationsGeneratorTest.ModelSnapshot.cs index dade6edb6ff..0a9e20b14fb 100644 --- a/test/EFCore.Design.Tests/Migrations/Design/CSharpMigrationsGeneratorTest.ModelSnapshot.cs +++ b/test/EFCore.Design.Tests/Migrations/Design/CSharpMigrationsGeneratorTest.ModelSnapshot.cs @@ -547,6 +547,16 @@ private class EntityWithNullableEnumType public Days? Day { get; set; } } + private class BaseEntityWithIntDiscriminator + { + public int Id { get; set; } + } + + private class DerivedEntityWithIntDiscriminator : BaseEntityWithIntDiscriminator + { + public string Name { get; set; } + } + private class ManyToManyLeft { public int Id { get; set; } @@ -2022,7 +2032,7 @@ public virtual void CheckConstraint_is_only_stored_in_snapshot_once_for_TPH() b.ToTable("BaseEntity", "DefaultSchema"); - b.HasDiscriminator().HasValue("BaseEntity"); + b.HasDiscriminator("Discriminator").HasValue("BaseEntity"); b.UseTphMappingStrategy(); }); @@ -2354,7 +2364,7 @@ public virtual void BaseType_is_stored_in_snapshot() b.ToTable("BaseEntity", "DefaultSchema"); - b.HasDiscriminator().HasValue("BaseEntity"); + b.HasDiscriminator("Discriminator").HasValue("BaseEntity"); b.UseTphMappingStrategy(); }); @@ -2425,7 +2435,7 @@ public virtual void Discriminator_annotations_are_stored_in_snapshot() b.ToTable("BaseEntity", "DefaultSchema"); - b.HasDiscriminator().IsComplete(true).HasValue("BaseEntity"); + b.HasDiscriminator("Discriminator").IsComplete(true).HasValue("BaseEntity"); b.UseTphMappingStrategy(); }); @@ -2500,7 +2510,7 @@ public virtual void Converted_discriminator_annotations_are_stored_in_snapshot() b.ToTable("BaseEntityWithStructDiscriminator", "DefaultSchema"); - b.HasDiscriminator().IsComplete(true).HasValue("Base"); + b.HasDiscriminator("Discriminator").IsComplete(true).HasValue("Base"); b.UseTphMappingStrategy(); }); @@ -3375,6 +3385,57 @@ public virtual void Discriminator_of_enum_to_string() Assert.False(discriminatorProperty.IsNullable); }); + [ConditionalFact] + public virtual void Discriminator_with_non_string_default_name_is_stored_in_snapshot() + => Test( + builder => + { + builder.Entity().HasBaseType(); + builder.Entity() + .HasDiscriminator("Discriminator") + .HasValue(0) + .HasValue(1); + }, + AddBoilerPlate( + GetHeading() + + """ + modelBuilder.Entity("Microsoft.EntityFrameworkCore.Migrations.Design.CSharpMigrationsGeneratorTest+BaseEntityWithIntDiscriminator", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Discriminator") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("BaseEntityWithIntDiscriminator", "DefaultSchema"); + + b.HasDiscriminator("Discriminator").HasValue(0); + + b.UseTphMappingStrategy(); + }); + + modelBuilder.Entity("Microsoft.EntityFrameworkCore.Migrations.Design.CSharpMigrationsGeneratorTest+DerivedEntityWithIntDiscriminator", b => + { + b.HasBaseType("Microsoft.EntityFrameworkCore.Migrations.Design.CSharpMigrationsGeneratorTest+BaseEntityWithIntDiscriminator"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.HasDiscriminator().HasValue(1); + }); +"""), + model => + { + var discriminatorProperty = model.GetEntityTypes().First().FindDiscriminatorProperty(); + Assert.Equal(typeof(int), discriminatorProperty.ClrType); + Assert.Equal("Discriminator", discriminatorProperty.Name); + }); + [ConditionalFact] public virtual void Temporal_table_information_is_stored_in_snapshot() => Test( @@ -4939,7 +5000,7 @@ public virtual void Property_column_name_is_stored_in_snapshot_when_DefaultColum b.ToTable("BarBase", "DefaultSchema"); - b.HasDiscriminator().HasValue("BarBase"); + b.HasDiscriminator("Discriminator").HasValue("BarBase"); b.UseTphMappingStrategy(); }); @@ -5155,7 +5216,7 @@ public virtual void Property_column_name_on_specific_table_is_stored_in_snapshot b.ToTable("BaseEntity", "DefaultSchema"); - b.HasDiscriminator().HasValue("BaseEntity"); + b.HasDiscriminator("Discriminator").HasValue("BaseEntity"); b.UseTphMappingStrategy(); }); @@ -7718,7 +7779,7 @@ public virtual void Do_not_generate_entity_type_builder_again_if_no_foreign_key_ b.ToTable("BaseType", "DefaultSchema"); - b.HasDiscriminator().HasValue("BaseType"); + b.HasDiscriminator("Discriminator").HasValue("BaseType"); b.UseTphMappingStrategy(); }); From ed302cc82c8a4c2ed24e038a1b9bec6c83c66fbb Mon Sep 17 00:00:00 2001 From: Andriy Svyryd Date: Wed, 26 Nov 2025 15:47:22 -0800 Subject: [PATCH 3/3] Cleanup tests --- ...rpMigrationsGeneratorTest.ModelSnapshot.cs | 42 ++++++++----------- 1 file changed, 18 insertions(+), 24 deletions(-) diff --git a/test/EFCore.Design.Tests/Migrations/Design/CSharpMigrationsGeneratorTest.ModelSnapshot.cs b/test/EFCore.Design.Tests/Migrations/Design/CSharpMigrationsGeneratorTest.ModelSnapshot.cs index 0a9e20b14fb..6324f7845aa 100644 --- a/test/EFCore.Design.Tests/Migrations/Design/CSharpMigrationsGeneratorTest.ModelSnapshot.cs +++ b/test/EFCore.Design.Tests/Migrations/Design/CSharpMigrationsGeneratorTest.ModelSnapshot.cs @@ -14,6 +14,7 @@ using NetTopologySuite; using NetTopologySuite.Geometries; using Xunit.Sdk; +using static Microsoft.EntityFrameworkCore.DbLoggerCategory; // ReSharper disable InconsistentNaming // ReSharper disable UnusedAutoPropertyAccessor.Local @@ -547,16 +548,6 @@ private class EntityWithNullableEnumType public Days? Day { get; set; } } - private class BaseEntityWithIntDiscriminator - { - public int Id { get; set; } - } - - private class DerivedEntityWithIntDiscriminator : BaseEntityWithIntDiscriminator - { - public string Name { get; set; } - } - private class ManyToManyLeft { public int Id { get; set; } @@ -2537,13 +2528,18 @@ public virtual void Converted_discriminator_annotations_are_stored_in_snapshot() """), o => { + var baseEntityType = o.FindEntityType(typeof(BaseEntityWithStructDiscriminator)); Assert.Equal( "Discriminator", - o.FindEntityType(typeof(BaseEntityWithStructDiscriminator))[CoreAnnotationNames.DiscriminatorProperty]); + baseEntityType[CoreAnnotationNames.DiscriminatorProperty]); Assert.Equal( "Base", - o.FindEntityType(typeof(BaseEntityWithStructDiscriminator))[CoreAnnotationNames.DiscriminatorValue]); + baseEntityType[CoreAnnotationNames.DiscriminatorValue]); + + var discriminatorProperty = baseEntityType.FindDiscriminatorProperty(); + Assert.Equal(typeof(string), discriminatorProperty.ClrType); + Assert.Equal("Discriminator", discriminatorProperty.Name); Assert.Equal( "Another", @@ -3390,16 +3386,17 @@ public virtual void Discriminator_with_non_string_default_name_is_stored_in_snap => Test( builder => { - builder.Entity().HasBaseType(); - builder.Entity() + builder.Entity().HasBaseType(); + builder.Entity() + .Ignore(b => b.Navigation) .HasDiscriminator("Discriminator") - .HasValue(0) - .HasValue(1); + .HasValue(0) + .HasValue(1); }, AddBoilerPlate( GetHeading() + """ - modelBuilder.Entity("Microsoft.EntityFrameworkCore.Migrations.Design.CSharpMigrationsGeneratorTest+BaseEntityWithIntDiscriminator", b => + modelBuilder.Entity("Microsoft.EntityFrameworkCore.Migrations.Design.CSharpMigrationsGeneratorTest+BaseType", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -3412,26 +3409,23 @@ public virtual void Discriminator_with_non_string_default_name_is_stored_in_snap b.HasKey("Id"); - b.ToTable("BaseEntityWithIntDiscriminator", "DefaultSchema"); + b.ToTable("BaseType", "DefaultSchema"); b.HasDiscriminator("Discriminator").HasValue(0); b.UseTphMappingStrategy(); }); - modelBuilder.Entity("Microsoft.EntityFrameworkCore.Migrations.Design.CSharpMigrationsGeneratorTest+DerivedEntityWithIntDiscriminator", b => + modelBuilder.Entity("Microsoft.EntityFrameworkCore.Migrations.Design.CSharpMigrationsGeneratorTest+DerivedType", b => { - b.HasBaseType("Microsoft.EntityFrameworkCore.Migrations.Design.CSharpMigrationsGeneratorTest+BaseEntityWithIntDiscriminator"); - - b.Property("Name") - .HasColumnType("nvarchar(max)"); + b.HasBaseType("Microsoft.EntityFrameworkCore.Migrations.Design.CSharpMigrationsGeneratorTest+BaseType"); b.HasDiscriminator().HasValue(1); }); """), model => { - var discriminatorProperty = model.GetEntityTypes().First().FindDiscriminatorProperty(); + var discriminatorProperty = model.FindEntityType(typeof(BaseType))!.FindDiscriminatorProperty(); Assert.Equal(typeof(int), discriminatorProperty.ClrType); Assert.Equal("Discriminator", discriminatorProperty.Name); });