Belangrijke wijzigingen in EF Core 9 (EF9)

Op deze pagina worden API's en gedragswijzigingen beschreven die het potentieel hebben om bestaande toepassingen te onderbreken die worden bijgewerkt van EF Core 8 naar EF Core 9. Controleer eerdere belangrijke wijzigingen als u een eerdere versie van EF Core bijwerkt:

Doelplatform

EF Core 9 richt zich op .NET 8. Dit betekent dat bestaande toepassingen die gericht zijn op .NET 8, dit kunnen blijven doen. Toepassingen die gericht zijn op oudere versies van .NET, .NET Core en .NET Framework, moeten zich richten op .NET 8 of .NET 9 om EF Core 9 te kunnen gebruiken.

Samenvatting

Notitie

Als u Azure Cosmos DB gebruikt, raadpleegt u de sectie aparte hieronder over Azure Cosmos DB breaking changes.

wijziging die fouten veroorzaken impact
Er wordt een uitzondering opgeworpen bij het toepassen van migraties als er modelwijzigingen in behandeling zijn Hoog
Uitzondering wordt gegenereerd bij het toepassen van migraties in een expliciete transactie Hoog
Microsoft.EntityFrameworkCore.Design niet gevonden bij het gebruik van EF-tools Gemiddeld
EF.Functions.Unhex() geeft nu terug byte[]? Laag
Gecompileerde modellen verwijzen nu rechtstreeks naar waardeconvertermethoden Laag
De ariteit van SqlFunctionExpression's nullbaarheidargumenten is gevalideerd Laag
ToString() methode retourneert nu een lege string voor null exemplaren Laag
gedeelde frameworkafhankelijkheden zijn bijgewerkt naar 9.0.x Laag
EF-hulpprogramma's bieden geen ondersteuning meer voor .NET Framework-projecten Laag
EF.Constant() en EF.Parameter() werkt niet meer binnen gecompileerde query's Laag
Sommige NoTrackingWithIdentityResolution query's zijn nu niet toegestaan voor JSON-verzamelingen Laag
Alle in behandeling zijnde migraties worden toegepast in één transactie Laag

Wijzigingen met hoge impact

Er wordt een uitzondering gegenereerd bij het toepassen van migraties als er modelwijzigingen in behandeling zijn

Volgprobleem #33732

Oud gedrag

Als het model wijzigingen in behandeling heeft vergeleken met de laatste migratie, worden deze niet toegepast met de rest van de migraties wanneer Migrate wordt aangeroepen.

Nieuw gedrag

Vanaf EF Core 9.0 wordt, als het model wijzigingen in behandeling heeft vergeleken met de laatste migratie, een uitzondering gegenereerd wanneer dotnet ef database update, Migrate of MigrateAsync wordt aangeroepen:

Het model voor context DbContext heeft wijzigingen die in behandeling zijn. Voeg een nieuwe migratie toe voordat u de database bijwerkt. Deze uitzondering kan worden onderdrukt of geregistreerd door gebeurtenis-id 'RelationalEventId.PendingModelChangesWarning' door te geven aan de methode ConfigureWarnings in DbContext.OnConfiguring of AddDbContext.

Waarom

Vergeet een nieuwe migratie toe te voegen nadat u modelwijzigingen hebt aangebracht, is een veelvoorkomende fout die in sommige gevallen moeilijk kan worden vastgesteld. De nieuwe uitzondering zorgt ervoor dat het model van de app overeenkomt met de database nadat de migraties zijn toegepast.

Maatregelen

Er zijn verschillende veelvoorkomende situaties waarin deze uitzondering kan worden gegenereerd:

  • Er zijn helemaal geen migraties. Dit is gebruikelijk wanneer de database op andere manieren wordt bijgewerkt.

    • Risicobeperking: als u niet van plan bent om migraties te gebruiken voor het beheer van het databaseschema, verwijdert u de Migrate of MigrateAsync aanroep, anders voegt u een migratie toe.
  • Er is ten minste één migratie, maar de momentopname van het model ontbreekt. Dit is gebruikelijk voor migraties die handmatig zijn gemaakt.

    • Mitigation: Voeg een nieuwe migratie toe met EF-hulpprogramma's. Dit zal de momentopname van het datamodel bijwerken.
  • Het model is niet gewijzigd door de ontwikkelaar, maar het is gebouwd op een niet-deterministische manier waardoor EF het kan detecteren als gewijzigd. Dit komt vaak voor wanneer new DateTime(), DateTime.Now, DateTime.UtcNowof Guid.NewGuid() worden gebruikt in objecten die aan HasDataworden geleverd.

    • Risicobeperking: voeg een nieuwe migratie toe, bekijk de inhoud ervan om de oorzaak te vinden en vervang de dynamische gegevens door een statische, vastgelegde waarde in het model. De migratie moet opnieuw worden gemaakt nadat het model is opgelost. Als dynamische gegevens moeten worden gebruikt voor seeding, kunt u overwegen het nieuwe seedingpatroon te gebruiken in plaats van HasData.
  • De laatste migratie is gemaakt voor een andere provider dan degene die is gebruikt om de migraties toe te passen.

  • De migraties worden dynamisch gegenereerd, gewijzigd of gekozen door enkele EF-services te vervangen.

    • Risicobeperking: de waarschuwing is in dit geval een vals alarm en moet worden genegeerd.
      options.ConfigureWarnings(w => w.Ignore(Microsoft.EntityFrameworkCore.Diagnostics.RelationalEventId.PendingModelChangesWarning))
  • U gebruikt ASP.NET Core Identiteit en wijzigopties die van invloed zijn op het model, zoals:

    .AddDefaultIdentity<ApplicationUser>(options =>
        {
            options.Stores.SchemaVersion = IdentitySchemaVersions.Version2;
            options.Stores.MaxLengthForKeys = 256;
            options.SignIn.RequireConfirmedAccount = false;
        })
    
    • Risicobeperking: Om ervoor te zorgen dat de opties consistent worden toegepast, moet de applicatie worden ingesteld als opstartproject bij het uitvoeren van de EF-hulpprogramma's of, als alternatief, IDesignTimeDbContextFactory moet worden geïmplementeerd in het project dat DbContext bevat.

      public class DatabaseContextDesignTimeFactory : IDesignTimeDbContextFactory<DatabaseContext>
      {
          public DatabaseContext CreateDbContext(string[] args)
          {
              var services = new ServiceCollection();
              AddIdentity(services);
              var serviceProvider = services.BuildServiceProvider();
              var optionsBuilder = new DbContextOptionsBuilder<DatabaseContext>();
              optionsBuilder.UseApplicationServiceProvider(serviceProvider);
              optionsBuilder.UseSqlServer();
              return new DatabaseContext(optionsBuilder.Options);
          }
      
          public static IServiceCollection AddIdentity(IServiceCollection services)
          {
              services.AddDefaultIdentity<ApplicationUser>(options =>
                  {
                      options.Stores.SchemaVersion = IdentitySchemaVersions.Version2;
                      options.Stores.MaxLengthForKeys = 256;
                      options.SignIn.RequireConfirmedAccount = false;
                  })
                  .AddRoles<IdentityRole>()
                  .AddEntityFrameworkStores<DatabaseContext>();
      
              return services;
          }
      }
      

