efcore: Generated Model Snapshot throws InvalidOperationException

When migrating from EF6 to EF7, the EF7 generated model snapshot throws System.InvalidOperationException: Table name must be specified to configure a table-specific property mapping. when being applied. The generated snapshot also prevents the migration from being removed due to the same exception.

The base type is mapped to a custom table name and the expected behavior is that types inheriting from the base table should share the same table name without explicit configuration.

Only the .csproj and nuget libraries were updated to 7, no model or configuration changes were made.

EF Core 6 generated model:

 modelBuilder.Entity("Domain.Aggregates.CustomerBalances.OutgoingCrossPlatformBalanceTransferLedgerEntry", b =>
                {
                    b.HasBaseType("Domain.Aggregates.CustomerBalances.LedgerEntry");

                    b.Property<string>("TransferId")
                        .HasColumnType("text")
                        .HasColumnName("OutgoingCrossPlatformBalanceTransferLedgerEntry_TransferId"); //moved to b.ToTable() in EF7

                    b.HasIndex("TransferId")
                        .IsUnique();

                    b.HasDiscriminator().HasValue("OutgoingCrossPlatformBalanceTransferLedgerEntry");
                });

EF Core 7 generated model:

 modelBuilder.Entity("Domain.Aggregates.CustomerBalances.OutgoingCrossPlatformBalanceTransferLedgerEntry", b =>
                {
                    b.HasBaseType("Domain.Aggregates.CustomerBalances.LedgerEntry");

                    b.Property<string>("TransferId")
                        .HasColumnType("text");

                    b.HasIndex("TransferId")
                        .IsUnique();

//b.ToTable() generated code in EF7
//exception thrown: System.InvalidOperationException: Table name must be specified to configure a table-specific property mapping.
                    b.ToTable(t =>
                        {
                            t.Property("TransferId")
                                .HasColumnName("OutgoingCrossPlatformBalanceTransferLedgerEntry_TransferId");
                        });

                    b.HasDiscriminator().HasValue("OutgoingCrossPlatformBalanceTransferLedgerEntry");
                });

Config for base type:

public class LedgerEntryConfiguration : IEntityTypeConfiguration<LedgerEntry>
{
    public void Configure(EntityTypeBuilder<LedgerEntry> builder)
    {
        builder.HasIndex(p => p.CreatedAt);
        builder.ToTable("BalanceLedgerEntries");
    }
}

By adding builder.ToTable("BalanceLedgerEntries"); in the subtype configuration, the following is generated in the snapshot, which works:

 b.ToTable("BalanceLedgerEntries", null, t =>
                        {
                            t.Property("TransferId")
                                .HasColumnName("OutgoingCrossPlatformBalanceTransferLedgerEntry_TransferId");
                        });

About this issue

  • Original URL
  • State: closed
  • Created 2 years ago
  • Reactions: 15
  • Comments: 15 (7 by maintainers)

Commits related to this issue

Most upvoted comments

This bug is a serious blocker for us. The workaround requires modifying the snapshot before creating and running migrations. Can’t this be released earlier?

Do we really need to wait 2 months for release cycle? I don’t really have much confidence in dev builds being stable when major release introduces such regressions.

@Prazdnik Second Tuesday in Feb 2023

We are getting the same error, (both with changes, but also with an empty migration) (Using TPH)

The snapshot now adds “ToTable” with property definitions, that was set on the “Property” before:

modelBuilder.Entity("Domain.Entities.Template", b =>
                {
                    b.HasBaseType("Domain.Entities.DynamicFieldListEntity");

                    b.Property<bool>("IncludeGdpr")
                        .HasColumnType("bit");

                    b.Property<string>("Name")
                        .IsRequired()
                        .HasColumnType("nvarchar(max)");

                    b.ToTable("DynamicFieldListEntity", "entities", t =>
                        {
                            t.Property("IncludeGdpr")
                                .HasColumnName("Template_IncludeGdpr");

                            t.Property("Name")
                                .HasColumnName("Template_Name");
                        });

                    b.HasDiscriminator().HasValue("Template");
                });

If we manually change this back in the snapshot, it again works:

            modelBuilder.Entity("Domain.Entities.Template", b =>
                {
                    b.HasBaseType("Domain.Entities.DynamicFieldListEntity");

                    b.Property<bool>("IncludeGdpr")
                        .HasColumnName("Template_IncludeGdpr")
                        .HasColumnType("bit");

                    b.Property<string>("Name")
                        .IsRequired()
                        .HasColumnName("Template_Name")
                        .HasColumnType("nvarchar(max)");

                    b.HasDiscriminator().HasValue("Template");
                });