Skip to content

Commit ebb6756

Browse files
[release/10.0] Fix snapshot generation for complex collection properties (#37271)
Skip generating HasMaxLength and HasPrecision annotations for properties in complex collections since ComplexCollectionTypePropertyBuilder doesn't support these methods. Also skip IsConcurrencyToken and ValueGenerated* methods for the same reason. Fixes #37256 Co-authored-by: AndriySvyryd <[email protected]>
1 parent 73b894d commit ebb6756

File tree

3 files changed

+145
-4
lines changed

3 files changed

+145
-4
lines changed

src/EFCore.Design/Migrations/Design/CSharpSnapshotGenerator.cs

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -452,7 +452,11 @@ protected virtual void GenerateProperty(
452452
// Note that GenerateAnnotations below does the corresponding decrement
453453
stringBuilder.IncrementIndent();
454454

455-
if (property.IsConcurrencyToken)
455+
// ComplexCollectionTypePropertyBuilder doesn't have IsConcurrencyToken method
456+
var isInComplexCollection = property.DeclaringType is IComplexType complexType
457+
&& complexType.ComplexProperty.IsCollection;
458+
459+
if (!isInComplexCollection && property.IsConcurrencyToken)
456460
{
457461
stringBuilder
458462
.AppendLine()
@@ -466,7 +470,8 @@ protected virtual void GenerateProperty(
466470
.Append(".IsRequired()");
467471
}
468472

469-
if (property.ValueGenerated != ValueGenerated.Never)
473+
// ComplexCollectionTypePropertyBuilder doesn't have ValueGenerated* methods
474+
if (!isInComplexCollection && property.ValueGenerated != ValueGenerated.Never)
470475
{
471476
stringBuilder
472477
.AppendLine()
@@ -498,8 +503,16 @@ protected virtual void GeneratePropertyAnnotations(
498503
.FilterIgnoredAnnotations(property.GetAnnotations())
499504
.ToDictionary(a => a.Name, a => a);
500505

501-
GenerateFluentApiForMaxLength(property, stringBuilder);
502-
GenerateFluentApiForPrecisionAndScale(property, stringBuilder);
506+
// ComplexCollectionTypePropertyBuilder doesn't have HasMaxLength or HasPrecision methods
507+
var isInComplexCollection = property.DeclaringType is IComplexType complexType
508+
&& complexType.ComplexProperty.IsCollection;
509+
510+
if (!isInComplexCollection)
511+
{
512+
GenerateFluentApiForMaxLength(property, stringBuilder);
513+
GenerateFluentApiForPrecisionAndScale(property, stringBuilder);
514+
}
515+
503516
GenerateFluentApiForIsUnicode(property, stringBuilder);
504517

505518
if (!annotations.ContainsKey(RelationalAnnotationNames.ColumnType)

test/EFCore.Design.Tests/Migrations/Design/CSharpMigrationsGeneratorTest.ModelSnapshot.cs

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6511,6 +6511,101 @@ public virtual void Complex_types_mapped_to_json_with_explicit_column_type_are_s
65116511
Assert.Equal(typeof(List<Dictionary<string, object>>), propertiesComplexCollection.ClrType);
65126512
});
65136513

6514+
[ConditionalFact]
6515+
public virtual void Complex_collection_property_annotations_not_supported_by_builder_are_ignored_in_snapshot()
6516+
=> Test(
6517+
builder =>
6518+
{
6519+
builder.Entity<EntityWithOneProperty>(b =>
6520+
{
6521+
b.HasKey(x => x.Id).HasName("PK_Custom");
6522+
6523+
b.ComplexProperty(
6524+
x => x.EntityWithTwoProperties, bb =>
6525+
{
6526+
bb.ToJson("TwoProps").HasColumnType("json");
6527+
bb.ComplexProperty(
6528+
x => x.EntityWithStringKey, bbb =>
6529+
{
6530+
bbb.ComplexCollection(x => x.Properties, bbbb =>
6531+
{
6532+
bbbb.HasJsonPropertyName("JsonProps");
6533+
// Set annotations directly on the model to simulate convention behavior
6534+
// These should NOT appear in snapshot because ComplexCollectionTypePropertyBuilder
6535+
// doesn't support these methods
6536+
var complexType = bbbb.Metadata.ComplexType;
6537+
var nameProperty = (IMutableProperty)complexType.FindProperty("Name")!;
6538+
nameProperty.SetMaxLength(100);
6539+
nameProperty.SetPrecision(10);
6540+
nameProperty.SetScale(2);
6541+
nameProperty.IsConcurrencyToken = true;
6542+
nameProperty.ValueGenerated = ValueGenerated.OnAdd;
6543+
});
6544+
});
6545+
});
6546+
});
6547+
},
6548+
AddBoilerPlate(
6549+
GetHeading()
6550+
+ """
6551+
modelBuilder.Entity("Microsoft.EntityFrameworkCore.Migrations.Design.CSharpMigrationsGeneratorTest+EntityWithOneProperty", b =>
6552+
{
6553+
b.Property<int>("Id")
6554+
.ValueGeneratedOnAdd()
6555+
.HasColumnType("int");
6556+
6557+
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
6558+
6559+
b.ComplexProperty(typeof(Dictionary<string, object>), "EntityWithTwoProperties", "Microsoft.EntityFrameworkCore.Migrations.Design.CSharpMigrationsGeneratorTest+EntityWithOneProperty.EntityWithTwoProperties#EntityWithTwoProperties", b1 =>
6560+
{
6561+
b1.Property<int>("AlternateId");
6562+
6563+
b1.Property<int>("Id");
6564+
6565+
b1.ComplexProperty(typeof(Dictionary<string, object>), "EntityWithStringKey", "Microsoft.EntityFrameworkCore.Migrations.Design.CSharpMigrationsGeneratorTest+EntityWithOneProperty.EntityWithTwoProperties#EntityWithTwoProperties.EntityWithStringKey#EntityWithStringKey", b2 =>
6566+
{
6567+
b2.Property<string>("Id");
6568+
6569+
b2.ComplexCollection(typeof(List<Dictionary<string, object>>), "Properties", "Microsoft.EntityFrameworkCore.Migrations.Design.CSharpMigrationsGeneratorTest+EntityWithOneProperty.EntityWithTwoProperties#EntityWithTwoProperties.EntityWithStringKey#EntityWithStringKey.Properties#EntityWithStringProperty", b3 =>
6570+
{
6571+
b3.Property<int>("Id");
6572+
6573+
b3.Property<string>("Name");
6574+
6575+
b3.HasJsonPropertyName("JsonProps");
6576+
});
6577+
});
6578+
6579+
b1
6580+
.ToJson("TwoProps")
6581+
.HasColumnType("json");
6582+
});
6583+
6584+
b.HasKey("Id")
6585+
.HasName("PK_Custom");
6586+
6587+
b.ToTable("EntityWithOneProperty", "DefaultSchema");
6588+
});
6589+
""", usingSystem: false, usingCollections: true),
6590+
o =>
6591+
{
6592+
var entityWithOneProperty = o.FindEntityType(typeof(EntityWithOneProperty));
6593+
var complexProperty1 = entityWithOneProperty.FindComplexProperty(nameof(EntityWithOneProperty.EntityWithTwoProperties));
6594+
var complexType1 = complexProperty1.ComplexType;
6595+
var entityWithStringKeyComplexProperty =
6596+
complexType1.FindComplexProperty(nameof(EntityWithTwoProperties.EntityWithStringKey));
6597+
var entityWithStringKeyComplexType = entityWithStringKeyComplexProperty.ComplexType;
6598+
6599+
var propertiesComplexCollection =
6600+
entityWithStringKeyComplexType.FindComplexProperty(nameof(EntityWithStringKey.Properties));
6601+
Assert.True(propertiesComplexCollection.IsCollection);
6602+
6603+
// MaxLength is NOT in the snapshot, so it won't be set on the model created from snapshot
6604+
// This verifies that the snapshot doesn't contain HasMaxLength which would cause a compile error
6605+
var nameProperty = propertiesComplexCollection.ComplexType.FindProperty("Name");
6606+
Assert.Null(nameProperty.GetMaxLength());
6607+
});
6608+
65146609
#endregion
65156610

65166611
#region HasKey

test/EFCore.Relational.Tests/Migrations/Internal/MigrationsModelDifferTest.cs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9922,6 +9922,39 @@ public virtual void Noop_on_complex_properties()
99229922
target => { },
99239923
Assert.Empty);
99249924

9925+
[ConditionalFact]
9926+
public void Noop_on_complex_collection_property_annotations_not_in_snapshot()
9927+
=> Execute(
9928+
builder =>
9929+
{
9930+
builder.Entity(
9931+
"Entity", e =>
9932+
{
9933+
e.Property<int>("Id").ValueGeneratedOnAdd();
9934+
e.HasKey("Id");
9935+
9936+
e.ComplexCollection<List<MyJsonComplex>, MyJsonComplex>(
9937+
"ComplexCollection", cp =>
9938+
{
9939+
cp.ToJson();
9940+
cp.Property(x => x.Value);
9941+
cp.Property(x => x.Date);
9942+
});
9943+
});
9944+
},
9945+
source =>
9946+
{
9947+
// Simulate convention setting MaxLength on string property in complex collection
9948+
// This annotation is NOT emitted in the snapshot because ComplexCollectionTypePropertyBuilder
9949+
// doesn't support HasMaxLength
9950+
var entity = source.Model.FindEntityType("Entity");
9951+
var complexProperty = entity!.FindComplexProperty("ComplexCollection")!;
9952+
var valueProperty = complexProperty.ComplexType.FindProperty("Value")!;
9953+
((IMutableProperty)valueProperty).SetMaxLength(255);
9954+
},
9955+
target => { },
9956+
Assert.Empty);
9957+
99259958
protected class MyJsonComplex
99269959
{
99279960
public string Value { get; set; }

0 commit comments

Comments
 (0)