Als uw scenario niet onder een van de bovenstaande gevallen valt en wanneer u een nieuwe migratie toevoegt, wordt elke keer dezelfde migratie of een lege migratie gemaakt en wordt de uitzondering nog steeds gegenereerd, maakt u een klein project voor opnieuw maken en het met het EF-team delen in een nieuw probleem.

Er wordt een uitzondering gegenereerd bij het toepassen van migraties in een expliciete transactie

volgprobleem #17578

Oud gedrag

Voor het flexibel toepassen van migraties is het volgende patroon vaak gebruikt:

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

Nieuw gedrag

Vanaf EF Core 9.0 starten Migrate- en MigrateAsync-aanroepen een transactie en voeren de opdrachten uit met behulp van een ExecutionStrategy en als uw app het bovenstaande patroon gebruikt, wordt er een uitzondering gegenereerd:

Er is een fout gegenereerd voor waarschuwing 'Microsoft. EntityFrameworkCore.Migrations.MigrationsUserTransactionWarning': er is een transactie gestart voordat migraties worden toegepast. Dit voorkomt dat een databasevergrendeling wordt verkregen en daarom wordt de database niet beschermd tegen gelijktijdige migratietoepassingen. De transacties en uitvoeringsstrategie worden indien nodig al beheerd door EF. Verwijder de externe transactie. Deze uitzondering kan worden onderdrukt of geregistreerd door gebeurtenis-id 'RelationalEventId.MigrationsUserTransactionWarning' door te geven aan de methode ConfigureWarnings in DbContext.OnConfiguring of AddDbContext.

Waarom

Als u een expliciete transactie gebruikt, voorkomt u dat een databasevergrendeling wordt verkregen en wordt de database daarom niet beschermd tegen gelijktijdige migratietoepassingen, maar wordt ook EF beperkt over hoe de transacties intern kunnen worden beheerd.

Maatregelen

Als er slechts één database-aanroep binnen de transactie is, verwijdert u de externe transactie en ExecutionStrategy:

await dbContext.Database.MigrateAsync(cancellationToken);

Als voor uw scenario een expliciete transactie is vereist en u een ander mechanisme hebt om gelijktijdige migratietoepassing te voorkomen, negeert u de waarschuwing:

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

Wijzigingen met gemiddelde impact

Microsoft.EntityFrameworkCore.Design niet gevonden bij het gebruik van EF tools

bijhouden van probleem #35265

Oud gedrag

Vroeger moesten de EF-hulpprogramma's op de volgende manier worden aangeroepen.

    <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="*.0.0">
      <PrivateAssets>all</PrivateAssets>
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
    </PackageReference>

Nieuw gedrag

Vanaf .NET SDK 9.0.200 wordt er een uitzondering gegenereerd wanneer een EF-hulpprogramma wordt aangeroepen:

Kan bestand of assembly 'Microsoft.EntityFrameworkCore.Design, Culture=neutral, PublicKeyToken=null' niet laden. Het systeem kan het opgegeven bestand niet vinden.

Waarom

EF-hulpprogramma's waren afhankelijk van een niet-gedocumenteerd gedrag van .NET SDK waardoor privéassets werden opgenomen in het gegenereerde bestand .deps.json. Dit is opgelost in sdk#45259. Helaas voldoet de EF-wijziging om hiervoor rekening te houden niet aan de onderhoudsbalk voor EF 9.0.x, dus deze wordt opgelost in EF 10.

Maatregelen

Als tijdelijke oplossing kunt u, voordat EF 10 wordt uitgebracht, de referentie van de Design assembly als publiceerbaar markeren.

    <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.1">
      <PrivateAssets>all</PrivateAssets>
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
      <Publish>true</Publish>
    </PackageReference>

Dit wordt opgenomen in het gegenereerde bestand .deps.json, maar heeft een neveneffect van het kopiëren van Microsoft.EntityFrameworkCore.Design.dll naar de uitvoer en het publiceren van mappen.

Wijzigingen met lage impact

EF.Functions.Unhex() geeft nu byte[]? terug

Volgprobleem #33864

Oud gedrag

