EF Core 3.0 中包含的中断性变更Breaking changes included in EF Core 3.0

以下 API 和行为更改有可能使现有应用程序在升级到 3.0.0 时中断。The following API and behavior changes have the potential to break existing applications when upgrading them to 3.0.0. 我们将仅影响数据库提供程序的更改记录在提供程序更改下。Changes that we expect to only impact database providers are documented under provider changes.

总结Summary

中断性变更Breaking change 影响Impact
不再在客户端上计算 LINQ 查询LINQ queries are no longer evaluated on the client High
EF Core 3.0 面向 .NET Standard 2.1,而不是 .NET Standard 2.0EF Core 3.0 targets .NET Standard 2.1 rather than .NET Standard 2.0 High
EF Core 命令行工具 dotnet ef 不再是 .NET Core SDK 的一部分The EF Core command-line tool, dotnet ef, is no longer part of the .NET Core SDK High
DetectChanges 遵循存储生成的键值DetectChanges honors store-generated key values High
FromSql、ExecuteSql 和 ExecuteSqlAsync 已重命名FromSql, ExecuteSql, and ExecuteSqlAsync have been renamed High
查询类型与实体类型合并Query types are consolidated with entity types High
Entity Framework Core 不再是 ASP.NET Core 共享框架的一部分Entity Framework Core is no longer part of the ASP.NET Core shared framework 中等Medium
默认情况下,现在会立即发生级联删除Cascade deletions now happen immediately by default 中等Medium
单个查询中现在开始预先加载相关实体Eager loading of related entities now happens in a single query 中等Medium
DeleteBehavior.Restrict 具有更简洁的语义DeleteBehavior.Restrict has cleaner semantics 中等Medium
从属类型关系的配置 API 已更改Configuration API for owned type relationships has changed 中等Medium
每个属性使用独立的内存中整数键生成Each property uses independent in-memory integer key generation 中等Medium
无跟踪查询不再执行标识解析No-tracking queries no longer perform identity resolution 中等Medium
元数据 API 更改Metadata API changes 中等Medium
特定于提供程序的元数据 API 更改Provider-specific Metadata API changes 中等Medium
UseRowNumberForPaging 已删除UseRowNumberForPaging has been removed 中等Medium
FromSql 方法在与存储过程配合使用时,无法进行组合FromSql method when used with stored procedure cannot be composed 中等Medium
只能在查询根上指定 FromSql 方法FromSql methods can only be specified on query roots Low
在调试级别记录查询执行已还原Query execution is logged at Debug level Reverted Low
不再在实体实例上设置临时键值Temporary key values are no longer set onto entity instances Low
与主体共享表的依赖实体现为可选项Dependent entities sharing the table with the principal are now optional Low
与并发标记列共享表的所有实体均必须将其映射到属性All entities sharing a table with a concurrency token column have to map it to a property Low
如果没有所有者,则无法使用跟踪查询来查询从属实体Owned entities cannot be queried without the owner using a tracking query Low
对于所有派生的类型而言,从未映射的类型继承的属性现在会映射到一个列中Inherited properties from unmapped types are now mapped to a single column for all derived types Low
外键属性约定不再匹配与主体属性相同的名称The foreign key property convention no longer matches same name as the principal property Low
现在,如果在 TransactionScope 完成前不再使用数据库连接,则该连接会关闭Database connection is now closed if not used anymore before the TransactionScope has been completed Low
默认情况下使用支持字段Backing fields are used by default Low
如果找到多个兼容的支持字段,则引发Throw if multiple compatible backing fields are found Low
“仅字段”属性名应与字段名匹配Field-only property names should match the field name Low
AddDbContext/AddDbContextPool 不再调用 AddLogging 和 AddMemoryCacheAddDbContext/AddDbContextPool no longer call AddLogging and AddMemoryCache Low
AddEntityFramework* 添加具有大小限制的 IMemoryCacheAddEntityFramework* adds IMemoryCache with a size limit Low
DbContext.Entry 现在执行本地 DetectChangesDbContext.Entry now performs a local DetectChanges Low
默认情况下,字符串和字节数组键不是客户端生成的String and byte array keys are not client-generated by default Low
ILoggerFactory 现在是一个在一定范围内有效的服务ILoggerFactory is now a scoped service Low
延迟加载代理不再假定导航属性已完全加载Lazy-loading proxies no longer assume navigation properties are fully loaded Low
默认情况下,现在过度创建内部服务提供程序是一个错误Excessive creation of internal service providers is now an error by default Low
使用单个字符串调用 HasOne/HasMany 的新行为New behavior for HasOne/HasMany called with a single string Low
多个异步方法的返回类型已从 Task 更改为 ValueTaskThe return type for several async methods has been changed from Task to ValueTask Low
关系式:TypeMapping 注释现在只是 TypeMappingThe Relational:TypeMapping annotation is now just TypeMapping Low
派生类型上的 ToTable 会引发异常ToTable on a derived type throws an exception Low
EF Core 不再发送 pragma 来执行 SQLite FKEF Core no longer sends pragma for SQLite FK enforcement Low
Microsoft.EntityFrameworkCore.Sqlite 现在依赖于 SQLitePCLRaw.bundle_e_sqlite3Microsoft.EntityFrameworkCore.Sqlite now depends on SQLitePCLRaw.bundle_e_sqlite3 Low
GUID 值现在以文本形式存储在 SQLite 上Guid values are now stored as TEXT on SQLite Low
Char 值现在以文本形式存储在 SQLite 上Char values are now stored as TEXT on SQLite Low
现在使用固定区域性的日历生成迁移 IDMigration IDs are now generated using the invariant culture's calendar Low
已从 IDbContextOptionsExtension 中删除扩展信息/元数据Extension info/metadata has been removed from IDbContextOptionsExtension Low
已重命名 LogQueryPossibleExceptionWithAggregateOperatorLogQueryPossibleExceptionWithAggregateOperator has been renamed Low
阐明 API 的外键约束名称Clarify API for foreign key constraint names Low
IRelationalDatabaseCreator.HasTables/HasTablesAsync 已公开IRelationalDatabaseCreator.HasTables/HasTablesAsync have been made public Low
Microsoft.EntityFrameworkCore.Design 现在是 DevelopmentDependency 包Microsoft.EntityFrameworkCore.Design is now a DevelopmentDependency package Low
SQLitePCL.raw 已更新为版本 2.0.0SQLitePCL.raw updated to version 2.0.0 Low
NetTopologySuite 已更新为版本 2.0.0NetTopologySuite updated to version 2.0.0 Low
使用 Microsoft.Data.SqlClient 而不是 System.Data.SqlClientMicrosoft.Data.SqlClient is used instead of System.Data.SqlClient Low
必须配置多个不明确的自引用关系Multiple ambiguous self-referencing relationships must be configured Low
DbFunction.Schema 为 null 或者空字符串将其配置为位于模型的默认架构中DbFunction.Schema being null or empty string configures it to be in model's default schema Low

不再在客户端上计算 LINQ 查询LINQ queries are no longer evaluated on the client

跟踪问题 #14935 另请参阅问题 #12795Tracking Issue #14935 Also see issue #12795

旧行为Old behavior

在 3.0 之前,当 EF Core 无法将查询中的表达式转换为 SQL 或参数时,它会在客户端上自动计算表达式的值。Before 3.0, when EF Core couldn't convert an expression that was part of a query to either SQL or a parameter, it automatically evaluated the expression on the client. 默认情况下,客户端对潜在的昂贵表达式的计算仅触发警告。By default, client evaluation of potentially expensive expressions only triggered a warning.

新行为New behavior

从 3.0 开始,EF Core 仅允许在客户端上计算顶级投影中的表达式(查询中的最后一个 Select() 调用)。Starting with 3.0, EF Core only allows expressions in the top-level projection (the last Select() call in the query) to be evaluated on the client. 当查询的任何其他部分中的表达式无法转换为 SQL 或参数时,将引发异常。When expressions in any other part of the query can't be converted to either SQL or a parameter, an exception is thrown.

为什么Why

自动的客户端查询计算允许执行许多查询,即使它们的重要组成部分无法转换。Automatic client evaluation of queries allows many queries to be executed even if important parts of them can't be translated. 此行为可能导致意外且具有潜在破坏性的行为,这些行为可能仅在生产中变得明显。This behavior can result in unexpected and potentially damaging behavior that may only become evident in production. 例如,Where() 调用中无法转换的条件可能导致表中的所有行从数据库服务器传输且筛选器应用于客户端。For example, a condition in a Where() call which can't be translated can cause all rows from the table to be transferred from the database server, and the filter to be applied on the client. 如果在开发中表中只包含几行,则不容易检测到这种情况,但是当应用程序转入生产环节时,由于表中可能包含数百万行,这种情况会非常严重。This situation can easily go undetected if the table contains only a few rows in development, but hit hard when the application moves to production, where the table may contain millions of rows. 在开发过程中,客户端求值警告也很容易被忽视。Client evaluation warnings also proved too easy to ignore during development.

除此之外,自动客户端计算可能会导致问题,其中改进特定表达式的查询转换会导致版本之间发生意外中断性变更。Besides this, automatic client evaluation can lead to issues in which improving query translation for specific expressions caused unintended breaking changes between releases.

缓解措施Mitigations

如果无法完全转换查询,则以可转换的形式重写查询,或使用 AsEnumerable()ToList() 或类似信息将数据显式返回客户端,然后可以进一步使用 LINQ 到对象处理。If a query can't be fully translated, then either rewrite the query in a form that can be translated, or use AsEnumerable(), ToList(), or similar to explicitly bring data back to the client where it can then be further processed using LINQ-to-Objects.

EF Core 3.0 面向 .NET Standard 2.1,而不是 .NET Standard 2.0EF Core 3.0 targets .NET Standard 2.1 rather than .NET Standard 2.0

跟踪问题 #15498Tracking Issue #15498

旧行为Old behavior

在 3.0 之前,EF Core 面向 .NET Standard 2.0,并在支持 .NET Standard 2.0 的所有平台上运行,包括 .NET Framework。Before 3.0, EF Core targeted .NET Standard 2.0 and would run on all platforms that support that standard, including .NET Framework.

新行为New behavior

从 3.0 开始,EF Core 面向 .NET Standard 2.1,并且在支持 .NET Standard 2.1 的所有平台上运行。Starting with 3.0, EF Core targets .NET Standard 2.1 and will run on all platforms that support this standard. 这不包括 .NET Framework。This does not include .NET Framework.

为什么Why

这是 .NET 技术中战略决策的一部分,旨在将重点放在 .NET Core 和其他新式 .NET 平台,例如 Xamarin。This is part of a strategic decision across .NET technologies to focus energy on .NET Core and other modern .NET platforms, such as Xamarin.

缓解措施Mitigations

请考虑迁移到新式 .NET 平台。Consider moving to a modern .NET platform. 如果无法做到这一点,请继续使用 EF Core 2.1 或 EF Core 2.2,这两者都支持 .NET Framework。If this is not possible, then continue to use EF Core 2.1 or EF Core 2.2, both of which support .NET Framework.

Entity Framework Core 不再是 ASP.NET Core 共享框架的一部分Entity Framework Core is no longer part of the ASP.NET Core shared framework

跟踪问题公告 #325Tracking Issue Announcements#325

旧行为Old behavior

在 ASP.NET Core 3.0 之前,当向 Microsoft.AspNetCore.AppMicrosoft.AspNetCore.All 添加包引用时,它将包括 EF Core 和一些 EF Core 数据提供程序(如 SQL Server 提供程序)。Before ASP.NET Core 3.0, when you added a package reference to Microsoft.AspNetCore.App or Microsoft.AspNetCore.All, it would include EF Core and some of the EF Core data providers like the SQL Server provider.

