Dela via


Icke-bakåtkompatibla ändringar i EF Core 9 (EF9)

Den här sidan dokumenterar API och beteendeändringar som kan bryta befintliga program som uppdateras från EF Core 8 till EF Core 9. Se till att granska tidigare icke-bakåtkompatibla ändringar om du uppdaterar från en tidigare version av EF Core:

Målramverk

EF Core 9 riktar in sig på .NET 8. Det innebär att befintliga program som är avsedda för .NET 8 kan fortsätta att göra det. Program som riktar sig till äldre .NET-, .NET Core- och .NET Framework-versioner måste rikta in sig på .NET 8 eller .NET 9 för att använda EF Core 9.

Sammanfattning

Not

Om du använder Azure Cosmos DB kan du läsa avsnittet separat nedan om azure Cosmos DB-icke-bakåtkompatibla ändringar.

Brytande ändring Effekt
undantag utlöses när migreringar tillämpas om det finns väntande modelländringar Hög
Undantag utlöses när migreringar tillämpas i en explicit transaktion Hög
EF.Functions.Unhex() returnerar nu byte[]? Låg
SqlFunctionExpression's nullabilitetsargumentens ariteter validerade Låg
ToString()-metoden returnerar nu en tom sträng för null instanser Låg
Delade ramverksberoenden uppdaterades till 9.0.x Låg

Ändringar med hög påverkan

Ett undantag kastas vid tillämpning av migreringar om det förekommer väntande modelländringar.

spårningsproblem #33732

Gammalt beteende

Om modellen har väntande ändringar jämfört med den senaste migreringen tillämpas de inte med resten av migreringarna när Migrate anropas.

Nytt beteende

Från och med EF Core 9.0, om modellen har väntande ändringar jämfört med den senaste migreringen utlöses ett undantag när dotnet ef database update, Migrate eller MigrateAsync anropas:

Modellen för kontexten DbContext har väntande ändringar. Lägg till en ny migrering innan du uppdaterar databasen. Det här undantaget kan ignoreras eller loggas genom att skicka händelse-ID:t "RelationalEventId.PendingModelChangesWarning" till metoden "ConfigureWarnings" i "DbContext.OnConfiguring" eller "AddDbContext".

Varför

Att glömma att lägga till en ny migrering efter att ha gjort modelländringar är ett vanligt misstag som kan vara svårt att diagnostisera i vissa fall. Det nya undantaget säkerställer att appens modell matchar databasen efter att migreringarna har tillämpats.

Åtgärder

Det finns flera vanliga situationer när det här undantaget kan utlöses:

  • Det finns inga migreringar alls. Detta är vanligt när databasen uppdateras på annat sätt.
    • Mitigation: Om du inte planerar att använda migreringar för att hantera databasschemat tar du bort Migrate- eller MigrateAsync-anropet, annars lägger du till en migrering.
  • Det finns minst en migrering, men modellögonblicksbilden saknas. Detta är vanligt för migreringar som skapas manuellt.
    • Mitigation: Lägg till en ny migrering med hjälp av EF-verktyg, detta uppdaterar modellögonblicksbilden.
  • Modellen ändrades inte av utvecklaren, men den är inbyggd på ett icke-deterministiskt sätt, vilket gör att EF identifierar den som ändrad. Detta är vanligt när new DateTime(), DateTime.Now, DateTime.UtcNoweller Guid.NewGuid() används i objekt som levereras till HasData().
    • Mitigation: Lägg till en ny migrering, granska dess innehåll för att hitta orsaken och ersätt dynamiska data med ett statiskt, hårdkodat värde i modellen. Migreringen ska återskapas när modellen har åtgärdats. Om dynamiska data måste användas för seeding bör du överväga att använda det nya seeding-mönstret i stället för HasData().
  • Den senaste migreringen skapades för en annan provider än den som användes för att tillämpa migreringarna.
  • Migreringarna genereras eller väljs dynamiskt genom att ersätta några av EF-tjänsterna.
    • Mitigation: Varningen är ett falskt alarm i det här fallet och bör undertryckas:

      options.ConfigureWarnings(w => w.Ignore(RelationalEventId.PendingModelChangesWarning))

