Zásadní změny v EF Core 8 (EF8)

Tato stránka dokumentuje rozhraní API a změny chování, které mají potenciál přerušit stávající aplikace, které se aktualizují z EF Core 7 na EF Core 8. Pokud se aktualizujete ze starší verze EF Core, nezapomeňte si projít dřívější zásadní změny:

Cílová architektura

EF Core 8 cílí na .NET 8. Aplikace, které cílí na starší verze .NET, .NET Core a .NET Framework, budou muset aktualizovat na cíl .NET 8.

Shrnutí

Změna způsobující chybu Dopad
Contains V dotazech LINQ může přestat fungovat na starších verzích SQL Serveru. Vysoká
Výčty ve formátu JSON se ve výchozím nastavení ukládají jako inty místo řetězců. Vysoká
SQL Server date a time teď generování uživatelského rozhraní pro .NET DateOnly a TimeOnly Střední
Logické sloupce s vygenerovanou hodnotou databáze se už nevygenerují jako prázdné. Střední
Metody SQLite Math se teď překládají na SQL. Nízká
ITypeBase nahrazuje IEntityType v některých rozhraních API. Nízká
Výrazy ValueGenerator musí používat veřejná rozhraní API. Nízká
ExcludeFromMigrations už nevyloučí jiné tabulky v hierarchii TPC. Nízká
Neudržované celočíselné klíče se uchovávají v dokumentech cosmos. Nízká
Relační model se generuje v kompilovaném modelu. Nízká
Generování uživatelského rozhraní může generovat různé názvy navigace Nízká
Diskriminátor má nyní maximální délku Nízká
Hodnoty klíčů SQL Serveru se porovnávají bez rozlišování velkých a malých písmen. Nízká

Změny s vysokým dopadem

Contains V dotazech LINQ může přestat fungovat na starších verzích SQL Serveru.

Problém se sledováním č. 13617

Staré chování

Dříve, když Contains byl operátor použit v dotazech LINQ se seznamem parametrizovaných hodnot, ef vygeneroval SQL, který byl neefektivní, ale fungoval na všech verzích SQL Serveru.

Nové chování

Od EF Core 8.0 teď EF generuje SQL, který je efektivnější, ale nepodporuje se na SQL Serveru 2014 a níže.

Upozorňujeme, že novější verze SQL Serveru můžou být nakonfigurované se starší úrovní kompatibility, takže jsou nekompatibilní s novým SQL Serverem. K tomu může dojít také u databáze Azure SQL, která byla migrována z předchozí místní instance SQL Serveru, která přenesla starou úroveň kompatibility.

Proč

Předchozí SQL vygenerovaný EF Core pro Contains vložení parametrizovaných hodnot jako konstanty v SQL. Například následující dotaz LINQ:

var names = new[] { "Blog1", "Blog2" };

var blogs = await context.Blogs
    .Where(b => names.Contains(b.Name))
    .ToArrayAsync();

... by se přeložila do následujícího SQL:

SELECT [b].[Id], [b].[Name]
FROM [Blogs] AS [b]
WHERE [b].[Name] IN (N'Blog1', N'Blog2')

Takové vložení konstantních hodnot do SQL vytváří mnoho problémů s výkonem, porazí ukládání plánů dotazů do mezipaměti a způsobuje nepotřebné vyřazení jiných dotazů. Nový překlad EF Core 8.0 používá funkci SQL Serveru OPENJSON k přenosu hodnot jako pole JSON. Tím se řeší problémy s výkonem, které jsou součástí předchozí techniky; funkce OPENJSON však není k dispozici v SQL Serveru 2014 a níže.

Další informace o této změně najdete v tomto blogovém příspěvku.

Omezení rizik

Pokud je vaše databáze SQL Server 2016 (13.x) nebo novější, nebo pokud používáte Azure SQL, zkontrolujte nakonfigurovanou úroveň kompatibility databáze pomocí následujícího příkazu:

SELECT name, compatibility_level FROM sys.databases;

Pokud je úroveň kompatibility nižší než 130 (SQL Server 2016), zvažte jeho úpravu na novější hodnotu (dokumentaci).

Jinak platí, že pokud je vaše verze databáze starší než SQL Server 2016 nebo je nastavená na starou úroveň kompatibility, kterou z nějakého důvodu nemůžete změnit, nakonfigurujte EF Core tak, aby se vrátila ke staršímu, méně efektivnímu SQL následujícím způsobem:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    => optionsBuilder
        .UseSqlServer(@"<CONNECTION STRING>", o => o.UseCompatibilityLevel(120));

