Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 17 additions & 4 deletions src/EFCore.Design/Migrations/Design/CSharpSnapshotGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -452,7 +452,11 @@ protected virtual void GenerateProperty(
// Note that GenerateAnnotations below does the corresponding decrement
stringBuilder.IncrementIndent();

if (property.IsConcurrencyToken)
// ComplexCollectionTypePropertyBuilder doesn't have IsConcurrencyToken method
var isInComplexCollection = property.DeclaringType is IComplexType complexType
&& complexType.ComplexProperty.IsCollection;

if (!isInComplexCollection && property.IsConcurrencyToken)
{
stringBuilder
.AppendLine()
Expand All @@ -466,7 +470,8 @@ protected virtual void GenerateProperty(
.Append(".IsRequired()");
}

if (property.ValueGenerated != ValueGenerated.Never)
// ComplexCollectionTypePropertyBuilder doesn't have ValueGenerated* methods
if (!isInComplexCollection && property.ValueGenerated != ValueGenerated.Never)
{
stringBuilder
.AppendLine()
Expand Down Expand Up @@ -498,8 +503,16 @@ protected virtual void GeneratePropertyAnnotations(
.FilterIgnoredAnnotations(property.GetAnnotations())
.ToDictionary(a => a.Name, a => a);

GenerateFluentApiForMaxLength(property, stringBuilder);
GenerateFluentApiForPrecisionAndScale(property, stringBuilder);
// ComplexCollectionTypePropertyBuilder doesn't have HasMaxLength or HasPrecision methods
var isInComplexCollection = property.DeclaringType is IComplexType complexType
&& complexType.ComplexProperty.IsCollection;

if (!isInComplexCollection)
{
GenerateFluentApiForMaxLength(property, stringBuilder);
GenerateFluentApiForPrecisionAndScale(property, stringBuilder);
}

GenerateFluentApiForIsUnicode(property, stringBuilder);

if (!annotations.ContainsKey(RelationalAnnotationNames.ColumnType)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6259,6 +6259,101 @@ public virtual void Complex_types_mapped_to_json_are_stored_in_snapshot()
Assert.Equal(typeof(List<Dictionary<string, object>>), propertiesComplexCollection.ClrType);
});

[ConditionalFact]
public virtual void Complex_collection_property_annotations_not_supported_by_builder_are_ignored_in_snapshot()
=> Test(
builder =>
{
builder.Entity<EntityWithOneProperty>(b =>
{
b.HasKey(x => x.Id).HasName("PK_Custom");

b.ComplexProperty(
x => x.EntityWithTwoProperties, bb =>
{
bb.ToJson("TwoProps").HasColumnType("json");
bb.ComplexProperty(
x => x.EntityWithStringKey, bbb =>
{
bbb.ComplexCollection(x => x.Properties, bbbb =>
{
bbbb.HasJsonPropertyName("JsonProps");
// Set annotations directly on the model to simulate convention behavior
// These should NOT appear in snapshot because ComplexCollectionTypePropertyBuilder
// doesn't support these methods
var complexType = bbbb.Metadata.ComplexType;
var nameProperty = (IMutableProperty)complexType.FindProperty("Name")!;
nameProperty.SetMaxLength(100);
nameProperty.SetPrecision(10);
nameProperty.SetScale(2);
nameProperty.IsConcurrencyToken = true;
nameProperty.ValueGenerated = ValueGenerated.OnAdd;
});
});
});
});
},
AddBoilerPlate(
GetHeading()
+ """
modelBuilder.Entity("Microsoft.EntityFrameworkCore.Migrations.Design.CSharpMigrationsGeneratorTest+EntityWithOneProperty", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");

SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));

b.ComplexProperty(typeof(Dictionary<string, object>), "EntityWithTwoProperties", "Microsoft.EntityFrameworkCore.Migrations.Design.CSharpMigrationsGeneratorTest+EntityWithOneProperty.EntityWithTwoProperties#EntityWithTwoProperties", b1 =>
{
b1.Property<int>("AlternateId");

b1.Property<int>("Id");

b1.ComplexProperty(typeof(Dictionary<string, object>), "EntityWithStringKey", "Microsoft.EntityFrameworkCore.Migrations.Design.CSharpMigrationsGeneratorTest+EntityWithOneProperty.EntityWithTwoProperties#EntityWithTwoProperties.EntityWithStringKey#EntityWithStringKey", b2 =>
{
b2.Property<string>("Id");

b2.ComplexCollection(typeof(List<Dictionary<string, object>>), "Properties", "Microsoft.EntityFrameworkCore.Migrations.Design.CSharpMigrationsGeneratorTest+EntityWithOneProperty.EntityWithTwoProperties#EntityWithTwoProperties.EntityWithStringKey#EntityWithStringKey.Properties#EntityWithStringProperty", b3 =>
{
b3.Property<int>("Id");

b3.Property<string>("Name");

b3.HasJsonPropertyName("JsonProps");
});
});

b1
.ToJson("TwoProps")
.HasColumnType("json");
});

b.HasKey("Id")
.HasName("PK_Custom");

b.ToTable("EntityWithOneProperty", "DefaultSchema");
});
""", usingSystem: false, usingCollections: true),
o =>
{
var entityWithOneProperty = o.FindEntityType(typeof(EntityWithOneProperty));
var complexProperty1 = entityWithOneProperty.FindComplexProperty(nameof(EntityWithOneProperty.EntityWithTwoProperties));
var complexType1 = complexProperty1.ComplexType;
var entityWithStringKeyComplexProperty =
complexType1.FindComplexProperty(nameof(EntityWithTwoProperties.EntityWithStringKey));
var entityWithStringKeyComplexType = entityWithStringKeyComplexProperty.ComplexType;

var propertiesComplexCollection =
entityWithStringKeyComplexType.FindComplexProperty(nameof(EntityWithStringKey.Properties));
Assert.True(propertiesComplexCollection.IsCollection);

// MaxLength is NOT in the snapshot, so it won't be set on the model created from snapshot
// This verifies that the snapshot doesn't contain HasMaxLength which would cause a compile error
var nameProperty = propertiesComplexCollection.ComplexType.FindProperty("Name");
Assert.Null(nameProperty.GetMaxLength());
});

#endregion

#region HasKey
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9922,6 +9922,39 @@ public virtual void Noop_on_complex_properties()
target => { },
Assert.Empty);

[ConditionalFact]
public void Noop_on_complex_collection_property_annotations_not_in_snapshot()
=> Execute(
builder =>
{
builder.Entity(
"Entity", e =>
{
e.Property<int>("Id").ValueGeneratedOnAdd();
e.HasKey("Id");

e.ComplexCollection<List<MyJsonComplex>, MyJsonComplex>(
"ComplexCollection", cp =>
{
cp.ToJson();
cp.Property(x => x.Value);
cp.Property(x => x.Date);
});
});
},
source =>
{
// Simulate convention setting MaxLength on string property in complex collection
// This annotation is NOT emitted in the snapshot because ComplexCollectionTypePropertyBuilder
// doesn't support HasMaxLength
var entity = source.Model.FindEntityType("Entity");
var complexProperty = entity!.FindComplexProperty("ComplexCollection")!;
var valueProperty = complexProperty.ComplexType.FindProperty("Value")!;
((IMutableProperty)valueProperty).SetMaxLength(255);
},
target => { },
Assert.Empty);

protected class MyJsonComplex
{
public string Value { get; set; }
Expand Down