We have been using EF Core for 6 months now, and recently, after updating our project from 2.1 to 2.2, any new migration script has a ton of unexpected DropForeignKey, DropUniqueConstraint, DropColumn statements. Neither of the Keys, UniqueConstraints or Columns exists, so running this on the DB obviously fails. But this prevents us from creating new migrations.
I cannot come up with a small reproducible example, but I will try to share some patterns of the statements I noticed.
All DROP COLUMN statements happen only for classes that are owned by parent classes that are owned by a grandparent (it has to be double owning, single not enough). By owning I mean something like that:
builder.OwnsOne(
nav => nav.MyCustomChildProperty, action =>
{
action.OwnsOne(nav => nav.MyCustomGrandChildProperty);
}
);
In all such scenarions, in my migration scripts, I would see DROP COLUMN statements like such:
MyCustomChildProperty_TempIdMyCustomChildProperty_TempId1 (this one is a maybe) MyCustomChildProperty_MyCustomGrandChildProperty_TempIdMyCustomChildProperty_MyCustomGrandChildProperty_TempId1 (this one is a maybe) MyCustomChildProperty_MyCustomGrandChildProperty_TempId2 (this one is a maybe) MyCustomChildProperty_MyCustomGrandChildProperty_MyCustomChildPropertyClassMyCustomClassIdId to it.I haven't tried to find a pattern for the other two types of statements (DropForeignKey, DropUniqueConstraint), but this seems that it should be related.
I tried deleting all of my migrations and creating a single fresh migration, and everything looks there as expected, therefore it seems that this is some bug that happens only if you already have some old migration scripts and trying to add a new one.
I have raised an SO question for this as well with no success.
We were running using 2.1 versions ofdotnetcore andef core`.
Currently, my versions are:
dotnet --version = 2.2.102dotnet ef --version = 2.2.1-servicing-10028Microsoft.EntityFrameworkCore.SqlServer, v2.2.1@EddyP23 Ideally, we need a runnable project/solution that reproduces what you are seeing. Absent of that, could you share you the model snapshot (from your Migrations folder) before and after adding a new migration that causes this issue?
Unfortunately, I can't.
But if that is of any help, a git diff after the problematic migration contains the following:
- .HasAnnotation("ProductVersion", "2.1.4-rtm-31024")
+ .HasAnnotation("ProductVersion", "2.2.1-servicing-10028")
and a lot of lines like this:
b.OwnsOne("A.B.C.D.E.F", "ParameterNameOfTypeF", b1 =>
{
- b1.Property<Guid?>("SomethingId");
+ b1.Property<Guid>("SomethingId");
+
+ b1.HasKey("SomethingId");
...
and a lot of lines like this:
b.OwnsOne("A.B.C.D.E.F", "ParameterNameOfTypeF", b1 =>
{
+ b1.HasKey("SomethingId");
...
and nothing else. For clarity, that is the migration that should not produce anything, as models have not changed at all.
We have the same issue with our code with OwnedTypes.
builder.OwnsOne(b => b.ContactAddr, cb =>
{
cb.OwnsOne(c => c.Coordinates);
});
builder.OwnsOne(b => b.LegalAddr, cb =>
{
cb.OwnsOne(c => c.Coordinates);
});
for both LegalAddr and ContactAddr we get DropForeignKey, DropUniqueConstraint, DropColumn in migrations with some TempId1 and TempId field
Also for simple OwnsOne it creates the issue of adding primary key from class that contains that owned type. This seems to happen when inheritance is involved between classes.
modelBuilder.Entity<SampleModel>().OwnsOne(x => x.SampleOwnedType);
I would rise this issue as Blocking issue, since there is no way to upgrade from 2.1 to 2.2 in this case.
Any news on this case? We are facing the same issue when upgrading to 2.2.
@arielmoraes We're treating it as a bug scheduled for 3.0, but we don't have a runnable project/solution or code listing yet. If you could provide that it would be very helpful.
@ajcvickers I will try to break down the classes needed to reproduce without showing much of our code.
In the mean time we noted that the migrations generated using the version 2.2 started to add the HasKey method for the OwnsOne relationships in the Snapshot, maybe this could be related because in the previous Snapshot the HasKey was not present and the code responsible for creating the migrations thinks those constraints should be dropped.
I tried to repro this on 3.0.0, but I hit #18183 instead.
Here's a model that repros from 2.1.11 to 2.2.6
using Microsoft.EntityFrameworkCore;
namespace ConsoleApp1
{
class OwningType
{
public int Id { get; set; }
public OwnedType OwnedType { get; set; }
}
class OwnedType
{
public NestedOwnedType NestedOwnedType { get; set; }
}
class NestedOwnedType
{
public int Value { get; set; }
}
class Context : DbContext
{
public DbSet<OwningType> OwningTypes { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder options)
=> options.UseSqlServer(@"Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=ConsoleApp1");
protected override void OnModelCreating(ModelBuilder modelBuilder)
=> modelBuilder.Entity<OwningType>()
.OwnsOne(c => c.OwnedType)
.OwnsOne(c => c.NestedOwnedType);
}
}
New exception on release/3.1:
InvalidOperationException: A key on entity type 'ConsoleApp1.NestedOwnedType' cannot contain the property 'OwnedTypeOwningTypeId' because it is nullable/optional. All properties on which a key is declared must be marked as non-nullable/required.
at Microsoft.EntityFrameworkCore.Metadata.Internal.EntityType.AddKey(IReadOnlyList`1 properties, ConfigurationSource configurationSource)
at Microsoft.EntityFrameworkCore.Metadata.Internal.EntityType.SetPrimaryKey(IReadOnlyList`1 properties, ConfigurationSource configurationSource)
at Microsoft.EntityFrameworkCore.Metadata.Internal.EntityType.Microsoft.EntityFrameworkCore.Metadata.IMutableEntityType.SetPrimaryKey(IReadOnlyList`1 properties)
at Microsoft.EntityFrameworkCore.Migrations.Internal.SnapshotModelProcessor.ProcessElement(IEntityType entityType, String version)
at Microsoft.EntityFrameworkCore.Migrations.Internal.SnapshotModelProcessor.Process(IModel model)
@bricelam Can you post the snapshot?
Note, only happens with snapshots generated on 2.1:
modelBuilder
.HasAnnotation("ProductVersion", "2.1.11-servicing-32099")
.HasAnnotation("Relational:MaxIdentifierLength", 128)
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
modelBuilder.Entity("ConsoleApp1.OwningType", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.HasKey("Id");
b.ToTable("OwningTypes");
});
modelBuilder.Entity("ConsoleApp1.OwningType", b =>
{
b.OwnsOne("ConsoleApp1.OwnedType", "OwnedType", b1 =>
{
b1.Property<int?>("OwningTypeId")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b1.ToTable("OwningTypes");
b1.HasOne("ConsoleApp1.OwningType")
.WithOne("OwnedType")
.HasForeignKey("ConsoleApp1.OwnedType", "OwningTypeId")
.OnDelete(DeleteBehavior.Cascade);
b1.OwnsOne("ConsoleApp1.NestedOwnedType", "NestedOwnedType", b2 =>
{
b2.Property<int?>("OwnedTypeOwningTypeId")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b2.Property<int>("Value");
b2.ToTable("OwningTypes");
b2.HasOne("ConsoleApp1.OwnedType")
.WithOne("NestedOwnedType")
.HasForeignKey("ConsoleApp1.NestedOwnedType", "OwnedTypeOwningTypeId")
.OnDelete(DeleteBehavior.Cascade);
});
});
});
@bricelam We can compensate by making the properties required before setting the PK. I assume it also reproes for 2.0 snapshots
class OwningType
{
public int Id { get; set; }
public OwnedType OwnedType1 { get; set; }
public OwnedType OwnedType2 { get; set; }
}
[Owned]
class OwnedType
{
public int Value { get; set; }
}
NullReferenceException: Object reference not set to an instance of an object.
at Microsoft.EntityFrameworkCore.Metadata.Internal.EntityType.AddKey(IReadOnlyList`1 properties, ConfigurationSource configurationSource)
at Microsoft.EntityFrameworkCore.Metadata.Internal.EntityType.SetPrimaryKey(IReadOnlyList`1 properties, ConfigurationSource configurationSource)
at Microsoft.EntityFrameworkCore.Metadata.Internal.EntityType.Microsoft.EntityFrameworkCore.Metadata.IMutableEntityType.SetPrimaryKey(IReadOnlyList`1 properties)
at Microsoft.EntityFrameworkCore.Migrations.Internal.SnapshotModelProcessor.ProcessElement(IEntityType entityType, String version)
at Microsoft.EntityFrameworkCore.Migrations.Internal.SnapshotModelProcessor.Process(IModel model)
I assume it also reproes for 2.0 snapshots
Getting NullRef on 2.0.3. Snapshot is mostly the same but no ValueGeneratedOnAdd or ValueGenerationStrategy annotation.
Oh, and Ids are aren't Nullable on 2.0
* aren't Nullable
The NRE is thrown because the model is read-only at that point, since we call FinalizeModel at https://github.com/aspnet/EntityFrameworkCore/blob/24b9aa1d2e14fe2e737255ede9b2a7a623fcf2af/src/EFCore.Relational/Infrastructure/ModelSnapshot.cs#L24
We should move that call to the SnapshotModelProcessor
@AndriySvyryd Added some tests in bricelam:ownold. Should I start making the product changes to make them pass or are you already on it?
@bricelam Go ahead. I'm working on https://github.com/aspnet/EntityFrameworkCore/issues/18628, it'll likely take me all the time left for 3.1
@AndriySvyryd - I can work on #18628 or anything else on your plate you find suitable.
@smitpatel I'd like to do at least the initial work. Once that's done we can see who'll handle the perf optimization (avoiding boxing).