Výčty ve formátu JSON se ve výchozím nastavení ukládají jako inty místo řetězců.

Problém se sledováním č. 13617

Staré chování

V EF7 jsou výčty mapované na JSON ve výchozím nastavení uloženy jako řetězcové hodnoty v dokumentu JSON.

Nové chování

Od EF Core 8.0 teď EF ve výchozím nastavení mapuje výčty na celočíselné hodnoty v dokumentu JSON.

Proč

EF má ve výchozím nastavení vždy namapované výčty na číselný sloupec v relačních databázích. Vzhledem k tomu, že EF podporuje dotazy, ve kterých hodnoty z JSON pracují s hodnotami ze sloupců a parametrů, je důležité, aby hodnoty ve formátu JSON odpovídaly hodnotám ve sloupci, který není json.

Omezení rizik

Chcete-li pokračovat v používání řetězců, nakonfigurujte vlastnost výčtu pomocí převodu. Příklad:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<User>().Property(e => e.Status).HasConversion<string>();
}

Nebo pro všechny vlastnosti typu výčtu::

protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder)
{
    configurationBuilder.Properties<StatusEnum>().HaveConversion<string>();
}

Změny se středním dopadem

SQL Server date a time teď generování uživatelského rozhraní pro .NET DateOnly a TimeOnly

Problém se sledováním č. 24507

Staré chování

Při generování databáze SQL Serveru se date sloupci nebo time v minulosti ef vygeneroval vlastnosti entity s typy DateTime a TimeSpan.

Nové chování

Počínaje EF Core 8.0 date a time jsou vygenerovány jako DateOnly a TimeOnly.

Proč

DateOnly a TimeOnly byly zavedeny v .NET 6.0 a jsou ideální pro mapování typů data a času databáze. DateTime obsahuje komponentu času, která se nevyužívá, a může způsobit nejasnost při mapování na date, a TimeSpan představuje časový interval – pravděpodobně i dny – místo času dne, kdy dojde k události. Použití nových typů zabraňuje chybám a nejasnostem a poskytuje přehlednost záměru.

Omezení rizik

Tato změna má vliv jenom na uživatele, kteří pravidelně znovu vygenerují svou databázi do modelu kódu EF (tok "database-first").

Na tuto změnu doporučujeme reagovat úpravou kódu tak, aby používal nově vygenerované a TimeOnly vygenerované DateOnly typy. Pokud to ale není možné, můžete upravit šablony generování, abyste se vrátili k předchozímu mapování. Uděláte to tak, že nastavíte šablony, jak je popsáno na této stránce. Potom upravte EntityType.t4 soubor, vyhledejte, kde se vygenerují vlastnosti entity (vyhledejte property.ClrType) a změňte kód na následující:

        var clrType = property.GetColumnType() switch
        {
            "date" when property.ClrType == typeof(DateOnly) => typeof(DateTime),
            "date" when property.ClrType == typeof(DateOnly?) => typeof(DateTime?),
            "time" when property.ClrType == typeof(TimeOnly) => typeof(TimeSpan),
            "time" when property.ClrType == typeof(TimeOnly?) => typeof(TimeSpan?),
            _ => property.ClrType
        };

        usings.AddRange(code.GetRequiredUsings(clrType));

        var needsNullable = Options.UseNullableReferenceTypes && property.IsNullable && !clrType.IsValueType;
        var needsInitializer = Options.UseNullableReferenceTypes && !property.IsNullable && !clrType.IsValueType;
#>
    public <#= code.Reference(clrType) #><#= needsNullable ? "?" : "" #> <#= property.Name #> { get; set; }<#= needsInitializer ? " = null!;" : "" #>
<#

Logické sloupce s vygenerovanou hodnotou databáze se už nevygenerují jako prázdné.

Sledování problému č. 15070

Staré chování

Dříve se nenulové bool sloupce s výchozím omezením databáze vygenerovaly jako vlastnosti s možnou bool? hodnotou null.

Nové chování

Od EF Core 8.0 se sloupce bez hodnoty null bool vždy vygenerují jako nenulové vlastnosti.

Proč

Vlastnost bool nebude mít její hodnotu odeslána do databáze, pokud je falsetato hodnota , což je CLR výchozí. Pokud má databáze výchozí hodnotu true pro sloupec, pak i když je falsehodnota vlastnosti , hodnota v databázi skončí jako true. V EF8 ale sentinel použitý k určení, jestli má vlastnost hodnotu, může být změněna. To se provádí automaticky pro bool vlastnosti s vygenerovanou hodnotou truedatabáze , což znamená, že už není nutné vygenerovat vlastnosti jako null.