De EF.Functions.Unhex() functie is eerder geannoteerd om byte[]te retourneren.

Nieuw gedrag

Vanaf EF Core 9.0 wordt Unhex() nu geannoteerd om byte[]?te retourneren.

Waarom

Unhex() wordt omgezet in de functie SQLite unhex, die NULL retourneert voor ongeldige invoer. Als gevolg hiervan heeft Unhex()null geretourneerd voor die gevallen, in strijd met de annotatie.

Maatregelen

Als u zeker weet dat de tekstinhoud die wordt doorgegeven aan Unhex() een geldige hexadecimale tekenreeks vertegenwoordigt, kunt u de operator null-forgiving toevoegen als een verklaring dat de aanroep nooit null retourneert:

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

Voeg anders runtimecontroles toe voor null op de retourwaarde van Unhex().

Gecompileerde modellen verwijzen nu rechtstreeks naar waardeconvertermethoden

Probleem met traceren #35033

Oud gedrag

Voorheen zou EF bij het gebruik van waardeconversieprogramma's met gecompileerde modellen (met behulp van dotnet ef dbcontext optimize) verwijzen naar het type conversieprogramma en werkte alles correct.

public sealed class BooleanToCharConverter() : ValueConverter<bool, char>(v => ConvertToChar(v), v => ConvertToBoolean(v))
{
    public static readonly BooleanToCharConverter Default = new();

    private static char ConvertToChar(bool value) // Private method
        => value ? 'Y' : 'N';

    private static bool ConvertToBoolean(char value) // Private method
        => value == 'Y';
}

Nieuw gedrag

Vanaf EF Core 9.0 genereert EF code die rechtstreeks verwijst naar de conversiemethoden zelf. Als deze methoden privé zijn, mislukt de compilatie.

Waarom

Deze wijziging was nodig om NativeAOT te ondersteunen.

Maatregelen

Maak de methoden waarnaar wordt verwezen door waardeconversieprogramma's openbaar of intern in plaats van privé.

De ariteit van nullability-argumenten van SqlFunctionExpression is gevalideerd.

bijhouden van probleem #33852

Oud gedrag

Eerder was het mogelijk om een SqlFunctionExpression te maken met een ander aantal argumenten en argumenten voor null-propagatie.

Nieuw gedrag

Vanaf EF Core 9.0 wordt EF nu gegooid als het aantal argumenten en de doorgifteargumenten voor null-baarheid niet overeenkomen.

Waarom

Het niet overeenkomende aantal argumenten en argumenten voor het doorgeven van null-waarden kan leiden tot onverwacht gedrag.

Maatregelen

Zorg ervoor dat de argumentsPropagateNullability hetzelfde aantal elementen heeft als de arguments. Wanneer je twijfelt, gebruik false voor een null-argument.

ToString() methode retourneert nu een lege tekenreeks voor null exemplaren

Volgkwestie #33941

Oud gedrag

Voorheen gaf EF inconsistente resultaten voor de methode ToString() wanneer de argumentwaarde nullwas. Bijvoorbeeld: ToString() op de eigenschap bool? met waarde null retourneerde null, maar voor uitdrukkingen zonder eigenschap bool?, waarvan de waarde null was, retourneerde het True. Het gedrag was ook inconsistent voor andere datatypen, bijvoorbeeld ToString() op null waarde-enum gaf een lege tekenreeks terug.

Nieuw gedrag

Vanaf EF Core 9.0 retourneert de ToString() methode nu in alle gevallen een lege tekenreeks wanneer de argumentwaarde wordt null.

Waarom

Het oude gedrag was inconsistent in verschillende gegevenstypen en situaties, en is niet afgestemd op het C#-gedrag.

Maatregelen

Als u wilt terugkeren naar het oude gedrag, moet u de query dienovereenkomstig herschrijven:

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

Gedeelde frameworkafhankelijkheden zijn bijgewerkt naar 9.0.x

Oud gedrag

Apps die gebruikmaken van de Microsoft.NET.Sdk.Web SDK en het targetten van net8.0, zullen pakketten zoals System.Text.Json, Microsoft.Extensions.Caching.Memory, Microsoft.Extensions.Configuration.Abstractions, Microsoft.Extensions.Logging en Microsoft.Extensions.DependencyModel uit het gedeelde framework halen, zodat deze assembly's normaal gesproken niet met de app worden geïmplementeerd.

Nieuw gedrag

Ef Core 9.0 ondersteunt nog steeds net8.0, maar verwijst nu naar de 9.0.x-versies van System.Text.Json, Microsoft.Extensions.Caching.Memory, Microsoft.Extensions.Configuration.Abstractions, Microsoft.Extensions.Logging en Microsoft.Extensions.DependencyModel. Apps die zijn gericht op net8.0, kunnen niet gebruikmaken van het gedeelde framework om te voorkomen dat deze assembly's worden geïmplementeerd.

Waarom

De overeenkomende afhankelijkheidsversies bevatten de nieuwste beveiligingsoplossingen en het gebruik ervan vereenvoudigt het onderhoudsmodel voor EF Core.

Maatregelen

Wijzig uw app om deze te richten op net9.0 om het vorige gedrag te herstellen.

EF-hulpprogramma's bieden geen ondersteuning meer voor .NET Framework-projecten

Traceringsprobleem #37745

Oud gedrag

Voorheen werkten de EF Core-hulpprogramma's (dotnet-ef CLI en Pakketbeheer Console) samen met projecten die gericht zijn op .NET Framework.

Nieuw gedrag

Vanaf EF Core 9.0 werken de EF Core-hulpprogramma's niet meer met projecten die zijn gericht op .NET Framework. De hulpprogramma's produceren een fout wanneer het opstartproject is gericht op .NET Framework.