新行为New behavior

从 3.0 开始,ASP.NET Core 共享框架不包括 EF Core 或任何 EF Core 数据提供程序。Starting in 3.0, the ASP.NET Core shared framework doesn't include EF Core or any EF Core data providers.

为什么Why

在此更改之前,获取 EF Core 需要不同的步骤,具体取决于应用程序是否是面向 ASP.NET Core 和 SQL Server。Before this change, getting EF Core required different steps depending on whether the application targeted ASP.NET Core and SQL Server or not. 此外,升级 ASP.NET Core 会强制升级 EF Core 和 SQL Server 提供程序,这并不总是可取的。Also, upgrading ASP.NET Core forced the upgrade of EF Core and the SQL Server provider, which isn't always desirable.

通过此更改,通过所有提供程序、支持的 .NET 实现和应用程序类型获取 EF Core 的体验都是一致的。With this change, the experience of getting EF Core is the same across all providers, supported .NET implementations and application types. 开发人员现在还可以准确控制何时升级 EF Core 和 EF Core 数据提供程序。Developers can also now control exactly when EF Core and EF Core data providers are upgraded.

缓解措施Mitigations

若要在 ASP.NET Core 3.0 应用程序或任何其他受支持的应用程序中使用 EF Core,请显式添加对应用程序将使用的 EF Core 数据库提供程序的包引用。To use EF Core in an ASP.NET Core 3.0 application or any other supported application, explicitly add a package reference to the EF Core database provider that your application will use.

EF Core 命令行工具 dotnet ef 不再是 .NET Core SDK 的一部分The EF Core command-line tool, dotnet ef, is no longer part of the .NET Core SDK

跟踪问题 #14016Tracking Issue #14016

旧行为Old behavior

.NET Core SDK 3.0 以前的版本包含 dotnet ef 工具,可以随时从任何项目的命令行使用,无需额外的步骤。Before 3.0, the dotnet ef tool was included in the .NET Core SDK and was readily available to use from the command line from any project without requiring extra steps.

新行为New behavior

从 3.0 版开始,.NET SDK 不再包含 dotnet ef 工具,因此,在使用它之前,必须将其明确安装为本地或全局工具。Starting in 3.0, the .NET SDK does not include the dotnet ef tool, so before you can use it you have to explicitly install it as a local or global tool.

为什么Why

此更改允许我们在 NuGet 上将 dotnet ef 作为常规 .NET CLI 工具分发和更新,这与 EF Core 3.0 也始终作为 NuGet 包分发的事实一致。This change allows us to distribute and update dotnet ef as a regular .NET CLI tool on NuGet, consistent with the fact that the EF Core 3.0 is also always distributed as a NuGet package.

缓解措施Mitigations

为了能够管理迁移或构架 DbContext,请安装 dotnet-ef 作为全局工具:To be able to manage migrations or scaffold a DbContext, install dotnet-ef as a global tool:

  $ dotnet tool install --global dotnet-ef

使用工具清单文件恢复声明为工具依赖项的项目依赖项时,还可以将其作为本地工具获取。You can also obtain it a local tool when you restore the dependencies of a project that declares it as a tooling dependency using a tool manifest file.

FromSql、ExecuteSql 和 ExecuteSqlAsync 已重命名FromSql, ExecuteSql, and ExecuteSqlAsync have been renamed

跟踪问题 #10996Tracking Issue #10996

旧行为Old behavior

在 EF Core 3.0 之前,这些方法名称是重载的,它们使用普通字符串或应内插到 SQL 和参数中的字符串。Before EF Core 3.0, these method names were overloaded to work with either a normal string or a string that should be interpolated into SQL and parameters.

新行为New behavior

自 EF Core 3.0 起,可使用 FromSqlRawExecuteSqlRawExecuteSqlRawAsync 创建一个参数化的查询,其中参数是从查询字符串中单独传递的。Starting with EF Core 3.0, use FromSqlRaw, ExecuteSqlRaw, and ExecuteSqlRawAsync to create a parameterized query where the parameters are passed separately from the query string. 例如:For example:

context.Products.FromSqlRaw(
    "SELECT * FROM Products WHERE Name = {0}",
    product.Name);

使用 FromSqlInterpolatedExecuteSqlInterpolatedExecuteSqlInterpolatedAsync 创建一个参数化的查询,其中参数作为内插查询字符串的一部分进行传递。Use FromSqlInterpolated, ExecuteSqlInterpolated, and ExecuteSqlInterpolatedAsync to create a parameterized query where the parameters are passed as part of an interpolated query string. 例如:For example:

context.Products.FromSqlInterpolated(
    $"SELECT * FROM Products WHERE Name = {product.Name}");

请注意,上述两个查询都将生成 SQL 参数相同的同一参数化的 SQL。Note that both of the queries above will produce the same parameterized SQL with the same SQL parameters.

为什么Why

此类方法重载使得在意图调用内插字符串方法时很容易意外调用原始字符串方法,反之亦然。Method overloads like this make it very easy to accidentally call the raw string method when the intent was to call the interpolated string method, and the other way around. 这会导致查询中的本该参数化的结果没有参数化。This could result in queries not being parameterized when they should have been.

缓解措施Mitigations

切换到使用新的方法名称。Switch to use the new method names.

FromSql 方法在与存储过程配合使用时,无法进行组合FromSql method when used with stored procedure cannot be composed

跟踪问题 #15392Tracking Issue #15392

旧行为Old behavior

在 EF Core 3.0 之前,FromSql 方法已尝试检测是否可对传入的 SQL 进行组合。Before EF Core 3.0, FromSql method tried to detect if the passed SQL can be composed upon. 当 SQL 像存储过程那样不可组合时,该方法进行客户端评估。It did client evaluation when the SQL was non-composable like a stored procedure. 以下查询在服务器上运行存储过程并在客户端执行 FirstOrDefault。The following query worked by running the stored procedure on the server and doing FirstOrDefault on the client side.

context.Products.FromSqlRaw("[dbo].[Ten Most Expensive Products]").FirstOrDefault();

新行为New behavior

从 EF Core 3.0 开始,EF Core 将不再尝试分析 SQL。Starting with EF Core 3.0, EF Core will not try to parse the SQL. 因此,如果在 FromSqlRaw/FromSqlInterpolated 之后组合,则 EF Core 会通过引发子查询来组合 SQL。So if you are composing after FromSqlRaw/FromSqlInterpolated, then EF Core will compose the SQL by causing sub query. 因此,如果将存储过程用于组合,则出现无效 SQL 语法的异常。So if you are using a stored procedure with composition then you will get an exception for invalid SQL syntax.

为什么Why

EF Core 3.0 不支持自动客户端评估,因为容易出错,如此处所述。EF Core 3.0 does not support automatic client evaluation, since it was error prone as explained here.

缓解措施Mitigation

如果在 FromSqlRaw/FromSqlInterpolated 中使用存储过程,你了解无法对其进行组合,因此可以紧随 FromSql 方法调用添加 AsEnumerable/AsAsyncEnumerable,以避免在服务器端上进行任何组合 。If you are using a stored procedure in FromSqlRaw/FromSqlInterpolated, you know that it cannot be composed upon, so you can add AsEnumerable/AsAsyncEnumerable right after the FromSql method call to avoid any composition on server side.

context.Products.FromSqlRaw("[dbo].[Ten Most Expensive Products]").AsEnumerable().FirstOrDefault();

只能在查询根上指定 FromSql 方法FromSql methods can only be specified on query roots

跟踪问题 #15704Tracking Issue #15704

旧行为Old behavior

在 EF Core 3.0 之前,可以在查询中的任意位置指定 FromSql 方法。Before EF Core 3.0, the FromSql method could be specified anywhere in the query.

新行为New behavior

从 EF Core 3.0 开始,只能在查询根上(即,直接在 DbSet<> 上)指定新的 FromSqlRawFromSqlInterpolated 方法(替换 FromSql)。Starting with EF Core 3.0, the new FromSqlRaw and FromSqlInterpolated methods (which replace FromSql) can only be specified on query roots, i.e. directly on the DbSet<>. 尝试在其他任何位置指定这些方法将导致编译错误。Attempting to specify them anywhere else will result in a compilation error.

为什么Why

在除 DbSet 之外的任意位置指定 FromSql 没有附加含义或附加值,并且可能在某些情况下存在多义性。Specifying FromSql anywhere other than on a DbSet had no added meaning or added value, and could cause ambiguity in certain scenarios.

缓解措施Mitigations

应移动 FromSql 调用以使其直接位于它们所应用的 DbSet 上。FromSql invocations should be moved to be directly on the DbSet to which they apply.

无跟踪查询不再执行标识解析No-tracking queries no longer perform identity resolution

跟踪问题 #13518Tracking Issue #13518

旧行为Old behavior

在 EF Core 3.0 之前,将对具有给定类型和 ID 的实体的每个匹配项使用同一个实体实例。Before EF Core 3.0, the same entity instance would be used for every occurrence of an entity with a given type and ID. 这与跟踪查询的行为匹配。This matches the behavior of tracking queries. 例如下面的查询:For example, this query:

var results = context.Products.Include(e => e.Category).AsNoTracking().ToList();

会为与给定类别关联的每个 Product 返回相同的 Category 实例。would return the same Category instance for each Product that is associated with the given category.

新行为New behavior

从 EF Core 3.0 开始,当在返回的图中的不同位置遇到具有给定类型和 ID 的实体时,将创建不同的实体实例。Starting with EF Core 3.0, different entity instances will be created when an entity with a given type and ID is encountered at different places in the returned graph. 例如,上面的查询现在将为每个 Product 返回新的 Category 实例,即使两个产品与同一类别关联。For example, the query above will now return a new Category instance for each Product even when two products are associated with the same category.

为什么Why

标识解析(即确定实体与先前遇到的实体具有相同的类型和 ID)会增加额外的性能和内存开销。Identity resolution (that is, determining that an entity has the same type and ID as a previously encountered entity) adds additional performance and memory overhead. 这通常会运行计数器,原因是最初使用无跟踪查询。This usually runs counter to why no-tracking queries are used in the first place. 此外,尽管标识解析有时会很有用,但如果要对实体进行序列化并将其发送到客户端(这对于无跟踪查询很常见),则不需要这样做。Also, while identity resolution can sometimes be useful, it is not needed if the entities are to be serialized and sent to a client, which is common for no-tracking queries.

缓解措施Mitigations

如果需要标识解析,请使用跟踪查询。Use a tracking query if identity resolution is required.

在调试级别记录查询执行已还原Query execution is logged at Debug level Reverted

跟踪问题 #14523Tracking Issue #14523

我们之所以还原此更改是因为,EF Core 3.0 中的新配置允许应用程序指定任何事件的日志级别。We reverted this change because new configuration in EF Core 3.0 allows the log level for any event to be specified by the application. 例如,若要将 SQL 日志记录切换为 Debug,请在 OnConfiguringAddDbContext 中显式配置级别:For example, to switch logging of SQL to Debug, explicitly configure the level in OnConfiguring or AddDbContext:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    => optionsBuilder
        .UseSqlServer(connectionString)
        .ConfigureWarnings(c => c.Log((RelationalEventId.CommandExecuting, LogLevel.Debug)));

不再在实体实例上设置临时键值Temporary key values are no longer set onto entity instances

跟踪问题 #12378Tracking Issue #12378

旧行为Old behavior

在 EF Core 3.0 之前,临时值已分配给所有键属性,这些属性稍后将具有数据库生成的实际值。Before EF Core 3.0, temporary values were assigned to all key properties that would later have a real value generated by the database. 通常这些临时值是较大负数。Usually these temporary values were large negative numbers.

