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:
- Icke-bakåtkompatibla ändringar i EF Core 8
- Icke-bakåtkompatibla ändringar i EF Core 7
- Oförenliga ändringar i EF Core 6
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.
Ändringar med hög påverkan
Ett undantag kastas vid tillämpning av migreringar om det förekommer väntande modelländringar.
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
- ellerMigrateAsync
-anropet, annars lägger du till en migrering.
-
Mitigation: Om du inte planerar att använda migreringar för att hantera databasschemat tar du bort
- 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.UtcNow
ellerGuid.NewGuid()
används i objekt som levereras tillHasData()
.-
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()
.
-
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
- Den senaste migreringen skapades för en annan provider än den som användes för att tillämpa migreringarna.
- Mitigation: Det här är ett scenario som inte stöds. Varningen kan ignoreras med hjälp av kodfragmentet nedan, men det här scenariot slutar sannolikt att fungera i en framtida EF Core-version. Den rekommenderade lösningen är för att generera en separat uppsättning migreringar för varje provider.
- 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
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[]?
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
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
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()
på 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.
Ändringar med hög påverkan
Den diskriminerande egenskapen heter nu $type
i stället för Discriminator
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
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 id
Blog|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
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
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
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
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
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
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
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.