dotnet / EntityFramework.Docs

Documentation for Entity Framework Core and Entity Framework 6
https://docs.microsoft.com/ef/
Creative Commons Attribution 4.0 International
1.59k stars 1.95k forks source link

Keyless entity without a table - broken in 7.0 #4144

Open RichardD2 opened 1 year ago

RichardD2 commented 1 year ago

As per this thread, I have a keyless entity which is not mapped to a table or view:

public sealed class StringSplitResult
{
    public string Value { get; set; } = string.Empty;
}

...

modelBuilder.Entity<StringSplitResult>().HasNoKey().ToTable(null, t => t.ExcludeFromMigrations());

This works perfectly in EF Core 6.0.

Immediately after upgrading to 7.0, when I try to add a migration, I get:

System.ArgumentNullException: Value cannot be null. (Parameter 'name')
  at Microsoft.EntityFrameworkCore.Utilities.Check.NotNull[T](T value, String parameterName)
  at Microsoft.EntityFrameworkCore.RelationalEntityTypeBuilderExtensions.ToTable[TEntity](EntityTypeBuilder1 entityTypeBuilder, String name, String schema, Action1 buildAction)
  at Microsoft.EntityFrameworkCore.RelationalEntityTypeBuilderExtensions.ToTable[TEntity](EntityTypeBuilder1 entityTypeBuilder, String name, Action1 buildAction)

EF Core version: 7.0.0 Database provider: Microsoft.EntityFrameworkCore.SqlServer Target framework: .NET 6.0 Operating system: Windows 11 22H2 IDE: Visual Studio 2022 17.4

RichardD2 commented 1 year ago

To work around this, I had to change the configuration to:

.ToTable(t => t.ExcludeFromMigrations())

I then had to modify the model snapshot and the designer for every existing migration to change:

b.ToTable((string)null, t => t.ExcludeFromMigrations());

to:

b.ToTable(t => t.ExcludeFromMigrations());

However, I have no idea whether this is the correct approach, or what side-effects this change will have.

AndriySvyryd commented 1 year ago

This is an unfortunate side effect of dotnet/efcore#19811. To achieve the same effect of .ToTable((string)null, t => t.ExcludeFromMigrations()); a migration needs to be created (or existing one modified) with .ToTable(t => t.ExcludeFromMigrations()); then call .ToTable((string)null); and create another migration.

We'll need to document this as a breaking change

RichardD2 commented 1 year ago

@AndriySvyryd Thanks for the reply. How would I fix it for existing migrations? Is it enough to edit them to remove the (string)null parameter? And would there be any side-effects of not having the .ToTable((string)null) call?

AndriySvyryd commented 1 year ago

Calling .ToTable(t => t.ExcludeFromMigrations()) instead of .ToTable((string)null) means that migrations will ignore it, but if you query the entity type then it will try to query the default table unless you have some configuration that overrides it (e.g. ToView). Also, if there are derived types in the model some validation could fail if the configuration is not compatible with the default table.

RichardD2 commented 1 year ago

@AndriySvyryd For this specific entity, it's a private nested and sealed class used to represent the results of a STRING_SPLIT call, and it's only every queried with a .FromSqlInterpolated call, so hopefully that shouldn't be a problem.

ajcvickers commented 1 year ago

Note from triage: document this as a breaking change.