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ě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é.
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 false
tato hodnota , což je CLR výchozí. Pokud má databáze výchozí hodnotu true
pro sloupec, pak i když je false
hodnota 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 true
databá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á aIProperty.DeclaringType
měla by být použita.IEntityTypeIgnoredConvention
je nyní zastaralá aITypeIgnoredConvention
měla by být použita.IValueGeneratorSelector.Select
nyní 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 kompilované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
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);
});
}
Více volání AddDbContext se používá v jiném pořadí.
Problém se sledováním č. 32518
Staré chování
Pokud bylo dříve provedeno více volání , AddDbContext
AddDbContextPool
AddDbContextFactory
nebo AddPooledDbContextFactor
byly provedeny se stejným typem kontextu, ale konfliktní konfigurace, první z nich vyhrál.
Nové chování
Počínaje EF Core 8.0 bude mít přednost konfigurace z posledního volání.
Proč
Tato změna byla změněna tak, aby byla konzistentní s novou metodou ConfigureDbContext
, kterou lze použít k přidání konfigurace před nebo za Add*
metody.
Omezení rizik
Obrácení pořadí Add*
volání
EntityTypeAttributeConventionBase nahrazeno TypeAttributeConventionBase
Nové chování
V EF Core 8.0 EntityTypeAttributeConventionBase
byl přejmenován na TypeAttributeConventionBase
.
Proč
TypeAttributeConventionBase
představuje lepší funkčnost, protože je nyní možné ji použít pro komplexní typy a typy entit.
Omezení rizik
Nahraďte EntityTypeAttributeConventionBase
použití .TypeAttributeConventionBase