新行为New behavior

从 3.0 开始,EF Core 将临时键值存储为实体跟踪信息的一部分,并保持键属性本身不变。Starting with 3.0, EF Core stores the temporary key value as part of the entity's tracking information, and leaves the key property itself unchanged.

为什么Why

此更改是为了防止当之前由某个 DbContext 实例跟踪的实体移动到另一个 DbContext 实例时,临时键值错误地变成永久值。This change was made to prevent temporary key values from erroneously becoming permanent when an entity that has been previously tracked by some DbContext instance is moved to a different DbContext instance.

缓解措施Mitigations

如果主键是存储生成的并且属于 Added 状态的实体,则将主键值分配到外键以在实体之间形成关联的应用可能会依赖于旧行为。Applications that assign primary key values onto foreign keys to form associations between entities may depend on the old behavior if the primary keys are store-generated and belong to entities in the Added state. 可通过以下方式避免:This can be avoided by:

  • 不使用存储生成的密钥。Not using store-generated keys.
  • 设置导航属性以形成关系,而不是设置外键值。Setting navigation properties to form relationships instead of setting foreign key values.
  • 从实体的跟踪信息中获取实际的临时键值。Obtain the actual temporary key values from the entity's tracking information. 例如,即使 blog.Id 本身尚未设置,context.Entry(blog).Property(e => e.Id).CurrentValue 也将返回临时值。For example, context.Entry(blog).Property(e => e.Id).CurrentValue will return the temporary value even though blog.Id itself hasn't been set.

DetectChanges 遵循存储生成的键值DetectChanges honors store-generated key values

跟踪问题 #14616Tracking Issue #14616

旧行为Old behavior

在 EF Core 3.0 之前,DetectChanges 找到的未跟踪实体将在 Added 状态中被跟踪,并在调用 SaveChanges 时作为新行插入。Before EF Core 3.0, an untracked entity found by DetectChanges would be tracked in the Added state and inserted as a new row when SaveChanges is called.

新行为New behavior

从 EF Core 3.0 开始,如果实体使用生成的键值并设置了某个键值,则将在 Modified 状态下跟踪实体。Starting with EF Core 3.0, if an entity is using generated key values and some key value is set, then the entity will be tracked in the Modified state. 这意味着假定存在实体的行,并且在调用 SaveChanges 时将更新该行。This means that a row for the entity is assumed to exist and it will be updated when SaveChanges is called. 如果未设置键值,或者实体类型未使用生成的键,则新实体仍将像先前版本一样被作为 Added 跟踪。If the key value isn't set, or if the entity type isn't using generated keys, then the new entity will still be tracked as Added as in previous versions.

为什么Why

进行此更改是为了在使用存储生成的键时更轻松、更一致地使用断开连接的实体图。This change was made to make it easier and more consistent to work with disconnected entity graphs while using store-generated keys.

缓解措施Mitigations

如果将实体类型配置为使用生成的键,但为新实例显式设置了键值,则此更改可能会中断应用程序。This change can break an application if an entity type is configured to use generated keys but key values are explicitly set for new instances. 解决方案是显式配置键属性,使其不使用生成的值。The fix is to explicitly configure the key properties to not use generated values. 例如,使用 Fluent API:For example, with the fluent API:

modelBuilder
    .Entity<Blog>()
    .Property(e => e.Id)
    .ValueGeneratedNever();

或使用数据注释:Or with data annotations:

[DatabaseGenerated(DatabaseGeneratedOption.None)]
public string Id { get; set; }

默认情况下,现在会立即发生级联删除Cascade deletions now happen immediately by default

跟踪问题 #10114Tracking Issue #10114

旧行为Old behavior

在 3.0 之前,直到调用 SaveChanges 时,EF Core 才会应用级联操作(删除所需主体时或者在切断与所需主体的关系时删除依赖实体)。Before 3.0, EF Core applied cascading actions (deleting dependent entities when a required principal is deleted or when the relationship to a required principal is severed) did not happen until SaveChanges was called.

新行为New behavior

从 3.0 开始,一旦检测到触发条件,EF Core 就会应用级联操作。Starting with 3.0, EF Core applies cascading actions as soon as the triggering condition is detected. 例如,调用 context.Remove() 来删除主体实体将导致所有跟踪的相关必需依赖项也立即设置为 DeletedFor example, calling context.Remove() to delete a principal entity will result in all tracked related required dependents also being set to Deleted immediately.

为什么Why

此更改是为了改善数据绑定和审核方案的体验;在相关体验中,有必要了解在调用 SaveChanges 之前会删除哪些实体 。This change was made to improve the experience for data binding and auditing scenarios where it is important to understand which entities will be deleted before SaveChanges is called.

缓解措施Mitigations

可以通过 context.ChangeTracker 上的设置还原以前的行为。The previous behavior can be restored through settings on context.ChangeTracker. 例如:For example:

context.ChangeTracker.CascadeDeleteTiming = CascadeTiming.OnSaveChanges;
context.ChangeTracker.DeleteOrphansTiming = CascadeTiming.OnSaveChanges;

跟踪问题 #18022Tracking issue #18022

旧行为Old behavior

在 3.0 之前,通过 Include 运算符预先加载集合导航会导致在关系数据库上生成多个查询,每个相关实体类型对应一个查询。Before 3.0, eagerly loading collection navigations via Include operators caused multiple queries to be generated on relational database, one for each related entity type.

新行为New behavior

从 3.0 开始,EF Core 会在关系数据库上使用 JOIN 生成单个查询。Starting with 3.0, EF Core generates a single query with JOINs on relational databases.

为什么Why

以发出多个查询的方式实现单个 LINQ 查询会导致出现许多问题,包括由于需要执行多次数据库往返而引起的性能不佳问题,以及每个查询都可能会观察到不同的数据库状态的数据不一致问题。Issuing multiple queries to implement a single LINQ query caused numerous issues, including negative performance as multiple database roundtrips were necessary, and data coherency issues as each query could observe a different state of the database.

缓解措施Mitigations

尽管从技术上讲,这不是一项中断性变更,但当单个查询在集合导航中包含大量 Include 运算符时,这可能对应用程序性能产生相当大的影响。While technically this is not a breaking change, it could have a considerable effect on application performance when a single query contains a large number of Include operator on collection navigations. 请参阅此评论,了解详细信息,以及如何以更有效的方式重写查询。See this comment for more information and for rewriting queries in a more efficient way.

**

DeleteBehavior.Restrict 具有更简洁的语义DeleteBehavior.Restrict has cleaner semantics

跟踪问题 #12661Tracking Issue #12661

旧行为Old behavior

3.0 之前,DeleteBehavior.Restrict 使用 Restrict 语义在数据库中创建外键,但也以不明显的方式更改了内部修复。Before 3.0, DeleteBehavior.Restrict created foreign keys in the database with Restrict semantics, but also changed internal fixup in a non-obvious way.

新行为New behavior

从 3.0 开始,DeleteBehavior.Restrict 确保使用 Restrict 语义创建外键--即无级联;在发生约束冲突时触发 - 但不会同时影响 EF 内部修复。Starting with 3.0, DeleteBehavior.Restrict ensures that foreign keys are created with Restrict semantics--that is, no cascades; throw on constraint violation--without also impacting EF internal fixup.

为什么Why

进行此更改是为了改进以直观方式使用 DeleteBehavior 的体验,且不产生意外副作用。This change was made to improve the experience for using DeleteBehavior in an intuitive manner, without unexpected side-effects.

缓解措施Mitigations

可使用 DeleteBehavior.ClientNoAction 还原以前的行为。The previous behavior can be restored by using DeleteBehavior.ClientNoAction.

查询类型与实体类型合并Query types are consolidated with entity types

跟踪问题 #14194Tracking Issue #14194

旧行为Old behavior

在 EF Core 3.0 之前,查询类型是一种查询未以结构化方式定义主键的数据的方法。Before EF Core 3.0, query types were a means to query data that doesn't define a primary key in a structured way. 也就是说,查询类型用于映射没有键的实体类型(更可能来自视图,但也可能来自表),而当有可用的键时则使用常规实体类型(更可能来自表,但也可能来自视图)。That is, a query type was used for mapping entity types without keys (more likely from a view, but possibly from a table) while a regular entity type was used when a key was available (more likely from a table, but possibly from a view).

新行为New behavior

现在,查询类型只是一个没有主键的实体类型。A query type now becomes just an entity type without a primary key. 无键实体类型与先前版本中的查询类型具有相同的功能。Keyless entity types have the same functionality as query types in previous versions.

为什么Why

这样做是为了减少对查询类型用途的混淆。This change was made to reduce the confusion around the purpose of query types. 具体来说,它们是无键实体类型,因此本质上是只读的,但是不应该仅仅因为实体类型需要是只读的就使用它们。Specifically, they are keyless entity types and they are inherently read-only because of this, but they should not be used just because an entity type needs to be read-only. 同样,它们通常映射到视图,但这只是因为视图通常不定义键。Likewise, they are often mapped to views, but this is only because views often don't define keys.

缓解措施Mitigations

API 的以下部分现已过时:The following parts of the API are now obsolete:

  • ModelBuilder.Query<>() - 需要调用 ModelBuilder.Entity<>().HasNoKey() 来将实体类型标记为没有键。ModelBuilder.Query<>() - Instead ModelBuilder.Entity<>().HasNoKey() needs to be called to mark an entity type as having no keys. 这仍然不会按约定配置,以避免在需要主键但是与约定不匹配时发生配置错误。This would still not be configured by convention to avoid misconfiguration when a primary key is expected, but doesn't match the convention.
  • DbQuery<> - 应使用 DbSet<>DbQuery<> - Instead DbSet<> should be used.
  • DbContext.Query<>() - 应使用 DbContext.Set<>()DbContext.Query<>() - Instead DbContext.Set<>() should be used.

从属类型关系的配置 API 已更改Configuration API for owned type relationships has changed

跟踪问题 #12444 跟踪问题 #9148 跟踪问题 #14153Tracking Issue #12444 Tracking Issue #9148 Tracking Issue #14153

旧行为Old behavior

EF Core 3.0 之前,会在 OwnsOneOwnsMany 调用之后直接执行所拥有关系的配置。Before EF Core 3.0, configuration of the owned relationship was performed directly after the OwnsOne or OwnsMany call.

新行为New behavior

从 EF Core 3.0 开始,Fluent API 会使用 WithOwner() 为所有者配置导航属性。Starting with EF Core 3.0, there is now fluent API to configure a navigation property to the owner using WithOwner(). 例如:For example:

modelBuilder.Entity<Order>.OwnsOne(e => e.Details).WithOwner(e => e.Order);

与所有者之间关系相关的配置现会在 WithOwner() 之后关联起来,就像配置其他关系一样。The configuration related to the relationship between owner and owned should now be chained after WithOwner() similarly to how other relationships are configured. 但从属类型本身的配置仍会在 OwnsOne()/OwnsMany() 之后关联。While the configuration for the owned type itself would still be chained after OwnsOne()/OwnsMany(). 例如:For example:

modelBuilder.Entity<Order>.OwnsOne(e => e.Details, eb =>
    {
        eb.WithOwner()
            .HasForeignKey(e => e.AlternateId)
            .HasConstraintName("FK_OrderDetails");
            
        eb.ToTable("OrderDetails");
        eb.HasKey(e => e.AlternateId);
        eb.HasIndex(e => e.Id);

        eb.HasOne(e => e.Customer).WithOne();

        eb.HasData(
            new OrderDetails
            {
                AlternateId = 1,
                Id = -1
            });
    });