Waarom

De huidige versie van EF Core-hulpprogramma's werkt met alle ondersteunde EF Core-versies en er zijn geen ondersteunde EF Core-versies meer die op .NET Framework werken.

Maatregelen

Werk uw project bij naar .NET (bijvoorbeeld .NET 8 of hoger). Als uw project momenteel is gericht op .NET Framework, raadpleegt u de handleiding porteren voor informatie over het migreren naar .NET.

EF.Constant() en EF.Parameter() werkt niet meer binnen gecompileerde query's

Traceringsprobleem #33674

Oud gedrag

In EF Core 8 Constant en Parameter kan worden gebruikt binnen gecompileerde query's (CompileQuery en CompileAsyncQuery):

var lookbackDays = 7;

var compiledQuery = EF.CompileAsyncQuery(
    (AppDbContext db) => db.Blogs
        .Where(b => b.PublishedOn >= DateTime.Today.AddDays(EF.Constant(-lookbackDays))));

Nieuw gedrag

Vanaf EF Core 9.0 genereert het gebruik Constant of Parameter binnen een gecompileerde query een InvalidCastException:

Unable to cast object of type 'System.Linq.Expressions.ConstantExpression' to type 'System.Linq.Expressions.ParameterExpression'.

Waarom

De interne implementatie van Constant is gewijzigd om volledige querycompilatie te voorkomen voor elke verschillende constante waarde. De vorige implementatie introduceerde constanten knooppunten vroegtijdig in de querypijplijn (vóór het query-caching), waardoor dure cachemissers werden veroorzaakt wanneer een andere waarde werd doorgegeven aan EF.Constant(). De nieuwe implementatie verwerkt deze methoden in een later stadium, wat niet compatibel is met gecompileerde query's.

Maatregelen

Verwijder ofwel de aanroep Constant of Parameter uit de gecompileerde query, of stop met het gebruik van een gecompileerde query voor die specifieke query. Houd er rekening mee dat het verwijderen van EF.Constant() ertoe leidt dat de waarde wordt verzonden als een SQL-parameter in plaats van als een constante geïnlineerd, wat de prestaties van het queryplan kan beïnvloeden.

Sommige NoTrackingWithIdentityResolution query's zijn nu niet toegestaan voor JSON-verzamelingen

Volgprobleem #33073

Oud gedrag

Voorheen kon het gebruik van AsNoTrackingWithIdentityResolution (of het instellen van NoTrackingWithIdentityResolution) bij query's die aan JSON toegewezen entiteitsverzamelingen bevatten, ongemerkt onjuiste resultaten of gegevenscorruptie veroorzaken, afhankelijk van de volgorde waarin entiteiten tijdens de materialisatie werden verwerkt. Bovendien kunnen dergelijke query's in sommige scenario's een niet-helpende Invalid token type: 'StartObject' uitzondering veroorzaken.

Nieuw gedrag

Vanaf EF Core 9.0 beperkt EF Core het gebruik van AsNoTrackingWithIdentityResolution voor bepaalde querypatronen voor JSON-verzamelingen om stille gegevenscorruptie te voorkomen:

  • Als entiteitsexemplaren in een JSON-verzameling worden gerealiseerd in een volgorde die gegevensbeschadiging kan veroorzaken, genereert EF Core een uitzondering die de gebruiker instrueert een ander traceringsgedrag te gebruiken.
  • Het rechtstreeks gebruiken van LINQ-operators (zoals OrderBy, WhereSkip, , Take, enzovoort) in JSON-verzamelingnavigatien in een query met AsNoTrackingWithIdentityResolution() is nu verboden. Met de volgende query wordt bijvoorbeeld een uitzondering gegenereerd:
var blogs = await context.Blogs
    .AsNoTrackingWithIdentityResolution()
    .Select(b => new
    {
        Blog = b,
        TopPosts = b.JsonPosts.OrderBy(p => p.Rating).Take(3).ToList()
    })
    .ToListAsync();

Waarom

De combinatie van AsNoTrackingWithIdentityResolution en JSON-verzamelingen kan ongemerkt onjuiste gematerialiseerde objecten opleveren door de manier waarop JSON vanuit de database wordt gestreamd: geneste includes in JSON maken deel uit van de materialisatie van het bovenliggende object in plaats van afzonderlijk te worden gematerialiseerd. De zelfstandige wijzigingstracker die wordt gebruikt voor identiteitsomzetting, is afhankelijk van sleutelwaarden om entiteitsexemplaren te ontdubbelen, maar wanneer LINQ-operators worden toegepast op JSON-verzamelingen, kan EF Core deze sleutelwaarden niet betrouwbaar doorgeven aan de materializer, wat resulteert in entiteiten met null-sleutels en mogelijke beschadiging van gegevens.

Maatregelen

Gebruik een reguliere traceringsquery als identiteitsomzetting is vereist:

var blogs = await context.Blogs
    .AsTracking()
    .Select(b => new
    {
        Blog = b,
        TopPosts = b.JsonPosts.OrderBy(p => p.Rating).Take(3).ToList()
    })
    .ToListAsync();

Als u identiteitsresolutie niet nodig hebt, gebruik dan in plaats daarvan AsNoTracking:

var blogs = await context.Blogs
    .AsNoTracking()
    .Select(b => new
    {
        Blog = b,
        TopPosts = b.JsonPosts.OrderBy(p => p.Rating).Take(3).ToList()
    })
    .ToListAsync();

Alle in behandeling zijnde migraties worden toegepast in één transactie

Traceringsprobleem #34439

Oud gedrag

Voorheen zou EF Core elke migratie (standaard) in een eigen transactie verpakken bij het toepassen van migraties.

Nieuw gedrag