Omezení rizik

Tato změna má vliv jenom na uživatele, kteří pravidelně znovu vygenerují svou databázi do modelu kódu EF (tok "database-first").

Na tuto změnu doporučujeme reagovat úpravou kódu tak, aby používal logickou vlastnost bez hodnoty null. Pokud to ale není možné, můžete upravit šablony generování, abyste se vrátili k předchozímu mapování. Uděláte to tak, že nastavíte šablony, jak je popsáno na této stránce. Potom upravte EntityType.t4 soubor, vyhledejte, kde se vygenerují vlastnosti entity (vyhledejte property.ClrType) a změňte kód na následující:

#>
        var propertyClrType = property.ClrType != typeof(bool)
                              || (property.GetDefaultValueSql() == null && property.GetDefaultValue() != null)
            ? property.ClrType
            : typeof(bool?);
#>
    public <#= code.Reference(propertyClrType) #><#= needsNullable ? "?" : "" #> <#= property.Name #> { get; set; }<#= needsInitializer ? " = null!;" : "" #>
<#
<#

Změny s nízkým dopadem

Metody SQLite Math se teď překládají na SQL.

Problém se sledováním č. 18843

Staré chování

Dříve se do SQL přeložily pouze metody Math Abs, Max, Min a Round. Všichni ostatní členové by se v klientovi vyhodnotili, pokud se zobrazí v konečném výrazu select dotazu.

Nové chování

V EF Core 8.0 se všechny Math metody s odpovídajícími matematickými funkcemi SQLite překládají do SQL.

Tyto matematické funkce byly povoleny v nativní knihovně SQLite, kterou poskytujeme ve výchozím nastavení (prostřednictvím naší závislosti na balíčku NuGet SQLitePCLRaw.bundle_e_sqlite3). Byly také povoleny v knihovně poskytované SQLitePCLRaw.bundle_e_sqlcipher. Pokud používáte některou z těchto knihoven, tato změna by na vaši aplikaci neměla mít vliv.

Existuje však šance, že aplikace, včetně nativní knihovny SQLite jinými prostředky, nemusí povolit matematické funkce. V těchto případech Math se metody přeloží do SQL a při spuštění se nezobrazí žádné takové chyby funkce .

Proč

SQLite přidal integrované matematické funkce ve verzi 3.35.0. I když jsou ve výchozím nastavení zakázané, staly se dostatečně přesvědčivé, že jsme se rozhodli poskytnout jim výchozí překlady v našem poskytovateli EF Core SQLite.

Spolupracovali jsme také s EricEm Sinkem na projektu SQLitePCLRaw, abychom umožnili matematické funkce ve všech nativních knihovnách SQLite, které jsou součástí tohoto projektu.

Omezení rizik

Nejjednodušší způsob, jak opravit konce, je, pokud je to možné, povolení matematické funkce je nativní knihovna SQLite zadáním možnosti SQLITE_ENABLE_MATH_FUNCTIONS kompilace.

Pokud neřídíte kompilaci nativní knihovny, můžete také opravit konce vytvořením funkcí za běhu pomocí rozhraní API Microsoft.Data.Sqlite .

sqliteConnection
    .CreateFunction<double, double, double>(
        "pow",
        Math.Pow,
        isDeterministic: true);

Alternativně můžete vynutit vyhodnocení klienta rozdělením výrazu Select na dvě části oddělené .AsEnumerable

// Before
var query = dbContext.Cylinders
    .Select(
        c => new
        {
            Id = c.Id
            // May throw "no such function: pow"
            Volume = Math.PI * Math.Pow(c.Radius, 2) * c.Height
        });

// After
var query = dbContext.Cylinders
    // Select the properties you'll need from the database
    .Select(
        c => new
        {
            c.Id,
            c.Radius,
            c.Height
        })
    // Switch to client-eval
    .AsEnumerable()
    // Select the final results
    .Select(
        c => new
        {
            Id = c.Id,
            Volume = Math.PI * Math.Pow(c.Radius, 2) * c.Height
        });

ITypeBase nahrazuje IEntityType v některých rozhraních API.

Problém se sledováním č. 13947

Staré chování

Dříve byly všechny mapované strukturální typy typy entit.