此外,使用固有类型目标调用 Entity()``HasOne()Set() 现将引发异常。Additionally calling Entity(), HasOne(), or Set() with an owned type target will now throw an exception.

为什么Why

此更改是为了更清晰的区分从属类型本身的配置和与从属类型的_关系_的配置。This change was made to create a cleaner separation between configuring the owned type itself and the relationship to the owned type. 这反过来消除了诸如 HasForeignKey 之类的方法的模糊性和混淆。This in turn removes ambiguity and confusion around methods like HasForeignKey.

缓解措施Mitigations

更改从属类型关系的配置以使用新的 API 曲面,如上例所示。Change configuration of owned type relationships to use the new API surface as shown in the example above.

与主体共享表的依赖实体现为可选项Dependent entities sharing the table with the principal are now optional

跟踪问题 #9005Tracking Issue #9005

旧行为Old behavior

考虑下列模型:Consider the following model:

public class Order
{
    public int Id { get; set; }
    public int CustomerId { get; set; }
    public OrderDetails Details { get; set; }
}

public class OrderDetails
{
    public int Id { get; set; }
    public string ShippingAddress { get; set; }
}

在 EF Core 3.0 之前,如果 OrderDetailsOrder 拥有且显式映射到同一张表,则在添加新的 Order 时,始终需要 OrderDetails 实例。Before EF Core 3.0, if OrderDetails is owned by Order or explicitly mapped to the same table then an OrderDetails instance was always required when adding a new Order.

新行为New behavior

自 3.0 起,EF Core 允许添加 Order 而不添加 OrderDetails,并将主键之外的所有 OrderDetails 属性映射到不为 null 的列中。Starting with 3.0, EF Core allows to add an Order without an OrderDetails and maps all of the OrderDetails properties except the primary key to nullable columns. 查询时,如果其任意所需属性均没有值,或者它在主键之外没有任何必需属性且所有属性均为 null,则 EF Core 会将 OrderDetails 设置为 nullWhen querying EF Core sets OrderDetails to null if any of its required properties doesn't have a value or if it has no required properties besides the primary key and all properties are null.

缓解措施Mitigations

如果你的模型具有依赖于所有可选列的表共享,但指向该共享的导航不应为 null,则应修改应用程序,使其处理导航为 null 的情况。If your model has a table sharing dependent with all optional columns, but the navigation pointing to it is not expected to be null then the application should be modified to handle cases when the navigation is null. 如果此方法不可行,则应向实体类型添加一个必需属性,或者至少要有一个属性分配有非 null 值。If this is not possible a required property should be added to the entity type or at least one property should have a non-null value assigned to it.

与并发标记列共享表的所有实体均必须将其映射到属性All entities sharing a table with a concurrency token column have to map it to a property

跟踪问题 #14154Tracking Issue #14154

旧行为Old behavior

考虑下列模型:Consider the following model:

public class Order
{
    public int Id { get; set; }
    public int CustomerId { get; set; }
    public byte[] Version { get; set; }
    public OrderDetails Details { get; set; }
}

public class OrderDetails
{
    public int Id { get; set; }
    public string ShippingAddress { get; set; }
}

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Order>()
        .Property(o => o.Version).IsRowVersion().HasColumnName("Version");
}

在 EF Core 3.0 之前,如果 OrderDetailsOrder 拥有且显式映射到同一张表,则只更新 OrderDetails 时,将不更新客户端上的 Version 值且下次更新将失败。Before EF Core 3.0, if OrderDetails is owned by Order or explicitly mapped to the same table then updating just OrderDetails will not update Version value on client and the next update will fail.

新行为New behavior

自 3.0 起,如果 新的 Version 值拥有 OrderDetails则 EF Core 会将该值传播给 OrderStarting with 3.0, EF Core propagates the new Version value to Order if it owns OrderDetails. 否则,会在模型验证期间引发异常。Otherwise an exception is thrown during model validation.

为什么Why

进行此更改的目的是避免在仅更新映射到同一张表的其中一个实体时使用过时的并发标记值。This change was made to avoid a stale concurrency token value when only one of the entities mapped to the same table is updated.

缓解措施Mitigations

共享表的所有实体都必须包含一个映射到并发标记列的属性。All entities sharing the table have to include a property that is mapped to the concurrency token column. 可在影子状态中创建一个:It's possible the create one in shadow-state:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<OrderDetails>()
        .Property<byte[]>("Version").IsRowVersion().HasColumnName("Version");
}

如果没有所有者,则无法使用跟踪查询来查询从属实体Owned entities cannot be queried without the owner using a tracking query

跟踪问题 #18876Tracking Issue #18876

旧行为Old behavior

在 EF Core 3.0 之前,可像任何其他导航一样查询从属实体。Before EF Core 3.0, the owned entities could be queried as any other navigation.

context.People.Select(p => p.Address);

新行为New behavior

自版本 3.0 起,如果跟踪查询在没有所有者的情况下投射一个从属实体,EF Core 将引发异常。Starting with 3.0, EF Core will throw if a tracking query projects an owned entity without the owner.

为什么Why

如果没有所有者,则无法操作从属实体,因此在绝大多数情况下,不该使用此方式查询它们。Owned entities cannot be manipulated without the owner, so in the vast majority of cases querying them in this way is an error.

缓解措施Mitigations

如果应跟踪从属实体,以便之后以任何方式进行修改,则应在查询中包含所有者。If the owned entity should be tracked to be modified in any way later then the owner should be included in the query.

否则,请添加一个 AsNoTracking() 调用:Otherwise add an AsNoTracking() call:

context.People.Select(p => p.Address).AsNoTracking();

对于所有派生的类型而言,从未映射的类型继承的属性现在会映射到一个列中Inherited properties from unmapped types are now mapped to a single column for all derived types

跟踪问题 #13998Tracking Issue #13998

旧行为Old behavior

考虑下列模型:Consider the following model:

public abstract class EntityBase
{
    public int Id { get; set; }
}

public abstract class OrderBase : EntityBase
{
    public int ShippingAddress { get; set; }
}

public class BulkOrder : OrderBase
{
}

public class Order : OrderBase
{
}

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Ignore<OrderBase>();
    modelBuilder.Entity<EntityBase>();
    modelBuilder.Entity<BulkOrder>();
    modelBuilder.Entity<Order>();
}

在 EF Core 3.0 之前,ShippingAddress 属性会为 BulkOrderOrder 默认映射到单独的列中。Before EF Core 3.0, the ShippingAddress property would be mapped to separate columns for BulkOrder and Order by default.

新行为New behavior

自 3.0 起,EF Core只会为 ShippingAddress 创建一个列。Starting with 3.0, EF Core only creates one column for ShippingAddress.

为什么Why

旧行为不是预期行为。The old behavoir was unexpected.

缓解措施Mitigations

属性仍可显式映射到所派生的类型上的单独的列中:The property can still be explicitly mapped to separate column on the derived types:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Ignore<OrderBase>();
    modelBuilder.Entity<EntityBase>();
    modelBuilder.Entity<BulkOrder>()
        .Property(o => o.ShippingAddress).HasColumnName("BulkShippingAddress");
    modelBuilder.Entity<Order>()
        .Property(o => o.ShippingAddress).HasColumnName("ShippingAddress");
}

外键属性约定不再匹配与主体属性相同的名称The foreign key property convention no longer matches same name as the principal property

跟踪问题 #13274Tracking Issue #13274

旧行为Old behavior

考虑下列模型:Consider the following model:

public class Customer
{
    public int CustomerId { get; set; }
    public ICollection<Order> Orders { get; set; }
}

public class Order
{
    public int Id { get; set; }
    public int CustomerId { get; set; }
}

在 EF Core 3.0 之前,CustomerId 属性将按约定用于外键。Before EF Core 3.0, the CustomerId property would be used for the foreign key by convention. 但是,如果 Order 是从属类型,那么这也会使 CustomerId 成为主键,这通常不是预期结果。However, if Order is an owned type, then this would also make CustomerId the primary key and this isn't usually the expectation.

新行为New behavior

自 3.0 起,如果外键的主体属性名称相同,EF Core 不会尝试通过转换来为外键使用属性。Starting with 3.0, EF Core doesn't try to use properties for foreign keys by convention if they have the same name as the principal property. 与主体属性名称关联的主体类型名称和与主体属性名称模式关联的导航名称仍然相匹配。Principal type name concatenated with principal property name, and navigation name concatenated with principal property name patterns are still matched. 例如:For example:

public class Customer
{
    public int Id { get; set; }
    public ICollection<Order> Orders { get; set; }
}

public class Order
{
    public int Id { get; set; }
    public int CustomerId { get; set; }
}
public class Customer
{
    public int Id { get; set; }
    public ICollection<Order> Orders { get; set; }
}

public class Order
{
    public int Id { get; set; }
    public int BuyerId { get; set; }
    public Customer Buyer { get; set; }
}

为什么Why

此更改是为了避免错误地在从属类型上定义主键属性。This change was made to avoid erroneously defining a primary key property on the owned type.

缓解措施Mitigations

如果该属性将成为外键,即为主键的一部分,则显式配置它。If the property was intended to be the foreign key, and hence part of the primary key, then explicitly configure it as such.

现在,如果在 TransactionScope 完成前不再使用数据库连接,则该连接会关闭Database connection is now closed if not used anymore before the TransactionScope has been completed

跟踪问题 #14218Tracking Issue #14218

旧行为Old behavior

在 EF Core 3.0 之前,如果上下文打开了 TransactionScope 中的连接,则该连接在 TransactionScope 处于活动期间仍保持打开状态。Before EF Core 3.0, if the context opens the connection inside a TransactionScope, the connection remains open while the current TransactionScope is active.

using (new TransactionScope())
{
    using (AdventureWorks context = new AdventureWorks())
    {
        context.ProductCategories.Add(new ProductCategory());
        context.SaveChanges();

        // Old behavior: Connection is still open at this point

        var categories = context.ProductCategories().ToList();
    }
}

新行为New behavior

自 3.0 起,一旦不再使用连接,EF Core 就会将其关闭。Starting with 3.0, EF Core closes the connection as soon as it's done using it.

为什么Why

此更改让你能够在同一 TransactionScope 中使用多个上下文。This change allows to use multiple contexts in the same TransactionScope. 新行为也与 EF6 一致。The new behavior also matches EF6.

缓解措施Mitigations

如果连接需要保持打开状态,则显式调用 OpenConnection() 将确保 EF Core 不永久关闭此连接:If the connection needs to remain open explicit call to OpenConnection() will ensure that EF Core doesn't close it prematurely:

using (new TransactionScope())
{
    using (AdventureWorks context = new AdventureWorks())
    {
        context.Database.OpenConnection();
        context.ProductCategories.Add(new ProductCategory());
        context.SaveChanges();

        var categories = context.ProductCategories().ToList();
        context.Database.CloseConnection();
    }
}

每个属性使用独立的内存中整数键生成Each property uses independent in-memory integer key generation

跟踪问题 #6872Tracking Issue #6872

旧行为Old behavior

在 EF Core 3.0 之前,一个共享值生成器用于所有内存中的整数键属性。Before EF Core 3.0, one shared value generator was used for all in-memory integer key properties.

新行为New behavior

从 EF Core 3.0 开始,每个整数键属性在使用内存数据库时都会获取其自己的值生成器。Starting with EF Core 3.0, each integer key property gets its own value generator when using the in-memory database. 此外,如果删除了数据库,则会为所有表重置键生成。Also, if the database is deleted, then key generation is reset for all tables.

为什么Why