Om ditt scenario inte faller under något av ovanstående fall och att lägga till en ny migrering skapar samma migrering varje gång eller en tom migrering och undantaget fortfarande uppstår, skapa ett litet repro-projekt och dela det med EF-teamet i ett nytt ärende.

Undantag utlöses när migreringar tillämpas i en explicit transaktion

spårningsproblem #17578

Gammalt beteende

För att tillämpa migreringar elastiskt användes ofta följande mönster:

await dbContext.Database.CreateExecutionStrategy().ExecuteAsync(async () =>
{
    await using var transaction = await dbContext.Database.BeginTransactionAsync(cancellationToken);
    await dbContext.Database.MigrateAsync(cancellationToken);
    await transaction.CommitAsync(cancellationToken);
});

Nytt beteende

Från och med EF Core 9.0 startar Migrate- och MigrateAsync-anrop en transaktion och kör kommandona med hjälp av en ExecutionStrategy och om appen använder ovanstående mönster utlöses ett undantag:

Ett fel genererades för varningen "Microsoft.EntityFrameworkCore.Migrations.MigrationsUserTransactionWarning": En transaktion startades innan migreringar tillämpades. Detta förhindrar att ett databaslås hämtas och därför skyddas inte databasen från samtidiga migreringsprogram. Transaktionsstrategin och utförandestrategin hanteras redan av EF efter behov. Ta bort den externa transaktionen. Det här undantaget kan ignoreras eller loggas genom att skicka händelse-ID:t "RelationalEventId.MigrationsUserTransactionWarning" till metoden "ConfigureWarnings" i "DbContext.OnConfiguring" eller "AddDbContext".

Varför

Med hjälp av en explicit transaktion förhindras att ett databaslås hämtas och därför skyddas databasen inte från samtidiga migreringsprogram, utan begränsar även EF för hur den kan hantera transaktionerna internt.

Åtgärder

Om det bara finns ett databasanrop i transaktionen tar du bort den externa transaktionen och ExecutionStrategy:

await dbContext.Database.MigrateAsync(cancellationToken);

Annars, om ditt scenario kräver en explicit transaktion och du har en annan mekanism för att förhindra samtidiga migreringsprogram, ignorerar du varningen:

options.ConfigureWarnings(w => w.Ignore(RelationalEventId.MigrationsUserTransactionWarning))

Ändringar med låg påverkan

EF.Functions.Unhex() returnerar nu byte[]?

spårningsproblem #33864

Gammalt beteende

Funktionen EF.Functions.Unhex() kommenterades tidigare för att returnera byte[].

Nytt beteende

Från och med EF Core 9.0 är Unhex() anmärkt för att returnera byte[]?.

Varför

Unhex() översätts till funktionen SQLite unhex, som returnerar NULL för ogiltiga indata. Som ett resultat returnerade Unhex()null för de fallen, i strid med anvisningarna.

Åtgärder

Om du är säker på att textinnehållet som skickas till Unhex() representerar en giltig hexadecimal sträng kan du helt enkelt lägga till operatorn null-forgiving som ett påstående om att anropet aldrig returnerar null:

var binaryData = await context.Blogs.Select(b => EF.Functions.Unhex(b.HexString)!).ToListAsync();

Annars, lägg till körningstidkontroller för null på returvärdet för Unhex().

SqlFunctionExpressions nullitetsarguments aritet validerat

spårningsproblem #33852

Gammalt beteende

Tidigare var det möjligt att skapa en SqlFunctionExpression med ett annat antal argument och nullabilitetsspridningsargument.

Nytt beteende

Från och med EF Core 9.0 utlöser EF nu om antalet argument och nullabilitetsspridningsargument inte matchar.

Varför

Att ha ett icke-matchande antal argument och argument för nullbarhetspropagering kan leda till oväntat beteende.

Åtgärder

Kontrollera att argumentsPropagateNullability har samma antal element som arguments. När du är osäker använder du false för nullability-argument.

