dotnet / efcore

EF Core is a modern object-database mapper for .NET. It supports LINQ queries, change tracking, updates, and schema migrations.
https://docs.microsoft.com/ef/
MIT License
13.65k stars 3.15k forks source link

Adding Migration with owned type throws NullReferenceException #16544

Closed lxalln closed 1 year ago

lxalln commented 5 years ago

After successfully creating a migration that adds an owned entity, the next time a migration is being created the command dotnet ef --startup-project ../web migrations add ___ is throwing a NullReferenceException.

System.NullReferenceException: Object reference not set to an instance of an object.
   at Microsoft.EntityFrameworkCore.Update.Internal.SharedTableEntryMap`1.CreateSharedTableEntryMapFactory(IReadOnlyList`1 entityTypes, IStateManager stateManager, String tableName, String schema)
   at Microsoft.EntityFrameworkCore.Migrations.Internal.MigrationsModelDiffer.DiffData(TableMapping source, TableMapping target, DiffContext diffContext)
   at Microsoft.EntityFrameworkCore.Migrations.Internal.MigrationsModelDiffer.Diff(TableMapping source, TableMapping target, DiffContext diffContext)+MoveNext()
   at Microsoft.EntityFrameworkCore.Migrations.Internal.MigrationsModelDiffer.DiffCollection[T](IEnumerable`1 sources, IEnumerable`1 targets, DiffContext diffContext, Func`4 diff, Func`3 add, Func`3 remove, Func`4[] predicates)+MoveNext()
   at System.Linq.Enumerable.ConcatIterator`1.MoveNext()
   at Microsoft.EntityFrameworkCore.Migrations.Internal.MigrationsModelDiffer.Sort(IEnumerable`1 operations, DiffContext diffContext)
   at Microsoft.EntityFrameworkCore.Migrations.Internal.MigrationsModelDiffer.GetDifferences(IModel source, IModel target)
   at Microsoft.EntityFrameworkCore.Migrations.Design.MigrationsScaffolder.ScaffoldMigration(String migrationName, String rootNamespace, String subNamespace, String language)
   at Microsoft.EntityFrameworkCore.Design.Internal.MigrationsOperations.AddMigration(String name, String outputDir, String contextType)
   at Microsoft.EntityFrameworkCore.Design.OperationExecutor.AddMigrationImpl(String name, String outputDir, String contextType)
   at Microsoft.EntityFrameworkCore.Design.OperationExecutor.AddMigration.<>c__DisplayClass0_1.<.ctor>b__0()
   at Microsoft.EntityFrameworkCore.Design.OperationExecutor.OperationBase.<>c__DisplayClass3_0`1.<Execute>b__0()
   at Microsoft.EntityFrameworkCore.Design.OperationExecutor.OperationBase.Execute(Action action)

I have debugged through the CreateSharedTableEntryMapFactory method and the behaviour is beyond strange. Although I will not pretend to understand what is going on to produce there SharedTableEntryMapFactories.

We are blowing up here: SharedTableEntryMap.cs:103

entityTypes has 2 items in it, the parent and the owned type. The previous call to CreateSharedTableEntryMapFactory also had these same to types.

However, upon calling entityType.FindForeignKeys(…), we find that entityType is actually null. Thus throws an exception.

This is behaviour I do not understand. The list has 2 not null object in it, but the foreach is grabbing a null item.

image

image

This does mean we can no longer create migrations. I am not sure how to proceed with this, or what I can remove to get it working again. I will continue investigating.

Further technical details

EF Core version: 2.1.8 & 2.2.6 Database Provider: Microsoft.EntityFrameworkCore.SqlServer Operating system: Windows 10 IDE: Rider 2019.1.2

ajcvickers commented 5 years ago

@lxalln Please post a small, runnable project/solution or complete code listing that demonstrates the behavior you are seeing.

bricelam commented 4 years ago

I am not able to reproduce this issue using version 2.1.11

@douglasg14b @sveinungf @sandrohanea Do any of you have a project I can use to investigate?

douglasg14b commented 4 years ago

@bricelam I do not, sorry.

Late into some night I sorted this out, but I have no idea how or when. If I had a timestamp on that reaction I could look into my commit history to see.

If I remember correctly it was a configuration issue, HOWEVER, the error EF was throwing was not helpful at all for diagnosing the problem. Running with that, this issue should probably focus on handling the problem and providing a meaningful error message if an example can be conjured up.

lxalln commented 4 years ago

@bricelam Sorry I haven't posted back to this. I have been unable to reproduce the error. I removed the migration that had been causing the issue, and then the problem went away.

I have not found the time go back and investigate more fully :(

sandrohanea commented 4 years ago

In my case, one of my colleagues replaced manually some values in the database snapshot. I didn't know that while I investigated this issue and when I found the problem I forgot I reacted with thumb up to this issue. I fixed the issue by removing the Migration and undo the database snapshot to a valid one.

sveinungf commented 4 years ago

I was able to solve the issue by removing the last migration and regenerate the database snapshot. Looking at the commit history and the diff in the snapshot file, it looks related to owned entities. The regenerated snapshot file added a call to the HasKey method on the model builder for the owned entity. Unfortunately, I can't share the code directly, but it looks similar to this (I added a comment on the line that was added):

partial class MyDbContextModelSnapshot : ModelSnapshot
{
  protected override void BuildModel(ModelBuilder modelBuilder)
  {
    // ...

    modelBuilder.Entity("Entities.Product", b =>
      {
        b.Property<int>("Id")
          .ValueGeneratedOnAdd()
          .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);

        b.Property<string>("ProductText")
          .HasColumnType("varchar(2048)");

        b.HasKey("Id");

        b.ToTable("Product");
    });

    modelBuilder.Entity("Entities.Product", b =>
      {
        b.OwnsOne("Entities.Values.ProductManufacturer", "Manufacturer", b1 =>
          {
            b1.Property<int>("ProductId")
              .ValueGeneratedOnAdd()
              .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);

            b1.Property<string>("Name")
              .HasColumnName("ManufacturerName")
              .HasColumnType("varchar(100)");

            // This line was added after recreating the snapshot
            b1.HasKey("ProductId");

            b1.ToTable("Product");

            b1.HasOne("Entities.Product")
              .WithOne("Manufacturer")
              .HasForeignKey("Entities.Values.ProductManufacturer", "ProductId")
              .OnDelete(DeleteBehavior.Cascade);
          });
      });
  }
}
bricelam commented 4 years ago

Thanks everyone for following up! I'm going to assume that this bug has since been fixed. Sometimes when a migration is generated using a prerelease version or a version before the bug was fixed, it can cause issues in the subsequent migration. Removing and re-adding the migration is the right thing to do in this situation.