Vanaf EF Core 9.0 worden alle in behandeling zijnde migraties binnen één transactie toegepast als onderdeel van migratievergrendeling. Als een migratie mislukt, wordt de hele transactie teruggedraaid. Dit gedrag is teruggedraaid in EF Core 10.

Waarom

Als u alle migraties in één transactie toepast, zorgt u ervoor dat de database volledig up-to-datum of ongewijzigd blijft als er een fout optreedt, waardoor tussenliggende statussen worden vermeden.

Maatregelen

Als u transactiegedrag per migratie in EF Core 9 nodig hebt, upgrade dan naar EF Core 10, waarbij elke migratie weer in een eigen transactie wordt verpakt.

Ingrijpende wijzigingen in Azure Cosmos DB

Er is uitgebreid werk gedaan om de Azure Cosmos DB provider in 9.0 beter te maken. De wijzigingen bevatten een aantal belangrijke wijzigingen die van grote invloed zijn; Lees het volgende zorgvuldig als u een upgrade uitvoert van een bestaande toepassing.

wijziging die fouten veroorzaken impact
De discriminatoreigenschap heet nu $type in plaats van Discriminator Hoog
De eigenschap id bevat standaard de discriminator niet meer Hoog
De eigenschap JSON-id is toegewezen aan de sleutel Hoog
Sync I/O via de Azure Cosmos DB-provider wordt niet meer ondersteund Gemiddeld
SQL-query's moeten nu JSON-waarden rechtstreeks projecteren Gemiddeld
Ongedefinieerde resultaten worden nu automatisch gefilterd uit queryresultaten Gemiddeld
onjuist vertaalde query's worden niet meer vertaald Gemiddeld
HasIndex gooit nu in plaats van te worden genegeerd Laag
IncludeRootDiscriminatorInJsonId hernoemd naar HasRootDiscriminatorInJsonId Laag
De versie waarnaar newtonsoft.Json wordt verwezen, is bijgewerkt van 10.0.2 naar 13.0.1 Laag

Wijzigingen met hoge impact

De discriminatoreigenschap heet nu $type in plaats van Discriminator

probleem bijhouden #34269

Oud gedrag

EF voegt automatisch een discriminatoreigenschap toe aan JSON-documenten om het entiteitstype te identificeren dat het document vertegenwoordigt. In eerdere versies van EF werd deze JSON-eigenschap standaard Discriminator genoemd.

Nieuw gedrag

Vanaf EF Core 9.0 wordt de discriminatoreigenschap nu standaard $type genoemd. Als u bestaande documenten in Azure Cosmos DB van eerdere versies van EF hebt, gebruiken deze de oude Discriminator naamgeving en na een upgrade naar EF 9.0 mislukken query's op deze documenten.

Waarom

Een opkomende JSON-praktijk maakt gebruik van een $type eigenschap in scenario's waarin het type van een document moet worden geïdentificeerd. System.Text.Json van .NET ondersteunt bijvoorbeeld ook polymorfisme, waarbij $type wordt gebruikt als standaarddiscriminatieeigenschapsnaam (docs). De standaardwaarde is gewijzigd om te worden afgestemd op de rest van het ecosysteem en het gemakkelijker te maken om te werken met externe hulpprogramma's.

Maatregelen

De eenvoudigste oplossing is om gewoon de naam van de discriminator-eigenschap te configureren als Discriminator, net zoals vóór het gebruik van HasDiscriminator.

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

Als u dit doet voor al uw entiteitstypen op het hoogste niveau, gedraagt EF zich net als voorheen.

Als u al uw documenten wilt bijwerken om de nieuwe $type naamgeving te gebruiken, zodat u de bovenstaande configuratie niet meer nodig hebt, kunt u dit doen met behulp van de Azure Cosmos DB SDK. De volgende code wijzigt de naam van de Discriminator eigenschap $type in alle documenten in een container:

using Microsoft.Azure.Cosmos;
using System.Text.Json.Nodes;

// Replace with your Cosmos DB connection string.
var cosmosClient = new CosmosClient(connectionString);
var container = cosmosClient.GetContainer("myDatabase", "myContainer");

using var feedIterator = container.GetItemQueryIterator<JsonObject>("SELECT * FROM c WHERE IS_DEFINED(c.Discriminator)");
while (feedIterator.HasMoreResults)
{
    foreach (var item in await feedIterator.ReadNextAsync())
    {
        if (item.ContainsKey("Discriminator"))
        {
            item["$type"] = item["Discriminator"]!.DeepClone();
            item.Remove("Discriminator");

            // The id and partition key are required for ReplaceItemAsync.
            // Adjust the partition key extraction to match your container's partition key path.
            var id = item["id"]!.GetValue<string>();
            var partitionKey = new PartitionKey(item["myPartitionKeyProperty"]!.GetValue<string>());
            await container.ReplaceItemAsync(item, id, partitionKey);
        }
    }
}

Notitie

Vervang door myPartitionKeyProperty de naam van de eigenschap die is geconfigureerd als de partitiesleutel in uw container. Als uw container het document id als partitiesleutel gebruikt, gebruikt u id ook als waarde voor de partitiesleutel.

De eigenschap id bevat nu standaard alleen de eigenschap EF-sleutel

Volgen van issue #34179

Oud gedrag

Voorheen heeft EF de discriminatorwaarde van uw entiteitstype ingevoegd in de eigenschap id van het document. Als u bijvoorbeeld een Blog entiteitstype hebt opgeslagen met een eigenschap Id met 8, bevat de eigenschap JSON idBlog|8.

Nieuw gedrag