Nové chování

Po zavedení složitých typů v EF8 se některá rozhraní API, která dříve používala, používala IEntityType , ITypeBase aby bylo možné tato rozhraní API používat s entitami nebo komplexními typy. Sem patří:

  • IProperty.DeclaringEntityType je nyní zastaralá a IProperty.DeclaringType měla by být použita.
  • IEntityTypeIgnoredConvention je nyní zastaralá a ITypeIgnoredConvention měla by být použita.
  • IValueGeneratorSelector.Selectnyní přijímá, ITypeBase který může být, ale nemusí být .IEntityType

Proč

Při zavedení složitých typů v EF8 je možné tato rozhraní API použít s rozhraním IEntityType API nebo IComplexType.

Omezení rizik

Stará rozhraní API jsou zastaralá, ale nebudou odebrána až do EF10. Kód by se měl aktualizovat tak, aby používal nová rozhraní API ASAP.

Výrazy ValueConverter a ValueComparer musí používat veřejná rozhraní API pro kompilovaný model.

Problém se sledováním č. 24896

Staré chování

ValueConverter Dříve nebyly do kompilovaného modelu zahrnuty definice, ValueComparer takže by mohly obsahovat libovolný kód.

Nové chování

EF teď extrahuje výrazy z ValueConverter objektů a ValueComparer zahrnuje tyto výrazy v jazyce C# v kompilovaném modelu. To znamená, že tyto výrazy musí používat pouze veřejné rozhraní API.

Proč

Tým EF postupně přesouvá do kompilovaného modelu další konstrukce, aby v budoucnu podporoval použití EF Core s AOT.

Omezení rizik

Zpřístupnit rozhraní API používaná porovnávačem Představte si například tento jednoduchý převaděč:

public class MyValueConverter : ValueConverter<string, byte[]>
{
    public MyValueConverter()
        : base(v => ConvertToBytes(v), v => ConvertToString(v))
    {
    }

    private static string ConvertToString(byte[] bytes)
        => ""; // ... TODO: Conversion code

    private static byte[] ConvertToBytes(string chars)
        => Array.Empty<byte>(); // ... TODO: Conversion code
}

Chcete-li tento převaděč použít v kompilovaném modelu s EF8, ConvertToString musí být tyto metody ConvertToBytes veřejné. Příklad:

public class MyValueConverter : ValueConverter<string, byte[]>
{
    public MyValueConverter()
        : base(v => ConvertToBytes(v), v => ConvertToString(v))
    {
    }

    public static string ConvertToString(byte[] bytes)
        => ""; // ... TODO: Conversion code

    public static byte[] ConvertToBytes(string chars)
        => Array.Empty<byte>(); // ... TODO: Conversion code
}

ExcludeFromMigrations už nevyloučí jiné tabulky v hierarchii TPC.

Problém se sledováním č. 30079

Staré chování

Dříve by použití ExcludeFromMigrations v tabulce v hierarchii TPC také vyloučilo další tabulky v hierarchii.

Nové chování

Od EF Core 8.0 ExcludeFromMigrations nemá vliv na ostatní tabulky.

Proč

Staré chování bylo chybou a zabránilo použití migrací ke správě hierarchií napříč projekty.

Omezení rizik

Explicitně použijte ExcludeFromMigrations pro jakoukoli jinou tabulku, která by měla být vyloučena.

Neudržované celočíselné klíče se uchovávají v dokumentech cosmos.

Problém se sledováním č. 31664

Staré chování

Dříve nebyly stínové celočíselné vlastnosti, které odpovídají kritériím syntetizované vlastnosti klíče, zachovány v dokumentu JSON, ale byly na cestě znovu syntetizovány.

Nové chování

Počínaje EF Core 8.0 jsou tyto vlastnosti nyní zachovány.

Proč

Původní chování bylo chybou a zabránilo tomu, aby vlastnosti, které odpovídají syntetizovaným klíčovým kritériím, zůstaly zachovány ve službě Cosmos.

Omezení rizik

Pokud by jeho hodnota neměla být zachována, vylučte vlastnost z modelu . Kromě toho můžete toto chování zcela zakázat nastavením Microsoft.EntityFrameworkCore.Issue31664 přepínače AppContext na true, viz AppContext pro uživatele knihovny další podrobnosti.

AppContext.SetSwitch("Microsoft.EntityFrameworkCore.Issue31664", isEnabled: true);

Relační model se generuje v kompilovaném modelu.

Problém se sledováním č. 24896