ToString()-metoden returnerar nu tom sträng för null instanser

spårningsproblem #33941

Gammalt beteende

Tidigare returnerade EF inkonsekventa resultat för metoden ToString() när argumentvärdet null. T.ex. ToString() på egenskapen bool? med värdet null returnerade null, men för icke-egenskapsuttrycken bool? vars värde var null returnerade det True. Beteendet var också inkonsekvent för andra datatyper, t.ex. ToString()null värdeuppräkning returnerade tom sträng.

Nytt beteende

Från och med EF Core 9.0 returnerar ToString()-metoden nu konsekvent en tom sträng i alla fall när argumentvärdet är null.

Varför

Det gamla beteendet var inkonsekvent mellan olika datatyper och situationer, samt inte i linje med det C#-beteendet.

Åtgärder

Om du vill återgå till det gamla beteendet skriver du om frågan i enlighet med detta:

var newBehavior = context.Entity.Select(x => x.NullableBool.ToString());
var oldBehavior = context.Entity.Select(x => x.NullableBool == null ? null : x.NullableBool.ToString());

Beroenden för delat ramverk uppdaterades till 9.0.x

Gammalt beteende

Appar som använder Microsoft.NET.Sdk.Web SDK och riktar sig mot net8.0 skulle lösa paket som System.Text.Json, Microsoft.Extensions.Caching.Memory, Microsoft.Extensions.Configuration.Abstractions, Microsoft.Extensions.Logging och Microsoft.Extensions.DependencyModel från det delade ramverket, så att dessa assemblies normalt inte levereras med appen.

Nytt beteende

EF Core 9.0 stöder fortfarande net8.0 men refererar nu till 9.0.x-versionerna av System.Text.Json, Microsoft.Extensions.Caching.Memory, Microsoft.Extensions.Configuration.Abstractions, Microsoft.Extensions.Logging och Microsoft.Extensions.DependencyModel. Appar som riktar in sig på net8.0 kan inte utnyttja det delade ramverket för att undvika att distribuera dessa sammansättningar.

Varför

Matchande beroendeversioner innehåller de senaste säkerhetskorrigeringarna och att använda dem förenklar servicemodellen för EF Core.

Åtgärder

Ändra så att appen riktar sig mot net9.0 för att återfå tidigare beteende.

Ändringar som bryter bakåtkompatibiliteten i Azure Cosmos DB

Omfattande arbete har gjorts för att göra Azure Cosmos DB-providern bättre i 9.0. Ändringarna innefattar ett antal högpåverkande kritiska ändringar; om du uppgraderar ett befintligt program, läs följande noggrant.

Icke-bakåtkompatibel ändring Påverkan
Den diskriminerande egendomen heter nu $type i stället för Discriminator Hög
Egenskapen id innehåller inte längre diskrimineringen som standard Hög
Egenskapen JSON id mappas till nyckeln Hög
Synkron I/O via Azure Cosmos DB-leverantören stöds inte längre Mellan
SQL-frågor måste nu projicera JSON-värden direkt Medel
Odefinierade resultat filtreras nu automatiskt från frågeresultat Medel
Felaktigt översatta frågor översätts inte längre Mellan
HasIndex kastar nu i stället för att ignoreras Låg
IncludeRootDiscriminatorInJsonId bytte namn till HasRootDiscriminatorInJsonId efter 9.0.0-rc.2 Låg

Ändringar med hög påverkan

Den diskriminerande egenskapen heter nu $type i stället för Discriminator

spårningsproblem #34269

Gammalt beteende

EF lägger automatiskt till en diskriminerande egenskap i JSON-dokument för att identifiera den entitetstyp som dokumentet representerar. I tidigare versioner av EF brukade den här JSON-egenskapen namnges Discriminator som standard.

Nytt beteende

Från och med EF Core 9.0 kallas den diskriminerande egenskapen nu $type som standard. Om du har befintliga dokument i Azure Cosmos DB från tidigare versioner av EF använder dessa gamla Discriminator namngivning, och när du har uppgraderat till EF 9.0 misslyckas frågor mot dessa dokument.