Vanaf EF Core 9.0 bevat de eigenschap JSON id niet langer de discriminatorwaarde en bevat deze alleen de waarde van uw sleuteleigenschap. In het bovenstaande voorbeeld is de eigenschap JSON id gewoon 8. Als u bestaande documenten in Azure Cosmos DB uit eerdere versies van EF hebt, hebben deze de discriminatorwaarde in de eigenschap JSON id en na een upgrade naar EF 9.0 mislukken query's op deze documenten.

Waarom

Omdat de eigenschap JSON-id uniek moet zijn, is de discriminator er eerder aan toegevoegd om verschillende entiteiten met dezelfde sleutelwaarde toe te staan. Het was bijvoorbeeld mogelijk om zowel een Blog als een Post te hebben, met een eigenschap Id die de waarde 8 bevat in dezelfde container en partitie. Dit is beter afgestemd op relationele databasegegevensmodelleringspatronen, waarbij elk entiteitstype wordt toegewezen aan een eigen tabel en daarom een eigen sleutelruimte heeft.

EF 9.0 heeft de mapping over het algemeen gewijzigd om beter aan te sluiten bij de gebruikelijke Azure Cosmos DB NoSQL praktijken en verwachtingen, in plaats van aan de verwachtingen van gebruikers uit relationele databases te voldoen. Bovendien maakt het met de discrimorwaarde in de eigenschap id het moeilijker voor externe hulpprogramma's en systemen om te communiceren met door EF gegenereerde JSON-documenten; dergelijke externe systemen zijn over het algemeen niet op de hoogte van de EF-discriminatorwaarden, die standaard zijn afgeleid van .NET typen.

Maatregelen

De eenvoudigste oplossing is om EF te configureren voor het opnemen van de discriminator in de JSON-eigenschap id, zoals voorheen met HasDiscriminatorInJsonId. Er is voor dit doel een nieuwe configuratieoptie geïntroduceerd:

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

Als u dit doet voor al uw entiteitstypen op het hoogste niveau, gedraagt EF zich net als voorheen.

Als u alle bestaande documenten wilt bijwerken om de discriminerende waarde uit de id eigenschap te verwijderen zodat u de bovenstaande configuratie niet meer nodig hebt, kunt u dit doen met behulp van de Azure Cosmos DB SDK. Houd er rekening mee dat dit alleen mogelijk is als entiteiten van verschillende typen niet dezelfde sleutelwaarde delen binnen dezelfde containerpartitie.

Azure Cosmos DB staat het bijwerken van de id eigenschap van een bestaand document niet toe. In plaats daarvan moet u het oude document verwijderen en een nieuw document maken met de bijgewerkte idversie. Met de volgende code wordt deze migratie uitgevoerd:

using Microsoft.Azure.Cosmos;
using System.Text.Json.Nodes;

// Replace with your Cosmos DB connection string.
var cosmosClient = new CosmosClient(connectionString);
var container = cosmosClient.GetContainer("myDatabase", "myContainer");

// Replace with the name of the property configured as the partition key in your container.
// If the partition key path is /id, set this to "id".
const string partitionKeyProperty = "myPartitionKeyProperty";

using var feedIterator = container.GetItemQueryIterator<JsonObject>("SELECT * FROM c");
while (feedIterator.HasMoreResults)
{
    foreach (var item in await feedIterator.ReadNextAsync())
    {
        var oldId = item["id"]!.GetValue<string>();
        var separatorIndex = oldId.IndexOf('|');
        if (separatorIndex != -1)
        {
            var newId = oldId[(separatorIndex + 1)..];

            // Capture the partition key value before updating the id.
            // When partitionKeyProperty is "id", this captures the old id for the delete operation.
            var partitionKeyForDelete = new PartitionKey(item[partitionKeyProperty]!.GetValue<string>());

            // Update the id to the new value without the discriminator prefix.
            item["id"] = JsonValue.Create(newId);

            // When partitionKeyProperty is "id", read it again after the update to get the new id for the create operation.
            var partitionKeyForCreate = new PartitionKey(item[partitionKeyProperty]!.GetValue<string>());

            // Azure Cosmos DB does not allow changing the id - create a new document and delete the old one.
            await container.CreateItemAsync(item, partitionKeyForCreate);
            await container.DeleteItemAsync<JsonObject>(oldId, partitionKeyForDelete);
        }
    }
}

Notitie

Stel partitionKeyProperty in op de naam van de eigenschap die is geconfigureerd als de partitiesleutel in uw container. Wanneer het pad naar de partitiesleutel is /id, wordt in het voorbeeld de oude id waarde voor de verwijderbewerking en de nieuwe id waarde voor de maakbewerking correct gebruikt, omdat partitionKeyProperty deze zowel vóór als na het bijwerken item["id"]wordt gelezen.

Als er zich een crash voordoet tussen de CreateItemAsync- en DeleteItemAsync-aanroepen, kunt u uiteindelijk beide documenten hebben. Wanneer u de migratie opnieuw uitvoert, kunt u dit afhandelen door 409 Conflict van CreateItemAsync op te vangen en vervolgens door te gaan met de aanroep van DeleteItemAsync om het oude document te verwijderen.

De eigenschap JSON id is toegewezen aan de sleutel

Volgen van issue #34179

Oud gedrag

Eerder heeft EF een schaduweigenschap gemaakt die is toegewezen aan de eigenschap JSON id, tenzij een van de eigenschappen expliciet is toegewezen aan id.

Nieuw gedrag

Vanaf EF Core 9 wordt de sleuteleigenschap, indien mogelijk, toegewezen aan de JSON-id eigenschap. Dit betekent dat de sleuteleigenschap niet langer wordt bewaard in het document onder een andere naam met dezelfde waarde, zodat niet-EF-code die de documenten verbruikt en erop vertrouwt dat deze eigenschap aanwezig is, niet meer goed werkt.