Staré chování

Relační model se dříve počítal za běhu i při použití kompilovaného modelu.

Nové chování

Od EF Core 8.0 je relační model součástí vygenerovaného modelu. U zvláště velkých modelů se však vygenerovaný soubor nemusí zkompilovat.

Proč

To bylo provedeno kvůli dalšímu zlepšení doby spuštění.

Omezení rizik

Upravte vygenerovaný *ModelBuilder.cs soubor a odeberte řádek AddRuntimeAnnotation("Relational:RelationalModel", CreateRelationalModel()); i metodu CreateRelationalModel().

Generování uživatelského rozhraní může generovat různé názvy navigace

Problém se sledováním č. 27832

Staré chování

Dříve při generování uživatelského rozhraní DbContext a typů entit z existující databáze byly názvy navigace pro relace někdy odvozeny od společné předpony více názvů sloupců cizích klíčů.

Nové chování

Od EF Core 8.0 se k generování navigačních názvů už nepoužívají běžné předpony názvů sloupců ze složeného cizího klíče.

Proč

Toto je nejasné pravidlo pojmenování, které někdy generuje velmi špatné názvy jako , S, Student_nebo dokonce jen _. Bez tohoto pravidla se už nevygenerují podivné názvy a zásady vytváření názvů pro navigaci jsou také jednodušší, což usnadňuje pochopení a predikci názvů, které názvy se vygenerují.

Omezení rizik

Nástroje EF Core Power Tools mají možnost vygenerovat navigace starým způsobem. Případně můžete vygenerovaný kód plně přizpůsobit pomocí šablon T4. Dá se použít k příkladu vlastností cizího klíče relací generování uživatelského rozhraní a použití libovolného pravidla vhodného pro váš kód k vygenerování požadovaných navigačních názvů.

Diskriminátor má nyní maximální délku

Problém sledování č. 10691

Staré chování

Dříve byly diskriminující sloupce vytvořené pro mapování dědičnosti TPH nakonfigurované jako nvarchar(max) v SQL Serveru nebo Azure SQL nebo ekvivalentní typ nevázaného řetězce v jiných databázích.

Nové chování

Počínaje EF Core 8.0 se vytvářejí diskriminující sloupce s maximální délkou, která pokrývá všechny známé nediskriminační hodnoty. EF vygeneruje migraci, která tuto změnu provede. Pokud je však nediskriminační sloupec omezen nějakým způsobem – například jako součást indexu – může dojít k AlterColumn selhání vytvořeného migrací.

Proč

nvarchar(max) sloupce jsou neefektivní a nepotřebné, pokud jsou známé délky všech možných hodnot.

Omezení rizik

Velikost sloupce může být explicitně nevázaná:

modelBuilder.Entity<Foo>()
    .Property<string>("Discriminator")
    .HasMaxLength(-1);

Hodnoty klíčů SQL Serveru se porovnávají bez rozlišování velkých a malých písmen.

Problém se sledováním č. 27526

Staré chování

Dříve se při sledování entit s řetězcovými klíči s zprostředkovateli databáze SQL Server nebo Azure SQL porovnávaly hodnoty klíčů pomocí výchozího pořadového porovnávače rozlišujícího velká a malá písmena .NET.

Nové chování

Počínaje EF Core 8.0 se hodnoty řetězcového klíče SQL Serveru nebo Azure SQL porovnávají pomocí výchozího porovnávače bez rozlišování malých a malých písmen .NET.

Proč

SQL Server ve výchozím nastavení používá porovnání bez rozlišování malých a velkých písmen při porovnávání hodnot cizích klíčů pro porovnávání hodnot s hlavními klíči. To znamená, že když EF používá porovnání s rozlišováním malých a velkých písmen, nemusí připojit cizí klíč k instančnímu klíči, když by měl.

Omezení rizik

Porovnání s rozlišováním velkých a malých písmen lze použít nastavením vlastního ValueComparer. Příklad:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    var comparer = new ValueComparer<string>(
        (l, r) => string.Equals(l, r, StringComparison.Ordinal),
        v => v.GetHashCode(),
        v => v);

    modelBuilder.Entity<Blog>()
        .Property(e => e.Id)
        .Metadata.SetValueComparer(comparer);

    modelBuilder.Entity<Post>(
        b =>
        {
            b.Property(e => e.Id).Metadata.SetValueComparer(comparer);
            b.Property(e => e.BlogId).Metadata.SetValueComparer(comparer);
        });
}