此更改的目的是使内存中的键生成更接近于实际的数据库键生成,并改进在使用内存中的数据库时隔离测试的功能。This change was made to align in-memory key generation more closely to real database key generation and to improve the ability to isolate tests from each other when using the in-memory database.

缓解措施Mitigations

这可能会中断依赖于特定内存中键值的应用程序。This can break an application that is relying on specific in-memory key values to be set. 请考虑不依赖于特定键值,或者进行更新以匹配新行为。Consider instead not relying on specific key values, or updating to match the new behavior.

默认情况下使用支持字段Backing fields are used by default

跟踪问题 #12430Tracking Issue #12430

旧行为Old behavior

在 3.0 之前,即使已知属性的支持字段,EF Core 仍将默认使用属性 getter 和 setter 方法读取和写入属性值。Before 3.0, even if the backing field for a property was known, EF Core would still by default read and write the property value using the property getter and setter methods. 例外情况是查询执行,如果已知,将直接设置支持字段。The exception to this was query execution, where the backing field would be set directly if known.

新行为New behavior

从 EF Core 3.0 开始,如果已知属性的支持字段,EF Core 将始终使用支持字段读取和写入该属性。Starting with EF Core 3.0, if the backing field for a property is known, then EF Core will always read and write that property using the backing field. 如果应用程序依赖于编码到 getter 或 setter 方法中的其他行为,则可能导致应用程序中断。This could cause an application break if the application is relying on additional behavior coded into the getter or setter methods.

为什么Why

此更改是为了防止 EF Core 在执行涉及实体的数据库操作时默认错误地触发业务逻辑。This change was made to prevent EF Core from erroneously triggering business logic by default when performing database operations involving the entities.

缓解措施Mitigations

可以通过在 ModelBuilder 上配置属性访问模式来恢复 3.0 之前的行为。The pre-3.0 behavior can be restored through configuration of the property access mode on ModelBuilder. 例如:For example:

modelBuilder.UsePropertyAccessMode(PropertyAccessMode.PreferFieldDuringConstruction);

如果找到多个兼容的支持字段,则引发Throw if multiple compatible backing fields are found

跟踪问题 #12523Tracking Issue #12523

旧行为Old behavior

在 EF Core 3.0 之前,如果多个字段与查找属性的后备字段的规则匹配,则将基于某种优先顺序选择一个字段。Before EF Core 3.0, if multiple fields matched the rules for finding the backing field of a property, then one field would be chosen based on some precedence order. 这可能导致在不明确的情况下使用错误字段。This could cause the wrong field to be used in ambiguous cases.

新行为New behavior

从 EF Core 3.0 开始,如果多个字段与同一属性匹配,则引发异常。Starting with EF Core 3.0, if multiple fields are matched to the same property, then an exception is thrown.

为什么Why

此更改是为了避免在只有一个字段是正确的情况下无提示地使用另一个字段。This change was made to avoid silently using one field over another when only one can be correct.

缓解措施Mitigations

具有不明确的支持字段的属性必须具有显式指定的字段。Properties with ambiguous backing fields must have the field to use specified explicitly. 例如,使用 Fluent API:For example, using the fluent API:

modelBuilder
    .Entity<Blog>()
    .Property(e => e.Id)
    .HasField("_id");

“仅字段”属性名应与字段名匹配Field-only property names should match the field name

旧行为Old behavior

在 EF Core 3.0 之前,可通过字符串值指定属性,如果在 .NET 类型上找不到具有该名称的属性,则 EF Core 将尝试使用约定规则将其与字段匹配。Before EF Core 3.0, a property could be specified by a string value and if no property with that name was found on the .NET type then EF Core would try to match it to a field using convention rules.

private class Blog
{
    private int _id;
    public string Name { get; set; }
}
modelBuilder
    .Entity<Blog>()
    .Property("Id");

新行为New behavior

从 EF Core 3.0 开始,“仅字段”属性必须与字段名完全匹配。Starting with EF Core 3.0, a field-only property must match the field name exactly.

modelBuilder
    .Entity<Blog>()
    .Property("_id");

为什么Why

进行此更改是为了避免对两个名称相似的属性使用相同的字段,这也使得仅字段属性的匹配规则与映射到 CLR 属性的属性相同。This change was made to avoid using the same field for two properties named similarly, it also makes the matching rules for field-only properties the same as for properties mapped to CLR properties.

缓解措施Mitigations