Waarom

EF 9.0 heeft de mapping algemeen gewijzigd om beter aan te sluiten bij de gangbare Azure Cosmos DB NoSQL-praktijken en -verwachtingen. En het is niet gebruikelijk om de sleutelwaarde tweemaal in het document op te slaan.

Maatregelen

Als u het gedrag van EF Core 8 wilt behouden, is de eenvoudigste oplossing om te gebruiken HasShadowId, een nieuwe configuratieoptie die voor dit doel is geïntroduceerd:

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

Als u dit doet voor al uw entiteitstypen op het hoogste niveau, gedraagt EF zich net als voorheen. U kunt deze ook toepassen op alle entiteitstypen in het model met één aanroep met behulp van HasShadowIds:

modelBuilder.HasShadowIds();

Als u het nieuwe EF 9.0-gedrag wilt toepassen en uw bestaande documenten de sleuteleigenschap bevatten die afzonderlijk is opgeslagen (bijvoorbeeld naast "Id": 8"id": "Blog|8"elkaar), kunt u de redundante sleuteleigenschap verwijderen met behulp van de Azure Cosmos DB SDK. Als u ook de id eigenschapsindeling migreert (waarbij het discriminatorvoorvoegsel wordt verwijderd), kunt u beide stappen combineren, zoals hierboven weergegeven in de id-eigenschapsmigratie. In het volgende voorbeeld wordt een redundante "Id" eigenschap verwijderd als de id migratie al is uitgevoerd:

using Microsoft.Azure.Cosmos;
using System.Text.Json.Nodes;

// Replace with your Cosmos DB connection string.
var cosmosClient = new CosmosClient(connectionString);
var container = cosmosClient.GetContainer("myDatabase", "myContainer");

// Adjust "Id" to match your entity's key property name.
using var feedIterator = container.GetItemQueryIterator<JsonObject>("SELECT * FROM c WHERE IS_DEFINED(c.Id)");
while (feedIterator.HasMoreResults)
{
    foreach (var item in await feedIterator.ReadNextAsync())
    {
        item.Remove("Id");

        var id = item["id"]!.GetValue<string>();

        // Adjust the partition key extraction to match your container's partition key path.
        var partitionKey = new PartitionKey(item["myPartitionKeyProperty"]!.GetValue<string>());
        await container.ReplaceItemAsync(item, id, partitionKey);
    }
}

Notitie

Vervang Id door de naam van de sleuteleigenschap van uw entiteit en myPartitionKeyProperty door de naam van de eigenschap die is geconfigureerd als de partitiesleutel in uw container.

Wijzigingen met gemiddelde impact

I/O synchroniseren via de Azure Cosmos DB-provider wordt niet meer ondersteund

Volgprobleem #32563

Oud gedrag

Voorheen zouden synchrone methoden zoals ToList of SaveChanges ertoe leiden dat EF Core synchroon blokkeert met behulp van .GetAwaiter().GetResult() bij het uitvoeren van asynchrone aanroepen met de Azure Cosmos DB SDK. Dit kan leiden tot een impasse.

Nieuw gedrag

Vanaf EF Core 9.0 wordt EF nu standaard uitgevoerd wanneer wordt geprobeerd synchrone I/O te gebruiken. Het uitzonderingsbericht is 'Azure Cosmos DB biedt geen ondersteuning voor synchrone I/O. Zorg ervoor dat u alleen asynchrone methoden gebruikt en wacht alleen op asynchrone methoden wanneer u Entity Framework Core gebruikt voor toegang tot Azure Cosmos DB. Zie https://aka.ms/ef-cosmos-nosync voor meer informatie.

Waarom

Synchrone blokkering op asynchrone methoden kan leiden tot deadlocks en de SDK van Azure Cosmos DB ondersteunt alleen asynchrone methoden.

Maatregelen

In EF Core 9.0 kan de fout worden onderdrukt met:

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

Dat gezegd hebbende, moeten toepassingen stoppen met het gebruik van synchronisatie-API's met Azure Cosmos DB, omdat dit niet wordt ondersteund door de Azure Cosmos DB SDK. De mogelijkheid om de uitzondering te onderdrukken, wordt verwijderd in een toekomstige release van EF Core, waarna de enige optie is om asynchrone API's te gebruiken.

SQL-query's moeten nu JSON-waarden rechtstreeks projecteren

Volgprobleem #25527

Oud gedrag

Eerder gegenereerde EF-query's zoals de volgende:

SELECT c["City"] FROM root c

Dergelijke query's zorgen ervoor dat Azure Cosmos DB elk resultaat in een JSON-object verpakt, als volgt:

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

Vanaf EF Core 9.0 voegt EF nu de VALUE modifier als volgt toe aan query's:

SELECT VALUE c["City"] FROM root c

Dergelijke query's zorgen ervoor dat Azure Cosmos DB de waarden rechtstreeks retourneert, zonder dat ze worden verpakt:

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

Als uw toepassing gebruikmaakt van SQL-query's, worden dergelijke query's waarschijnlijk verbroken na een upgrade naar EF 9.0, omdat ze de VALUE modifier niet bevatten.

Waarom

Het verpakken van elk resultaat in een extra JSON-object kan leiden tot prestatievermindering in sommige scenario's, bloats de nettolading van het JSON-resultaat en is niet de natuurlijke manier om met Azure Cosmos DB te werken.

Maatregelen

Als u dit wilt beperken, voegt u de VALUE modifier toe aan de projecties van uw SQL-query's, zoals hierboven wordt weergegeven.

Ongedefinieerde resultaten worden nu automatisch gefilterd uit de queryresultaten.

Volgprobleem #25527