Varför

En ny JSON-metod använder en $type-egenskap i scenarier där ett dokuments typ måste identifieras. Till exempel. NET:s System.Text.Json stöder också polymorfism med $type som standardnamn för diskriminerande egenskap (dokument). För att anpassa sig till resten av ekosystemet och göra det lättare att samverka med externa verktyg ändrades standardinställningen.

Åtgärder

Den enklaste åtgärden är att helt enkelt konfigurera namnet på diskriminatoregenskapen till Discriminator, precis som tidigare.

modelBuilder.Entity<Session>().HasDiscriminator<string>("Discriminator");

Om du gör detta för alla dina entitetstyper på den översta nivån fungerar EF precis som tidigare.

Om du vill kan du nu även uppdatera alla dokument så att de använder den nya $type namngivning.

Egenskapen id innehåller nu endast egenskapen EF-nyckel som standard

spårningsproblem #34179

Gammalt beteende

Tidigare infogade EF det diskriminerande värdet för din entitetstyp i dokumentets id-egenskap. Om du till exempel har sparat en Blog entitetstyp med en Id-egenskap som innehåller 8, innehåller egenskapen JSON idBlog|8.

Nytt beteende

Från och med EF Core 9.0 innehåller JSON-id-egenskapen inte längre det diskriminerande värdet och innehåller bara värdet för din nyckelegenskap. I exemplet ovan skulle egenskapen JSON id helt enkelt vara 8. Om du har befintliga dokument i Azure Cosmos DB från tidigare versioner av EF har dessa det diskriminerande värdet i JSON-id-egenskapen, och när du har uppgraderat till EF 9.0 misslyckas frågor mot dessa dokument.

Varför

Eftersom JSON-id-egenskapen måste vara unik har diskrimineringen tidigare lagts till för att tillåta att olika entiteter med samma nyckelvärde finns. Detta gjorde det till exempel möjligt att ha både en Blog och en Post med en Id egenskap som innehåller värdet 8 i samma container och partition. Detta överensstämmer bättre med relationsdatabasens datamodelleringsmönster, där varje entitetstyp mappas till sin egen tabell och därför har sitt eget nyckelutrymme.

EF 9.0 ändrade i allmänhet mappningen så att den var mer anpassad till vanliga Azure Cosmos DB NoSQL-metoder och förväntningar, i stället för att motsvara förväntningarna hos användare som kommer från relationsdatabaser. Dessutom har det diskriminerande värdet i den id egenskapen gjort det svårare för externa verktyg och system att interagera med EF-genererade JSON-dokument. sådana externa system är inte allmänt medvetna om ef-diskriminerande värden, som som standard härleds från .NET-typer.

Åtgärder

Den enklaste lösningen är att helt enkelt konfigurera EF för att inkludera diskrimineringen i JSON-id-egenskapen, som tidigare. Ett nytt konfigurationsalternativ har introducerats för detta ändamål:

modelBuilder.Entity<Session>().HasDiscriminatorInJsonId();

Om du gör detta för alla dina entitetstyper på den översta nivån fungerar EF precis som tidigare.

Om du vill kan du uppdatera alla dokument för att skriva om deras JSON-id egenskap. Observera att detta endast är möjligt om entiteter av olika typer inte delar samma ID-värde i samma container.

Egenskapen JSON id mappas till nyckeln

spårningsproblem #34179

Gammalt beteende

Tidigare skapade EF en skuggegenskap som mappats till egenskapen JSON id, såvida inte en av egenskaperna mappades till id explicit.

Nytt beteende

Från och med EF Core 9 mappas nyckelegenskapen till JSON-id-egenskapen enligt konventionen om möjligt. Det innebär att nyckelegenskapen inte längre sparas i dokumentet under ett annat namn med samma värde, så att icke-EF-kod som använder dokumenten och som förlitar sig på den här egenskapen inte längre fungerar korrekt.

Varför

EF 9.0 ändrade i allmänhet mappningen till att vara mer anpassad till vanliga Azure Cosmos DB NoSQL-metoder och förväntningar. Och det är inte vanligt att lagra nyckelvärdet två gånger i dokumentet.