仅字段属性名必须与它们映射到的字段名相同。Field-only properties must be named the same as the field they are mapped to. 在 3.0 版之后的 EF Core 未来版本中,我们计划重新启用显式配置与属性名称不同的字段名称(请参阅问题 15307):In a future release of EF Core after 3.0, we plan to re-enable explicitly configuring a field name that is different from the property name (see issue #15307):

modelBuilder
    .Entity<Blog>()
    .Property("Id")
    .HasField("_id");

AddDbContext/AddDbContextPool 不再调用 AddLogging 和 AddMemoryCacheAddDbContext/AddDbContextPool no longer call AddLogging and AddMemoryCache

跟踪问题 #14756Tracking Issue #14756

旧行为Old behavior

在 EF Core 3.0 之前,调用 AddDbContextAddDbContextPool 的操作也会通过调用 AddLoggingAddMemoryCache 向 DI 注册日志记录和内存缓存服务。Before EF Core 3.0, calling AddDbContext or AddDbContextPool would also register logging and memory caching services with DI through calls to AddLogging and AddMemoryCache.

新行为New behavior

从 EF Core 3.0 开始,AddDbContextAddDbContextPool 将无法再在依赖注入 (DI) 中注册这些服务。Starting with EF Core 3.0, AddDbContext and AddDbContextPool will no longer register these services with Dependency Injection (DI).

为什么Why

EF Core 3.0 不要求这些服务位于应用程序的 DI 容器中。EF Core 3.0 does not require that these services are in the application's DI container. 但是,如果 ILoggerFactory 在应用程序的 DI 容器中注册,它仍然会由 EF Core 使用。However, if ILoggerFactory is registered in the application's DI container, then it will still be used by EF Core.

缓解措施Mitigations

如果应用程序需要这些服务,则使用 AddLoggingAddMemoryCache 将它们显式注册到 DI 容器中。If your application needs these services, then register them explicitly with the DI container using AddLogging or AddMemoryCache.

AddEntityFramework* 添加具有大小限制的 IMemoryCacheAddEntityFramework* adds IMemoryCache with a size limit

跟踪问题 #12905Tracking Issue #12905

旧行为Old behavior

在 EF Core 3.0 之前,调用 AddEntityFramework* 方法还将向 DI 注册内存缓存服务,且没有大小限制。Before EF Core 3.0, calling AddEntityFramework* methods would also register memory caching services with DI without a size limit.

新行为New behavior

自 EF Core 3.0 起,AddEntityFramework* 注册的 IMemoryCache 服务具有大小限制。Starting with EF Core 3.0, AddEntityFramework* will register an IMemoryCache service with a size limit. 如果随后添加的任何其他服务依赖于 IMemoryCache,则它们很快就会达到默认限制,从而导致异常或性能下降。If any other services added afterwards depend on IMemoryCache they can quickly reach the default limit causing exceptions or degraded performance.

为什么Why

如果查询缓存逻辑中存在 bug 或者查询是动态生成的,则无限制地使用 IMemoryCache 可能会导致内存使用量不受控制。Using IMemoryCache without a limit could result in uncontrolled memory usage if there is a bug in query caching logic or the queries are generated dynamically. 设定默认限制可减少潜在的 DoS 攻击。Having a default limit mitigates a potential DoS attack.

缓解措施Mitigations

在大多数情况下,如果同时调用了 AddDbContextAddDbContextPool,则无需调用 AddEntityFramework*In most cases calling AddEntityFramework* is not necessary if AddDbContext or AddDbContextPool is called as well. 因此,最好的缓解措施是删除 AddEntityFramework* 调用。Therefore, the best mitigation is to remove the AddEntityFramework* call.

如果应用程序需要这些服务,则使用 AddMemoryCache 预先向 DI 容器显式注册 IMemoryCache 实现。If your application needs these services, then register a IMemoryCache implementation explicitly with the DI container beforehand using AddMemoryCache.

DbContext.Entry 现在执行本地 DetectChangesDbContext.Entry now performs a local DetectChanges

跟踪问题 #13552Tracking Issue #13552

旧行为Old behavior

在 EF Core 3.0 之前,调用 DbContext.Entry 将导致检测到所有被跟踪实体的更改。Before EF Core 3.0, calling DbContext.Entry would cause changes to be detected for all tracked entities. 这确保了 EntityEntry 中暴露的状态是最新的。This ensured that the state exposed in the EntityEntry was up-to-date.

新行为New behavior

从 EF Core 3.0 开始,调用 DbContext.Entry 现在只会尝试检测给定实体和与之相关的任何跟踪主体实体的更改。Starting with EF Core 3.0, calling DbContext.Entry will now only attempt to detect changes in the given entity and any tracked principal entities related to it. 这意味着可能无法通过调用此方法检测到其他位置的更改,这可能会影响应用程序状态。This means that changes elsewhere may not have been detected by calling this method, which could have implications on application state.

请注意,如果 ChangeTracker.AutoDetectChangesEnabled 设置为 false,则即使是本地更改检测也将被禁用。Note that if ChangeTracker.AutoDetectChangesEnabled is set to false then even this local change detection will be disabled.

导致更改检测的其他方法(例如 ChangeTracker.EntriesSaveChanges)仍然会导致所有被跟踪实体的完整 DetectChangesOther methods that cause change detection--for example ChangeTracker.Entries and SaveChanges--still cause a full DetectChanges of all tracked entities.

为什么Why

此更改是为了提高使用 context.Entry 的默认性能。This change was made to improve the default performance of using context.Entry.

缓解措施Mitigations

在调用 Entry 之前显式调用 ChangeTracker.DetectChanges() 以确保 3.0 之前的行为。Call ChangeTracker.DetectChanges() explicitly before calling Entry to ensure the pre-3.0 behavior.

默认情况下,字符串和字节数组键不是客户端生成的String and byte array keys are not client-generated by default

跟踪问题 #14617Tracking Issue #14617

旧行为Old behavior

在 EF Core 3.0 之前,可以使用 stringbyte[] 键属性,而不需要显式地设置非 null 值。Before EF Core 3.0, string and byte[] key properties could be used without explicitly setting a non-null value. 在这种情况下,键值将在客户端上生成为 GUID,并序列化为 byte[] 的字节。In such a case, the key value would be generated on the client as a GUID, serialized to bytes for byte[].

新行为New behavior

从 EF Core 3.0 开始,将引发异常,指示未设置任何键值。Starting with EF Core 3.0 an exception will be thrown indicating that no key value has been set.

为什么Why

之所以进行此更改是因为客户端生成的 string/byte[] 值通常没有用,并且默认行为使得很难以通用方式推断生成的键值。This change was made because client-generated string/byte[] values generally aren't useful, and the default behavior made it hard to reason about generated key values in a common way.

缓解措施Mitigations

如果没有设置其他非 null 值,则可以通过显式指定键属性应使用生成的值来获得 3.0 之前的行为。The pre-3.0 behavior can be obtained by explicitly specifying that the key properties should use generated values if no other non-null value is set. 例如,使用 Fluent API:For example, with the fluent API:

modelBuilder
    .Entity<Blog>()
    .Property(e => e.Id)
    .ValueGeneratedOnAdd();

或使用数据注释:Or with data annotations:

[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public string Id { get; set; }

ILoggerFactory 现在是一个在一定范围内有效的服务ILoggerFactory is now a scoped service

跟踪问题 #14698Tracking Issue #14698

旧行为Old behavior

在 EF Core 3.0 之前,ILoggerFactory 被注册为单一实例服务。Before EF Core 3.0, ILoggerFactory was registered as a singleton service.

新行为New behavior

从 EF Core 3.0 开始,ILoggerFactory 现已注册为作用域。Starting with EF Core 3.0, ILoggerFactory is now registered as scoped.

为什么Why

此更改是为了允许记录器与 DbContext 实例关联,从而启用其他功能并删除一些反常行为,例如内部服务提供商爆炸式增长的情况。This change was made to allow association of a logger with a DbContext instance, which enables other functionality and removes some cases of pathological behavior such as an explosion of internal service providers.

缓解措施Mitigations

除非在 EF Core 内部服务提供商上注册和使用自定义服务,否则此更改不应影响应用程序代码。This change should not impact application code unless it is registering and using custom services on the EF Core internal service provider. 这并不常见。This isn't common. 在这些情况下,大多数事情仍然有效,但是需要更改依赖于 ILoggerFactory 的任何单一实例服务以便以不同的方式获取 ILoggerFactoryIn these cases, most things will still work, but any singleton service that was depending on ILoggerFactory will need to be changed to obtain the ILoggerFactory in a different way.

如果遇到此类情况,请在 EF Core GitHub 问题跟踪程序上提交一个问题,让我们知道你是如何使用 ILoggerFactory 的,以便我们更好地理解今后如何避免这种情况再次发生。If you run into situations like this, please file an issue at on the EF Core GitHub issue tracker to let us know how you are using ILoggerFactory such that we can better understand how not to break this again in the future.

延迟加载代理不再假定导航属性已完全加载Lazy-loading proxies no longer assume navigation properties are fully loaded

跟踪问题 #12780Tracking Issue #12780

旧行为Old behavior

在 EF Core 3.0 之前,一旦 DbContext 被处置,就无法知道从该上下文获得的实体上的给定导航属性是否已完全加载。Before EF Core 3.0, once a DbContext was disposed there was no way of knowing if a given navigation property on an entity obtained from that context was fully loaded or not. 相反,如果导航有一个非 null 值,代理将假定加载一个引用导航,如果导航非空,则假定加载集合导航。Proxies would instead assume that a reference navigation is loaded if it has a non-null value, and that a collection navigation is loaded if it isn't empty. 在这些情况下,尝试延迟加载将是无效的。In these cases, attempting to lazy-load would be a no-op.

新行为New behavior

从 EF Core 3.0 开始,代理会跟踪是否加载了导航属性。Starting with EF Core 3.0, proxies keep track of whether or not a navigation property is loaded. 这意味着如果尝试访问在释放了上下文之后加载的导航属性,其结果始终是无操作,即使加载的导航为空或为 null。This means attempting to access a navigation property that is loaded after the context has been disposed will always be a no-op, even when the loaded navigation is empty or null. 相反,即使导航属性是非空集合,尝试访问未加载的导航属性也会引发异常。Conversely, attempting to access a navigation property that isn't loaded will throw an exception if the context is disposed even if the navigation property is a non-empty collection. 如果出现这种情况,则表示应用程序代码在无效时间尝试使用延迟加载,应将应用程序更改为不执行此操作。If this situation arises, it means the application code is attempting to use lazy-loading at an invalid time, and the application should be changed to not do this.

为什么Why

此更改是为了在尝试对已释放的 DbContext 实例进行延迟加载时使行为保持一致和正确。This change was made to make the behavior consistent and correct when attempting to lazy-load on a disposed DbContext instance.

缓解措施Mitigations

更新应用程序代码,以避免尝试对已释放的上下文进行延迟加载,或者将其配置为无操作,如异常消息中所述。Update application code to not attempt lazy-loading with a disposed context, or configure this to be a no-op as described in the exception message.

默认情况下,现在过度创建内部服务提供程序是一个错误Excessive creation of internal service providers is now an error by default

跟踪问题 #10236Tracking Issue #10236

旧行为Old behavior

在 EF Core 3.0 之前,对于创建了大量内部服务提供程序的应用程序,将会记录一个警告。Before EF Core 3.0, a warning would be logged for an application creating a pathological number of internal service providers.

新行为New behavior

从 EF Core 3.0 开始,现在会考虑此警告,并引发错误和异常。Starting with EF Core 3.0, this warning is now considered and error and an exception is thrown.

为什么Why

此更改是为了通过更明显地暴露这个病态案例来驱动生成更好的应用程序代码。This change was made to drive better application code through exposing this pathological case more explicitly.

缓解措施Mitigations

遇到此错误时,最合适的操作是了解根本原因并停止创建如此多的内部服务提供程序。The most appropriate cause of action on encountering this error is to understand the root cause and stop creating so many internal service providers. 但是,可以通过 DbContextOptionsBuilder 上的配置将错误转换回警告(或忽略)。However, the error can be converted back to a warning (or ignored) via configuration on the DbContextOptionsBuilder. 例如:For example:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
    optionsBuilder
        .ConfigureWarnings(w => w.Log(CoreEventId.ManyServiceProvidersCreatedWarning));
}

使用单个字符串调用 HasOne/HasMany 的新行为New behavior for HasOne/HasMany called with a single string

跟踪问题 #9171Tracking Issue #9171

旧行为Old behavior

在 EF Core 3.0 之前,对通过单个字符串调用 HasOneHasMany 的代码的解释令人困惑。Before EF Core 3.0, code calling HasOne or HasMany with a single string was interpreted in a confusing way. 例如:For example:

modelBuilder.Entity<Samurai>().HasOne("Entrance").WithOne();

该代码看起来是使用 Entrance 导航属性(可能是私有属性)将 Samurai 与某些其他实体类型关联起来的。The code looks like it is relating Samurai to some other entity type using the Entrance navigation property, which may be private.

实际上,此代码试图创建与某个名为 Entrance 的实体类型的关系,该实体类型没有导航属性。In reality, this code attempts to create a relationship to some entity type called Entrance with no navigation property.

新行为New behavior

从 EF Core 3.0 开始,上面的代码现执行它以前应执行的操作。Starting with EF Core 3.0, the code above now does what it looked like it should have been doing before.

为什么Why

这一旧行为令人非常困惑,尤其是在读取配置代码和查找错误时。The old behavior was very confusing, especially when reading the configuration code and looking for errors.

缓解措施Mitigations

这只会中断使用类型名称字符串显式配置关系而无需显式指定导航属性的应用程序。This will only break applications that are explicitly configuring relationships using strings for type names and without specifying the navigation property explicitly. 这并不常见。This is not common. 以前的行为可以通过显式传递导航属性名称的 null 获得。The previous behavior can be obtained through explicitly passing null for the navigation property name. 例如:For example:

modelBuilder.Entity<Samurai>().HasOne("Some.Entity.Type.Name", null).WithOne();

多个异步方法的返回类型已从 Task 更改为 ValueTaskThe return type for several async methods has been changed from Task to ValueTask

跟踪问题 #15184Tracking Issue #15184

旧行为Old behavior

以下异步方法之前返回的是 Task<T>The following async methods previously returned a Task<T>:

  • DbContext.FindAsync()
  • DbSet.FindAsync()
  • DbContext.AddAsync()
  • DbSet.AddAsync()
  • ValueGenerator.NextValueAsync()(及派生类)ValueGenerator.NextValueAsync() (and deriving classes)

新行为New behavior

上述方法现返回一个 ValueTask<T>,其中 T 与前述相同。The aforementioned methods now return a ValueTask<T> over the same T as before.

为什么Why

此更改会减少在调用这些方法时发生的堆分配数量,从而提高整体性能。This change reduces the number of heap allocations incurred when invoking these methods, improving general performance.

缓解措施Mitigations

仅需重新编译只等待上述 API 的应用程序 -无需更改源。Applications simply awaiting the above APIs only need to be recompiled - no source changes are necessary. 更复杂的用法(例如,将返回的 Task 传递到 Task.WhenAny())通常需要通过对返回的 ValueTask<T> 调用 AsTask() 来将其转换为 Task<T>A more complex usage (e.g. passing the returned Task to Task.WhenAny()) typically require that the returned ValueTask<T> be converted to a Task<T> by calling AsTask() on it. 请注意,这会抵消此更改所带来的分配数减少优势。Note that this negates the allocation reduction that this change brings.

关系式:TypeMapping 注释现在只是 TypeMappingThe Relational:TypeMapping annotation is now just TypeMapping

跟踪问题 #9913Tracking Issue #9913

旧行为Old behavior

类型映射注释的注释名称是“Relational:TypeMapping”。The annotation name for type mapping annotations was "Relational:TypeMapping".

新行为New behavior

类型映射注释的注释名称现在是“TypeMapping”。The annotation name for type mapping annotations is now "TypeMapping".

为什么Why

类型映射现在不仅用于关系数据库提供程序。Type mappings are now used for more than just relational database providers.

缓解措施Mitigations

这只会中断直接作为注释访问类型映射的应用程序,这不常见。This will only break applications that access the type mapping directly as an annotation, which isn't common. 最合适的修复操作是使用 API 曲面来访问类型映射,而不是直接使用注释。The most appropriate action to fix is to use API surface to access type mappings rather than using the annotation directly.

派生类型上的 ToTable 会引发异常ToTable on a derived type throws an exception

跟踪问题 #11811Tracking Issue #11811

旧行为Old behavior

在 EF Core 3.0 之前,将忽略调用派生类型的 ToTable(),因为只有继承映射策略是 TPH,这是无效的。Before EF Core 3.0, ToTable() called on a derived type would be ignored since only inheritance mapping strategy was TPH where this isn't valid.

新行为New behavior

从 EF Core 3.0 开始,同时为在以后的版本中添加 TPT 和 TPC 支持做准备,调用派生类型的 ToTable() 现在将引发异常,以避免将来发生意外的映射更改。Starting with EF Core 3.0 and in preparation for adding TPT and TPC support in a later release, ToTable() called on a derived type will now throw an exception to avoid an unexpected mapping change in the future.

为什么Why

目前,将派生类型映射到不同的表是无效的。Currently it isn't valid to map a derived type to a different table. 这种改变避免了在将来当它变为有效时被中断。This change avoids breaking in the future when it becomes a valid thing to do.

缓解措施Mitigations

删除将派生类型映射到其他表的任何尝试。Remove any attempts to map derived types to other tables.

用 HasIndex 替换 ForSqlServerHasIndexForSqlServerHasIndex replaced with HasIndex

跟踪问题 #12366Tracking Issue #12366

旧行为Old behavior

在 EF Core 3.0 之前,ForSqlServerHasIndex().ForSqlServerInclude() 提供了一种配置与 INCLUDE 一起使用的列的方法。Before EF Core 3.0, ForSqlServerHasIndex().ForSqlServerInclude() provided a way to configure columns used with INCLUDE.

新行为New behavior

从 EF Core 3.0 开始,现在支持在关系级别上对索引使用 IncludeStarting with EF Core 3.0, using Include on an index is now supported at the relational level. 请使用 HasIndex().ForSqlServerInclude()Use HasIndex().ForSqlServerInclude().

为什么Why

此更改是为了将用于索引的 API 与 Include 合并到一个位置,以供所有数据库提供程序使用。This change was made to consolidate the API for indexes with Include into one place for all database providers.

缓解措施Mitigations

使用新的 API,如上所示。Use the new API, as shown above.

元数据 API 更改Metadata API changes

跟踪问题 #214Tracking Issue #214

新行为New behavior

以下属性已转换为扩展方法:The following properties were converted to extension methods:

  • IEntityType.QueryFilter -> GetQueryFilter()
  • IEntityType.DefiningQuery -> GetDefiningQuery()
  • IProperty.IsShadowProperty -> IsShadowProperty()
  • IProperty.BeforeSaveBehavior -> GetBeforeSaveBehavior()
  • IProperty.AfterSaveBehavior -> GetAfterSaveBehavior()

为什么Why

此更改简化了上述接口的实现。This change simplifies the implementation of the aforementioned interfaces.

缓解措施Mitigations

使用新的扩展方法。Use the new extension methods.

特定于提供程序的元数据 API 更改Provider-specific Metadata API changes

跟踪问题 #214Tracking Issue #214

新行为New behavior

将展开特定于提供程序的扩展方法:The provider-specific extension methods will be flattened out:

  • IProperty.Relational().ColumnName -> IProperty.GetColumnName()
  • IEntityType.SqlServer().IsMemoryOptimized -> IEntityType.IsMemoryOptimized()
  • PropertyBuilder.UseSqlServerIdentityColumn() -> PropertyBuilder.UseIdentityColumn()

为什么Why

此更改简化了上述扩展方法的实现。This change simplifies the implementation of the aforementioned extension methods.

缓解措施Mitigations

使用新的扩展方法。Use the new extension methods.

EF Core 不再发送 pragma 来执行 SQLite FKEF Core no longer sends pragma for SQLite FK enforcement

跟踪问题 #12151Tracking Issue #12151

旧行为Old behavior

在 EF Core 3.0 之前,当打开与 SQLite 的连接时,EF Core 会发送 PRAGMA foreign_keys = 1Before EF Core 3.0, EF Core would send PRAGMA foreign_keys = 1 when a connection to SQLite is opened.

新行为New behavior

从 EF Core 3.0 开始,当打开到 SQLite 的连接时,EF Core 不再发送 PRAGMA foreign_keys = 1Starting with EF Core 3.0, EF Core no longer sends PRAGMA foreign_keys = 1 when a connection to SQLite is opened.

为什么Why

之所以进行此更改,是因为 EF Core 默认使用 SQLitePCLRaw.bundle_e_sqlite3,这意味着 FK 强制执行操作在默认情况下是打开的,并且不需要在每次打开连接时显式启用。This change was made because EF Core uses SQLitePCLRaw.bundle_e_sqlite3 by default, which in turn means that FK enforcement is switched on by default and doesn't need to be explicitly enabled each time a connection is opened.

缓解措施Mitigations

默认情况下,SQLitePCLRaw.bundle_e_sqlite3 中启用了默认用于 EF Core 的外键。Foreign keys are enabled by default in SQLitePCLRaw.bundle_e_sqlite3, which is used by default for EF Core. 对于其他情况,可以通过在连接字符串中指定 Foreign Keys=True 来启用外键。For other cases, foreign keys can be enabled by specifying Foreign Keys=True in your connection string.

Microsoft.EntityFrameworkCore.Sqlite 现在依赖于 SQLitePCLRaw.bundle_e_sqlite3Microsoft.EntityFrameworkCore.Sqlite now depends on SQLitePCLRaw.bundle_e_sqlite3

旧行为Old behavior

在 EF Core 3.0 之前,EF Core 使用 SQLitePCLRaw.bundle_greenBefore EF Core 3.0, EF Core used SQLitePCLRaw.bundle_green.

新行为New behavior

从 EF Core 3.0 开始,EF Core 使用 SQLitePCLRaw.bundle_e_sqlite3Starting with EF Core 3.0, EF Core uses SQLitePCLRaw.bundle_e_sqlite3.

为什么Why

此更改是为了使 iOS 上使用的 SQLite 版本与其他平台一致。This change was made so that the version of SQLite used on iOS consistent with other platforms.

缓解措施Mitigations

若要在 iOS 上使用本机 SQLite 版本,请配置 Microsoft.Data.Sqlite 以使用其他 SQLitePCLRaw 捆绑包。To use the native SQLite version on iOS, configure Microsoft.Data.Sqlite to use a different SQLitePCLRaw bundle.

GUID 值现在以文本形式存储在 SQLite 上Guid values are now stored as TEXT on SQLite

跟踪问题 #15078Tracking Issue #15078

旧行为Old behavior

GUID 值之前以 BLOB 值形式存储在 SQLite 上。Guid values were previously stored as BLOB values on SQLite.

新行为New behavior

Guid 值现在以文本形式存储。Guid values are now stored as TEXT.

为什么Why

GUID 的二进制格式不会进行标准化。The binary format of Guids is not standardized. 以文本形式存储值使数据库与其他技术更兼容。Storing the values as TEXT makes the database more compatible with other technologies.

缓解措施Mitigations

现在通过执行如下的 SQL,可以将现有数据库转成新的格式。You can migrate existing databases to the new format by executing SQL like the following.

UPDATE MyTable
SET GuidColumn = hex(substr(GuidColumn, 4, 1)) ||
                 hex(substr(GuidColumn, 3, 1)) ||
                 hex(substr(GuidColumn, 2, 1)) ||
                 hex(substr(GuidColumn, 1, 1)) || '-' ||
                 hex(substr(GuidColumn, 6, 1)) ||
                 hex(substr(GuidColumn, 5, 1)) || '-' ||
                 hex(substr(GuidColumn, 8, 1)) ||
                 hex(substr(GuidColumn, 7, 1)) || '-' ||
                 hex(substr(GuidColumn, 9, 2)) || '-' ||
                 hex(substr(GuidColumn, 11, 6))
WHERE typeof(GuidColumn) == 'blob';

在 EF Core 中,还可以通过为这些属性配置值转换器,继续使用以前的行为模式。In EF Core, you could also continue using the previous behavior by configuring a value converter on these properties.

modelBuilder
    .Entity<MyEntity>()
    .Property(e => e.GuidProperty)
    .HasConversion(
        g => g.ToByteArray(),
        b => new Guid(b));

Microsoft.Data.Sqlite 仍然能够从“BLOB”和“文本”列读取 GUID 值;但是,由于参数和常量的默认格式已更改,可能需要针对涉及 GUID 的大多数情况采取措施。Microsoft.Data.Sqlite remains capable of reading Guid values from both BLOB and TEXT columns; however, since the default format for parameters and constants has changed you'll likely need to take action for most scenarios involving Guids.

Char 值现在以文本形式存储在 SQLite 上Char values are now stored as TEXT on SQLite

跟踪问题 #15020Tracking Issue #15020

旧行为Old behavior

Char 值之前以整数值形式存储在 SQLite 上。Char values were previously sored as INTEGER values on SQLite. 例如,A 的 char 值存储为整数值 65 。For example, a char value of A was stored as the integer value 65.

新行为New behavior

Char 值现在以文本形式存储。Char values are now stored as TEXT.

为什么Why

以文本形式存储值显得更加自然,并且使数据库与其他技术更兼容。Storing the values as TEXT is more natural and makes the database more compatible with other technologies.

缓解措施Mitigations

现在通过执行如下的 SQL,可以将现有数据库转成新的格式。You can migrate existing databases to the new format by executing SQL like the following.

UPDATE MyTable
SET CharColumn = char(CharColumn)
WHERE typeof(CharColumn) = 'integer';

在 EF Core 中,还可以通过为这些属性配置值转换器,继续使用以前的行为模式。In EF Core, you could also continue using the previous behavior by configuring a value converter on these properties.

modelBuilder
    .Entity<MyEntity>()
    .Property(e => e.CharProperty)
    .HasConversion(
        c => (long)c,
        i => (char)i);

Microsoft.Data.Sqlite 也仍然能够读取整数列和文本列的字符值,因此某些情况可能不需要任何操作。Microsoft.Data.Sqlite also remains capable of reading character values from both INTEGER and TEXT columns, so certain scenarios may not require any action.

现在使用固定区域性的日历生成迁移 IDMigration IDs are now generated using the invariant culture's calendar

跟踪问题 #12978Tracking Issue #12978

旧行为Old behavior

以前使用当前区域性的日历无意生成迁移 ID。Migration IDs were inadvertently generated using the current culture's calendar.

新行为New behavior

现在始终使用固定区域性的日历(公历)生成迁移 ID。Migration IDs are now always generated using the invariant culture's calendar (Gregorian).

为什么Why

更新数据库或解决合并冲突时,迁移的顺序非常重要。The order of migrations is important when updating the database or resolving merge conflicts. 使用固定日历可以避免因团队成员采用不同系统日历而产生的顺序问题。Using the invariant calendar avoids ordering issues that can result from team members having different system calendars.

缓解措施Mitigations

如果有人使用年份时间大于公历日历的非公历日历(如泰国佛历),则会受到影响。This change affects anyone using a non-Gregorian calendar where the year is greater than the Gregorian calendar (like the Thai Buddhist calendar). 现有迁移 ID 需要进行更新,以便新迁移排在现有迁移之后。Existing migration IDs will need to be updated so that new migrations are ordered after existing migrations.

在迁移的设计者文件的 Migration 属性中可以找到迁移 ID。The migration ID can be found in the Migration attribute in the migrations' designer files.

 [DbContext(typeof(MyDbContext))]
-[Migration("25620318122820_MyMigration")]
+[Migration("20190318122820_MyMigration")]
 partial class MyMigration
 {

迁移历史记录表还需要更新。The Migrations history table also needs to be updated.

UPDATE __EFMigrationsHistory
SET MigrationId = CONCAT(LEFT(MigrationId, 4)  - 543, SUBSTRING(MigrationId, 4, 150))

UseRowNumberForPaging 已删除UseRowNumberForPaging has been removed

跟踪问题 #16400Tracking Issue #16400

旧行为Old behavior

在 EF Core 3.0 之前,UseRowNumberForPaging 可用于生成与 SQL Server 2008 兼容的分页 SQL。Before EF Core 3.0, UseRowNumberForPaging could be used to generate SQL for paging that is compatible with SQL Server 2008.

新行为New behavior

从 EF Core 3.0 开始,EF 将仅生成仅与更高的 SQL Server 版本兼容的分页 SQL。Starting with EF Core 3.0, EF will only generate SQL for paging that is only compatible with later SQL Server versions.

为什么Why

我们正在进行此更改,因为 SQL Server 2008 不再是受支持的产品,并且更新此功能以使用在 EF Core 3.0 中做出的查询更改是一项重要工作。We are making this change because SQL Server 2008 is no longer a supported product and updating this feature to work with the query changes made in EF Core 3.0 is significant work.

缓解措施Mitigations

建议更新到更高的 SQL Server 版本,或者使用更高的兼容性级别,以便支持生成的 SQL。We recommend updating to a newer version of SQL Server, or using a higher compatibility level, so that the generated SQL is supported. 这就是说,如果无法执行此操作,请在跟踪问题中做出详细注释That being said, if you are unable to do this, then please comment on the tracking issue with details. 我们可能会根据反馈重新考虑这个决定。We may revisit this decision based on feedback.

已从 IDbContextOptionsExtension 中删除扩展信息/元数据Extension info/metadata has been removed from IDbContextOptionsExtension

跟踪问题 #16119Tracking Issue #16119

旧行为Old behavior

IDbContextOptionsExtension 包含用于提供扩展元数据的方法。IDbContextOptionsExtension contained methods for providing metadata about the extension.

新行为New behavior

这些方法已迁移到从新 IDbContextOptionsExtension.Info 属性返回的新 DbContextOptionsExtensionInfo 抽象基类。These methods have been moved onto a new DbContextOptionsExtensionInfo abstract base class, which is returned from a new IDbContextOptionsExtension.Info property.

为什么Why

在从版本 2.0 迁移到版本 3.0 的过程中,我们需要多次添加或更改这些方法。Over the releases from 2.0 to 3.0 we needed to add to or change these methods several times. 将它们拆分成新抽象基类可以更轻松地进行此类更改,而不会破坏现有扩展。Breaking them out into a new abstract base class will make it easier to make these kind of changes without breaking existing extensions.

缓解措施Mitigations

将扩展更新为采用新模式。Update extensions to follow the new pattern. 例如,许多用于 EF Core 源代码中不同种类扩展的 IDbContextOptionsExtension 实现。Examples are found in the many implementations of IDbContextOptionsExtension for different kinds of extensions in the EF Core source code.

已重命名 LogQueryPossibleExceptionWithAggregateOperatorLogQueryPossibleExceptionWithAggregateOperator has been renamed

跟踪问题 #10985Tracking Issue #10985

更改Change

RelationalEventId.LogQueryPossibleExceptionWithAggregateOperator 已重命名为 RelationalEventId.LogQueryPossibleExceptionWithAggregateOperatorWarningRelationalEventId.LogQueryPossibleExceptionWithAggregateOperator has been renamed to RelationalEventId.LogQueryPossibleExceptionWithAggregateOperatorWarning.

为什么Why

将此警告事件的命名方式与所有其他警告事件保持一致。Aligns the naming of this warning event with all other warning events.

缓解措施Mitigations

使用新名称。Use the new name. (注意:事件 ID 号码未更改。)(Note that the event ID number has not changed.)

阐明 API 的外键约束名称Clarify API for foreign key constraint names

跟踪问题 #10730Tracking Issue #10730

旧行为Old behavior

在 EF Core 3.0 之前,外键约束名称被简单地称为“名称”。Before EF Core 3.0, foreign key constraint names were referred to as simply the "name". 例如:For example:

var constraintName = myForeignKey.Name;

新行为New behavior

从 EF Core 3.0 开始,外键约束名称现在被称为“约束名称”。Starting with EF Core 3.0, foreign key constraint names are now referred to as the "constraint name". 例如:For example:

var constraintName = myForeignKey.ConstraintName;

为什么Why

这样不仅可以让此领域的命名方式保持一致,还阐明了这是外键约束的名称,不是定义外键所依据的列或属性名称。This change brings consistency to naming in this area, and also clarifies that this is the name of the foreign key constraint, and not the column or property name that the foreign key is defined on.

缓解措施Mitigations

使用新名称。Use the new name.

IRelationalDatabaseCreator.HasTables/HasTablesAsync 已公开IRelationalDatabaseCreator.HasTables/HasTablesAsync have been made public

跟踪问题 #15997Tracking Issue #15997

旧行为Old behavior

在 EF Core 3.0 推出前,这些方法受保护。Before EF Core 3.0, these methods were protected.

新行为New behavior

自 EF Core 3.0 起,这些方法是公共的。Starting with EF Core 3.0, these methods are public.

为什么Why

EF 使用这些方法来确定数据库是否已创建但为空。These methods are used by EF to determine if a database is created but empty. 这也适用于在 EF 外部确定是否要应用迁移。This can also be useful from outside EF when determining whether or not to apply migrations.

缓解措施Mitigations

更改任何重写的可访问性。Change the accessibility of any overrides.

Microsoft.EntityFrameworkCore.Design 现在是 DevelopmentDependency 包Microsoft.EntityFrameworkCore.Design is now a DevelopmentDependency package

跟踪问题 #11506Tracking Issue #11506

旧行为Old behavior

在 EF Core 3.0 推出前,Microsoft.EntityFrameworkCore.Design 是常规 NuGet 包,它的程序集可以由依赖它的项目引用。Before EF Core 3.0, Microsoft.EntityFrameworkCore.Design was a regular NuGet package whose assembly could be referenced by projects that depended on it.

新行为New behavior

自 EF Core 3.0 起,它是 DevelopmentDependency 包。Starting with EF Core 3.0, it is a DevelopmentDependency package. 这意味着,依赖项不会过渡流动到其他项目中,并且你也无法再默认引用它的程序集。This means that the dependency won't flow transitively into other projects, and that you can no longer, by default, reference its assembly.

为什么Why

此包仅用于设计时。This package is only intended to be used at design time. 已部署的应用程序不得引用它。Deployed applications shouldn't reference it. 让它成为 DevelopmentDependency 包强化了此建议。Making the package a DevelopmentDependency reinforces this recommendation.

缓解措施Mitigations

如果需要引用此包来重写 EF Core 的设计时行为,则可更新项目中的 PackageReference 项元数据。If you need to reference this package to override EF Core's design-time behavior, then you can update PackageReference item metadata in your project.

<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="3.0.0">
  <PrivateAssets>all</PrivateAssets>
  <!-- Remove IncludeAssets to allow compiling against the assembly -->
  <!--<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>-->
</PackageReference>

如果正在通过 Microsoft.EntityFrameworkCore.Tools 过渡引用此包,必须向此包添加显式 PackageReference,以更改它的元数据。If the package is being referenced transitively via Microsoft.EntityFrameworkCore.Tools, you will need to add an explicit PackageReference to the package to change its metadata. 必须在需要此包中的类型的任何项目下添加此类显式引用。Such an explicit reference must be added to any project where the types from the package are needed.

SQLitePCL.raw 已更新为版本 2.0.0SQLitePCL.raw updated to version 2.0.0

跟踪问题 #14824Tracking Issue #14824

旧行为Old behavior

Microsoft.EntityFrameworkCore.Sqlite 以前依赖 SQLitePCL.raw 版本 1.1.12。Microsoft.EntityFrameworkCore.Sqlite previously depended on version 1.1.12 of SQLitePCL.raw.

新行为New behavior

我们已将包更新为依赖版本 2.0.0。We've updated our package to depend on version 2.0.0.

为什么Why

SQLitePCL.raw 版本 2.0.0 定目标到 .NET Standard 2.0。Version 2.0.0 of SQLitePCL.raw targets .NET Standard 2.0. 它以前定目标到 .NET Standard 1.1,这就要求必须将可传递的包用作大型收尾,才能正常运行。It previously targeted .NET Standard 1.1 which required a large closure of transitive packages to work.

缓解措施Mitigations

SQLitePCL.raw 版本 2.0.0 包括一些重大变化。SQLitePCL.raw version 2.0.0 includes some breaking changes. 有关详细信息,请参阅发行说明See the release notes for details.

NetTopologySuite 已更新为版本 2.0.0NetTopologySuite updated to version 2.0.0

跟踪问题 #14825Tracking Issue #14825

旧行为Old behavior

空间包以前依赖于 NetTopologySuite 的 1.15.1 版。The spatial packages previously depended on version 1.15.1 of NetTopologySuite.

新行为New behavior

我们已将包更新为依赖版本 2.0.0。We've update our package to depend on version 2.0.0.

为什么Why

NetTopologySuite 2.0.0 版旨在解决 EF Core 用户遇到的几个可用性问题。Version 2.0.0 of NetTopologySuite aims to address several usability issues encountered by EF Core users.

缓解措施Mitigations

NetTopologySuite 2.0.0 版包括一些重大更改。NetTopologySuite version 2.0.0 includes some breaking changes. 有关详细信息,请参阅发行说明See the release notes for details.

使用 Microsoft.Data.SqlClient 而不是 System.Data.SqlClientMicrosoft.Data.SqlClient is used instead of System.Data.SqlClient

跟踪问题 #15636Tracking Issue #15636

旧行为Old behavior

Microsoft.EntityFrameworkCore.SqlServer 以前依赖 System.Data.SqlClient。Microsoft.EntityFrameworkCore.SqlServer previously depended on System.Data.SqlClient.

新行为New behavior

我们已将包更新为依赖 Microsoft.Data.SqlClient。We've updated our package to depend on Microsoft.Data.SqlClient.

为什么Why

Microsoft.Data.SqlClient 是今后用于 SQL Server 的旗舰版数据访问驱动程序。而 System.Data.SqlClient 不再是开发的重点。Microsoft.Data.SqlClient is the flagship data access driver for SQL Server going forward, and System.Data.SqlClient no longer be the focus of development. 一些重要功能(例如 Always Encrypted)仅可在 Microsoft.Data.SqlClient 上使用。Some important features, such as Always Encrypted, are only available on Microsoft.Data.SqlClient.

缓解措施Mitigations

如果你的代码直接依赖于 System.Data.SqlClient,则必须将其更改为 Microsoft.Data.SqlClient;由于这两个包与 API 的兼容性都非常高,因此这只是简单的包和命名空间更改。If your code takes a direct dependency on System.Data.SqlClient, you must change it to reference Microsoft.Data.SqlClient instead; as the two packages maintain a very high degree of API compatibility, this should only be a simple package and namespace change.

必须配置多个不明确的自引用关系Multiple ambiguous self-referencing relationships must be configured

跟踪问题 #13573Tracking Issue #13573

旧行为Old behavior

具有多个自引用单向导航属性和匹配的 FK 的实体类型被错误配置为单个关系。An entity type with multiple self-referencing uni-directional navigation properties and matching FKs was incorrectly configured as a single relationship. 例如:For example:

public class User 
{
        public Guid Id { get; set; }
        public User CreatedBy { get; set; }
        public User UpdatedBy { get; set; }
        public Guid CreatedById { get; set; }
        public Guid? UpdatedById { get; set; }
}

新行为New behavior

现已在模型构建中检测到此场景,引发了一个指示模型不明确的异常。This scenario is now detected in model building and an exception is thrown indicating that the model is ambiguous.

为什么Why

生成的模型是不明确的,在这种情况下可能会出现错误。The resultant model was ambiguous and will likely usually be wrong for this case.

缓解措施Mitigations

使用关系的完全配置。Use full configuration of the relationship. 例如:For example:

modelBuilder
     .Entity<User>()
     .HasOne(e => e.CreatedBy)
     .WithMany();
 
 modelBuilder
     .Entity<User>()
     .HasOne(e => e.UpdatedBy)
     .WithMany();

DbFunction.Schema 为 null 或者空字符串将其配置为位于模型的默认架构中DbFunction.Schema being null or empty string configures it to be in model's default schema

跟踪问题 #12757Tracking Issue #12757

旧行为Old behavior

配置了实为空字符串的架构的 DbFunction 被视为不带架构的内置函数。A DbFunction configured with schema as an empty string was treated as built-in function without a schema. 例如,下述代码会将 DatePart CLR 函数映射到 SqlServer 上的 DATEPART 内置函数。For example following code will map DatePart CLR function to DATEPART built-in function on SqlServer.

[DbFunction("DATEPART", Schema = "")]
public static int? DatePart(string datePartArg, DateTime? date) => throw new Exception();

新行为New behavior

所有 DbFunction 映射都被视为映射到用户定义的函数。All DbFunction mappings are considered to be mapped to user defined functions. 因此,空字符串值会将函数置于模型的默认架构中。Hence empty string value would put the function inside the default schema for the model. 这可能是通过 Fluent API modelBuilder.HasDefaultSchema() 显式配置的架构,否则为 dboWhich could be the schema configured explicitly via fluent API modelBuilder.HasDefaultSchema() or dbo otherwise.

为什么Why

如果之前的架构为空,可以此将函数视为内置项,但此逻辑仅适用于内置函数不属于任何架构的 SqlServer。Previously schema being empty was a way to treat that function is built-in but that logic is only applicable for SqlServer where built-in functions do not belong to any schema.

缓解措施Mitigations

手动配置 DbFunction 的转换,以将其映射到内置函数中。Configure DbFunction's translation manually to map it to a built-in function.

modelBuilder
    .HasDbFunction(typeof(MyContext).GetMethod(nameof(MyContext.DatePart)))
    .HasTranslation(args => SqlFunctionExpression.Create("DatePart", args, typeof(int?), null));