Oud gedrag

Eerder gegenereerde EF-query's zoals de volgende:

SELECT c["City"] FROM root c

Dergelijke query's zorgen ervoor dat Azure Cosmos DB elk resultaat in een JSON-object verpakt, als volgt:

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

Als een van de resultaten niet is gedefinieerd (bijvoorbeeld de eigenschap City niet aanwezig was in het document), werd er een leeg document geretourneerd en zou EF null voor dat resultaat retourneren.

Nieuw gedrag

Vanaf EF Core 9.0 voegt EF nu de VALUE modifier als volgt toe aan query's:

SELECT VALUE c["City"] FROM root c

Dergelijke query's zorgen ervoor dat Azure Cosmos DB de waarden rechtstreeks retourneert, zonder dat ze worden verpakt:

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

Het Azure Cosmos DB gedrag is om automatisch undefined waarden uit de resultaten te filteren. Dit betekent dat als een van de eigenschappen City niet aanwezig is in het document, de query slechts één resultaat retourneert, in plaats van twee resultaten, waarbij één null.

Waarom

Het verpakken van elk resultaat in een extra JSON-object kan leiden tot prestatievermindering in sommige scenario's, bloats de nettolading van het JSON-resultaat en is niet de natuurlijke manier om met Azure Cosmos DB te werken.

Maatregelen

Als het belangrijk is voor uw toepassing om null-waarden te verkrijgen voor niet-gedefinieerde resultaten, verbind dan de undefined-waarden tot null met behulp van de nieuwe EF.Functions.Coalesce-operator.

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

Onjuist vertaalde query's worden niet meer vertaald

Traceringsprobleem #34123

Oud gedrag

Eerder vertaalde EF vragen zoals deze:

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

De SQL-vertaling voor deze query is echter onjuist:

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

In SQL wordt de WHERE-component geëvalueerd voordat de OFFSET- en LIMIT-componenten; maar in de bovenstaande LINQ-query wordt de operator Take weergegeven vóór de operator Where. Als gevolg hiervan kunnen dergelijke query's onjuiste resultaten retourneren.

Nieuw gedrag

Vanaf EF Core 9.0 worden dergelijke query's niet meer vertaald en wordt er een uitzondering gegenereerd.

Waarom

Onjuiste vertalingen kunnen leiden tot beschadiging van gegevens op de achtergrond, wat kan leiden tot moeilijk te detecteren fouten in uw toepassing. EF geeft altijd de voorkeur aan een fail-fast aanpak door vroegtijdig fouten op te werpen, in plaats van mogelijk gegevenscorruptie te veroorzaken.

Maatregelen

Als u tevreden bent met het vorige gedrag en dezelfde SQL wilt uitvoeren, wisselt u gewoon de volgorde van LINQ-operators om:

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

Helaas biedt Azure Cosmos DB momenteel geen ondersteuning voor de component OFFSET en LIMIT in SQL-subquery's. Dit is wat de juiste vertaling van de oorspronkelijke LINQ-query vereist.

Wijzigingen met lage impact

HasIndex gooit nu in plaats van genegeerd te worden

probleem bijhouden #34023

Oud gedrag

Voorheen werden aanroepen naar HasIndex genegeerd door de EF Cosmos DB-provider.

Nieuw gedrag

De provider genereert nu als HasIndex is opgegeven.

Waarom

In Azure Cosmos DB worden alle eigenschappen standaard geïndexeerd en hoeft er geen indexering te worden opgegeven. Hoewel het mogelijk is om een aangepast indexeringsbeleid te definiëren, wordt dit momenteel niet ondersteund door EF en kan dit worden gedaan via de Azure Portal zonder EF-ondersteuning. Omdat HasIndex oproepen niets uitrichtten, zijn ze niet langer toegestaan.

Maatregelen

Verwijder alle aanroepen naar HasIndex.

IncludeRootDiscriminatorInJsonId werd hernoemd naar HasRootDiscriminatorInJsonId na de release van 9.0.0-rc.2

Probleem bijhouden #34717

Oud gedrag

De IncludeRootDiscriminatorInJsonId API is geïntroduceerd in 9.0.0 rc.1.

Nieuw gedrag

Voor de definitieve release van EF Core 9.0 is de naam van de API gewijzigd in HasRootDiscriminatorInJsonId

Waarom

Een andere gerelateerde API is hernoemd om te beginnen met Has in plaats van Include, en dus is deze ook hernoemd voor consistentie.

Maatregelen

Als uw code gebruikmaakt van de IncludeRootDiscriminatorInJsonId-API, verander dit naar HasRootDiscriminatorInJsonId.

De versie waarnaar newtonsoft.Json wordt verwezen, is bijgewerkt van 10.0.2 naar 13.0.1

Oud gedrag

De Cosmos-provider verwijst naar Newtonsoft.Json versie 10.0.2.

Nieuw gedrag

Vanaf EF Core 9.0 verwijst de Cosmos-provider naar Newtonsoft.Json versie 13.0.1.

Waarom

De eerder gebruikte versie van Newtonsoft.Json heeft bekende beveiligingsproblemen. De versie is bijgewerkt om te voorkomen dat deze afhankelijk is van een pakketversie met bekende beveiligingsproblemen.

Maatregelen

De upgrade naar Newtonsoft.Json 13.0.1 mag in de meeste gevallen geen problemen veroorzaken. Als uw toepassing Newtonsoft.Json rechtstreeks gebruikt en afhankelijk is van een specifieke oudere versie, kunt u uw toepassing bijwerken zodat deze compatibel is met Newtonsoft.Json 13.0.1 of hoger. Zie de opmerkingen bij de release van Newtonsoft.Json voor meer informatie over wijzigingen tussen versies.