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
15 changes: 15 additions & 0 deletions src/EFCore.Design/Migrations/Design/CSharpSnapshotGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System.Text;
using System.Text.Json;
using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.EntityFrameworkCore.Metadata.Internal;

Expand Down Expand Up @@ -837,6 +838,20 @@ protected virtual void GenerateEntityTypeAnnotations(
.FilterIgnoredAnnotations(entityType.GetAnnotations())
.ToDictionary(a => a.Name, a => a);

// Add ContainerColumnType annotation if entity is mapped to JSON but the type annotation is missing
if (annotations.ContainsKey(RelationalAnnotationNames.ContainerColumnName)
&& !annotations.ContainsKey(RelationalAnnotationNames.ContainerColumnType))
{
var containerColumnType = entityType.GetContainerColumnType()
?? Dependencies.RelationalTypeMappingSource.FindMapping(typeof(JsonElement))?.StoreType;
if (containerColumnType != null)
{
annotations[RelationalAnnotationNames.ContainerColumnType] = new Annotation(
RelationalAnnotationNames.ContainerColumnType,
containerColumnType);
}
}

GenerateTableMapping(entityTypeBuilderName, entityType, stringBuilder, annotations);
GenerateSplitTableMapping(entityTypeBuilderName, entityType, stringBuilder);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4362,7 +4362,9 @@ public virtual void Owned_types_mapped_to_json_are_stored_in_snapshot()

b1.ToTable("EntityWithOneProperty", "DefaultSchema");

b1.ToJson("EntityWithTwoProperties");
b1
.ToJson("EntityWithTwoProperties")
.HasColumnType("nvarchar(max)");

b1.WithOwner("EntityWithOneProperty")
.HasForeignKey("EntityWithOnePropertyId");
Expand Down Expand Up @@ -4437,6 +4439,7 @@ public virtual void Owned_types_mapped_to_json_are_stored_in_snapshot()

Assert.Equal(nameof(EntityWithOneProperty), ownedType1.GetTableName());
Assert.Equal("EntityWithTwoProperties", ownedType1.GetContainerColumnName());
Assert.Equal("nvarchar(max)", ownedType1.GetContainerColumnType());

var ownership2 = ownedType1.FindNavigation(nameof(EntityWithStringKey)).ForeignKey;
Assert.Equal("EntityWithTwoPropertiesEntityWithOnePropertyId", ownership2.Properties[0].Name);
Expand Down Expand Up @@ -4473,6 +4476,83 @@ public virtual void Owned_types_mapped_to_json_are_stored_in_snapshot()
Assert.Equal("Name", ownedProperties3[3].Name);
});

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

b.OwnsOne(
x => x.EntityWithTwoProperties, bb =>
{
bb.ToJson().HasColumnType("json");
bb.Ignore(x => x.Id);
bb.Property(x => x.AlternateId).HasJsonPropertyName("NotKey");
bb.WithOwner(e => e.EntityWithOneProperty);
});
});
},
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.HasKey("Id")
.HasName("PK_Custom");

b.ToTable("EntityWithOneProperty", "DefaultSchema");
});

modelBuilder.Entity("Microsoft.EntityFrameworkCore.Migrations.Design.CSharpMigrationsGeneratorTest+EntityWithOneProperty", b =>
{
b.OwnsOne("Microsoft.EntityFrameworkCore.Migrations.Design.CSharpMigrationsGeneratorTest+EntityWithTwoProperties", "EntityWithTwoProperties", b1 =>
{
b1.Property<int>("EntityWithOnePropertyId")
.HasColumnType("int");

b1.Property<int>("AlternateId")
.HasColumnType("int")
.HasAnnotation("Relational:JsonPropertyName", "NotKey");

b1.HasKey("EntityWithOnePropertyId");

b1.ToTable("EntityWithOneProperty", "DefaultSchema");

b1
.ToJson("EntityWithTwoProperties")
.HasColumnType("json");

b1.WithOwner("EntityWithOneProperty")
.HasForeignKey("EntityWithOnePropertyId");

b1.Navigation("EntityWithOneProperty");
});

b.Navigation("EntityWithTwoProperties");
});
""", usingSystem: false),
o =>
{
var entityWithOneProperty = o.FindEntityType(typeof(EntityWithOneProperty));
Assert.Equal("PK_Custom", entityWithOneProperty.GetKeys().Single().GetName());

var ownership1 = entityWithOneProperty.FindNavigation(nameof(EntityWithOneProperty.EntityWithTwoProperties))
.ForeignKey;
var ownedType1 = ownership1.DeclaringEntityType;
Assert.Equal(nameof(EntityWithOneProperty), ownedType1.GetTableName());
Assert.Equal("EntityWithTwoProperties", ownedType1.GetContainerColumnName());
Assert.Equal("json", ownedType1.GetContainerColumnType());
});

private class Order
{
public int Id { get; set; }
Expand Down
53 changes: 53 additions & 0 deletions test/EFCore.SqlServer.Tests/Migrations/SqlServerModelDifferTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1900,4 +1900,57 @@ public void Rebuild_index_with_different_datacompression_value()

Assert.Equal(DataCompressionType.Page, annotationValue);
});

[ConditionalFact]
public void Alter_column_from_nvarchar_max_to_json_for_owned_type()
=> Execute(
_ => { },
source => source.Entity(
"Blog",
x =>
{
x.Property<int>("Id");
x.HasKey("Id");
x.OwnsOne(
"Details", "Details", d =>
{
d.Property<string>("Author");
d.Property<int>("Viewers");
d.ToJson();
});
}),
target => target.Entity(
"Blog",
x =>
{
x.Property<int>("Id");
x.HasKey("Id");
x.OwnsOne(
"Details", "Details", d =>
{
d.Property<string>("Author");
d.Property<int>("Viewers");
d.ToJson().HasColumnType("json");
});
}),
upOps =>
{
Assert.Equal(1, upOps.Count);

var operation = Assert.IsType<AlterColumnOperation>(upOps[0]);
Assert.Equal("Blog", operation.Table);
Assert.Equal("Details", operation.Name);
Assert.Equal("json", operation.ColumnType);
Assert.Equal("nvarchar(max)", operation.OldColumn.ColumnType);
},
downOps =>
{
Assert.Equal(1, downOps.Count);

var operation = Assert.IsType<AlterColumnOperation>(downOps[0]);
Assert.Equal("Blog", operation.Table);
Assert.Equal("Details", operation.Name);
Assert.Equal("nvarchar(max)", operation.ColumnType);
Assert.Equal("json", operation.OldColumn.ColumnType);
});
}