Åtgärder

Om du vill bevara EF Core 8-beteendet är den enklaste lösningen att använda ett nytt konfigurationsalternativ som har introducerats för detta ändamål:

modelBuilder.Entity<Session>().HasShadowId();

Om du gör detta för alla dina entitetstyper på den översta nivån fungerar EF precis som tidigare. Eller så kan du tillämpa den på alla entitetstyper i modellen med ett anrop:

modelBuilder.HasShadowIds();

Ändringar med medelpåverkan

Synkroniserings-I/O via Azure Cosmos DB-providern stöds inte längre

spårningsproblem #32563

Gammalt beteende

Tidigare skulle anrop av synkrona metoder som ToList eller SaveChanges orsaka att EF Core blockerar synkront med .GetAwaiter().GetResult() när man gjorde asynkrona anrop mot Azure Cosmos DB SDK. Detta kan leda till ett dödläge.

Nytt beteende

Från och med EF Core 9.0 genererar EF nu som standard vid försök att använda synkron I/O. Undantagsmeddelandet är "Azure Cosmos DB stöder inte synkron I/O. Se till att endast asynkrona metoder används och väntar på rätt sätt när du använder Entity Framework Core för att få åtkomst till Azure Cosmos DB. Mer information finns i https://aka.ms/ef-cosmos-nosync."

Varför

Synkron blockering på asynkrona metoder kan leda till dödläge och Azure Cosmos DB SDK stöder endast asynkrona metoder.

Åtgärder

I EF Core 9.0 kan felet ignoreras med:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
    optionsBuilder.ConfigureWarnings(w => w.Ignore(CosmosEventId.SyncNotSupported));
}

Med detta sagt bör program sluta använda synkroniserings-API:er med Azure Cosmos DB eftersom detta inte stöds av Azure Cosmos DB SDK. Möjligheten att utelämna undantaget tas bort i en framtida version av EF Core, varefter det enda alternativet är att använda asynkrona API:er.

SQL-frågor måste nu projicera JSON-värden direkt

spårningsproblem #25527

Gammalt beteende

Tidigare genererade EF frågor, till exempel följande:

SELECT c["City"] FROM root c

Sådana frågor gör att Azure Cosmos DB omsluter varje resultat i ett JSON-objekt enligt följande:

[
    {
        "City": "Berlin"
    },
    {
        "City": "México D.F."
    }
]
Nytt beteende

Från och med EF Core 9.0 lägger EF nu till VALUE-modifieraren i frågor på följande sätt:

SELECT VALUE c["City"] FROM root c

Sådana frågor gör att Azure Cosmos DB returnerar värdena direkt, utan att omslutas:

[
    "Berlin",
    "México D.F."
]

Om ditt program använder SQL-frågorbryts sådana frågor troligen efter uppgradering till EF 9.0 eftersom de inte innehåller VALUE-modifieraren.

Varför

Omslutning av varje resultat i ett ytterligare JSON-objekt kan orsaka prestandaförsämring i vissa scenarier, öka JSON-resultatets storlek och är inte det naturliga sättet att arbeta med Azure Cosmos DB.

Åtgärder

Du kan undvika problemet genom att lägga till VALUE modifierare i projektionerna för dina SQL-frågor, som du ser ovan.

Odefinierade resultat filtreras nu automatiskt från frågeresultat

spårningsproblem #25527

Gammalt beteende

Tidigare genererade EF frågor, till exempel följande:

SELECT c["City"] FROM root c

Sådana frågor gör att Azure Cosmos DB omsluter varje resultat i ett JSON-objekt enligt följande:

[
    {
        "City": "Berlin"
    },
    {
        "City": "México D.F."
    }
]

Om något av resultaten var odefinierat (t.ex. att egenskapen City saknades i dokumentet) returnerades ett tomt dokument och EF skulle returnera null för det resultatet.

Nytt beteende

Från och med EF Core 9.0 lägger EF nu till VALUE-modifieraren i frågor på följande sätt:

SELECT VALUE c["City"] FROM root c

Sådana frågor gör att Azure Cosmos DB returnerar värdena direkt, utan att omslutas:

[
    "Berlin",
    "México D.F."
]

Azure Cosmos DB-beteendet är att automatiskt filtrera undefined värden från resultat. Det innebär att om en av City egenskaper saknas i dokumentet returnerar frågan bara ett enda resultat, i stället för två resultat, där en är null.

Varför

Omslutning av varje resultat i ett ytterligare JSON-objekt kan orsaka prestandaförsämring i vissa scenarier, öka storleken på JSON-resultatnyttolasten och är inte det naturliga sättet att arbeta med Azure Cosmos DB.

Åtgärder

Om det är viktigt att få null värden för ditt program när resultaten är odefinierade, slå samman undefined värden till null med hjälp av den nya operatorn EF.Functions.Coalesce.

var users = await context.Customer
    .Select(c => EF.Functions.CoalesceUndefined(c.City, null))
    .ToListAsync();

Felaktigt översatta frågor översätts inte längre

spårningsproblem #34123

Gammalt beteende

Tidigare översatte EF frågor, till exempel följande:

var sessions = await context.Sessions
    .Take(5)
    .Where(s => s.Name.StartsWith("f"))
    .ToListAsync();

SQL-översättningen för den här frågan var dock felaktig:

SELECT c
FROM root c
WHERE ((c["Discriminator"] = "Session") AND STARTSWITH(c["Name"], "f"))
OFFSET 0 LIMIT @__p_0

I SQL utvärderas WHERE-satsen innan satserna OFFSET och LIMIT. men i LINQ-frågan ovan visas Take-operatorn före Where-operatorn. Därför kan sådana frågor returnera felaktiga resultat.

Nytt beteende

Från och med EF Core 9.0 översätts inte längre sådana frågor och ett undantag genereras.

Varför

Felaktiga översättningar kan orsaka att tysta data skadas, vilket kan leda till svårupptäckta buggar i ditt program. EF föredrar alltid att felsnabbt genom att kasta i förväg i stället för att eventuellt orsaka skadade data.

Åtgärder

Om du var nöjd med det tidigare beteendet och vill köra samma SQL växlar du bara runt ordningen på LINQ-operatorer:

var sessions = await context.Sessions
    .Where(s => s.Name.StartsWith("f"))
    .Take(5)
    .ToListAsync();

Azure Cosmos DB stöder tyvärr för närvarande inte OFFSET- och LIMIT-satser i SQL-underfrågor, vilket är vad korrekt översättning av den ursprungliga LINQ-frågan kräver.

Ändringar med låg påverkan

HasIndex kastar nu i stället för att ignoreras

spårningsproblem #34023

Gammalt beteende

Tidigare ignorerades anrop till HasIndex av EF Cosmos DB-providern.

Nytt beteende

Providern genererar nu om HasIndex har angetts.

Varför

I Azure Cosmos DB indexeras alla egenskaper som standard och ingen indexering behöver anges. Även om det är möjligt att definiera en anpassad indexeringsprincip stöds detta för närvarande inte av EF och kan göras via Azure-portalen utan EF-stöd. Eftersom HasIndex samtal inte hade någon effekt, är de inte längre tillåtna.

Förmildrande åtgärder

Ta bort alla anrop till HasIndex.

IncludeRootDiscriminatorInJsonId bytte namn till HasRootDiscriminatorInJsonId efter 9.0.0-rc.2

spårningsproblem #34717

Gammalt beteende

API:et IncludeRootDiscriminatorInJsonId introducerades i 9.0.0 rc.1.

Nytt beteende

För den slutliga versionen av EF Core 9.0 har API:et bytt namn till HasRootDiscriminatorInJsonId

Varför

Ett annat relaterat API har bytt namn för att börja med Has i stället för Include, och därför har det här också bytt namn för konsekvens.

Åtgärder

Om koden använder IncludeRootDiscriminatorInJsonId-API:et ändrar du den till att referera till HasRootDiscriminatorInJsonId i stället.