EF Core 6.0 的中斷性變更

下列 API 和行為變更有可能中斷現有應用程式更新至 EF Core 6.0。

目標 Framework

EF Core 6.0 的目標是 .NET 6。 以舊版 .NET、.NET Core 和 .NET Framework 版本為目標的應用程式必須以 .NET 6 為目標,才能使用 EF Core 6.0。

摘要

重大變更 影響
巢狀選擇性相依專案共享數據表,且不需要儲存任何屬性
變更擁有實體的擁有者現在會擲回例外狀況
Azure Cosmos DB:相關實體類型會探索為擁有
SQLite:集區 連線
沒有對應聯結實體的多對多關聯性現在已建立 Scaffold
清除 DeleteBehavior 與 ON DELETE 值之間的對應
記憶體內部資料庫驗證必要的屬性不包含 Null
已移除集合聯結時的最後一個 ORDER BY
DbSet 不再實作 IAsyncEnumerable
TVF 傳回實體類型預設也會對應至數據表
檢查條件約束名稱唯一性現在已驗證
已新增 IReadOnly 元數據介面並移除擴充方法
IExecutionStrategy 現在是單一服務
SQL Server:將更多錯誤視為暫時性錯誤
Azure Cosmos DB:'id' 值中會逸出更多字元
部分單一服務現在已設定範圍 低*
新增或取代服務之延伸模組的新快取 API 低*
新的快照集和設計時間模型初始化程式
OwnedNavigationBuilder.HasIndex 立即傳回不同的類型
DbFunctionBuilder.HasSchema(null) 重寫 [DbFunction(Schema = "schema")]
預先初始化的導覽會由來自資料庫查詢的值覆寫
查詢時,資料庫中未知的列舉字串值不會轉換成列舉預設值
DbFunctionBuilder.HasTranslation 現在提供函式自變數作為 IReadOnlyList,而不是 IReadOnlyCollection
當實體對應至數據表值函式時,不會移除預設數據表對應
dotnet-ef 目標為 .NET 6
IModelCacheKeyFactory 實作可能需要更新以處理設計時間快取
NavigationBaseIncludeIgnored 現在預設為錯誤

* 這些變更對資料庫提供者和延伸模組的作者特別感興趣。

高影響變更

不允許巢狀選擇性相依專案共享數據表,且不需要任何屬性

追蹤問題 #24558

舊的行為

具有巢狀選擇性相依專案共享數據表且不允許任何必要屬性的模型,但在查詢數據時可能會導致數據遺失,然後再儲存一次。 例如,請考慮下列模型:

public class Customer
{
    public int Id { get; set; }
    public string Name { get; set; }
    public ContactInfo ContactInfo { get; set; }
}

[Owned]
public class ContactInfo
{
    public string Phone { get; set; }
    public Address Address { get; set; }
}

[Owned]
public class Address
{
    public string House { get; set; }
    public string Street { get; set; }
    public string City { get; set; }
    public string Postcode { get; set; }
}

或 中 ContactInfo 不需要任何 Address 屬性,而且所有這些實體類型都對應至相同的數據表。 選擇性相依項目的規則(與必要的相依項相反)表示,如果的所有數據行ContactInfo都是 Null,則查詢擁有者Customer時不會建立 任何 實例ContactInfo。 不過,這也表示不會建立 實例 Address ,即使數據 Address 行不是 Null 也一樣。

新的行為

嘗試使用此模型現在會擲回下列例外狀況:

System.InvalidOperationException:實體類型 'ContactInfo' 是選擇性的相依專案,使用數據表共用並包含其他相依專案,不需要任何必要的非共用屬性,即可識別實體是否存在。 如果資料庫中所有可為 Null 的屬性都包含 Null 值,則不會在查詢中建立對象實例,導致巢狀相依值遺失。 新增必要的屬性,以建立具有 Null 值的其他屬性的實例,或將傳入導覽標示為一律建立實例所需的值。

這可防止查詢和儲存數據時遺失數據。

原因為何

使用具有巢狀選擇性相依的模型共享數據表,且不需要的屬性通常會導致無訊息數據遺失。

風險降低

避免使用選擇性相依專案共享數據表,且不需要任何屬性。 有三個簡單的方法可以執行這項操作:

  1. 讓相依項目成為必要專案。 這表示相依實體在查詢之後一律會有值,即使其所有屬性都是 Null 也一樣。 例如:

    public class Customer
    {
        public int Id { get; set; }
        public string Name { get; set; }
    
        [Required]
        public Address Address { get; set; }
    }
    

    或:

    modelBuilder.Entity<Customer>(
        b =>
            {
                b.OwnsOne(e => e.Address);
                b.Navigation(e => e.Address).IsRequired();
            });
    
  2. 請確定相依專案至少包含一個必要屬性。

  3. 將選擇性相依項目對應至自己的數據表,而不是與主體共享數據表。 例如:

    modelBuilder.Entity<Customer>(
        b =>
            {
                b.ToTable("Customers");
                b.OwnsOne(e => e.Address, b => b.ToTable("CustomerAddresses"));
            });
    

選擇性相依專案和這些風險降低範例的問題包含在 EF Core 6.0 新功能的檔中。

中等影響變更

變更擁有實體的擁有者現在會擲回例外狀況

追蹤問題 #4073

舊的行為

可以將擁有的實體重新指派給不同的擁有者實體。

新的行為

此動作現在會擲回例外狀況:

屬性 '{entityType}。{property}' 是索引鍵的一部分,因此無法修改或標示為已修改。 若要變更具有識別外鍵的現有實體主體,請先刪除相依專案並叫用 『SaveChanges』,然後將相依專案與新的主體產生關聯。

原因為何

雖然我們不需要在擁有的類型上存在索引鍵屬性,但 EF 仍會建立陰影屬性,以做為主鍵和指向擁有者的外鍵。 當擁有者實體變更時,它會導致擁有實體上的外鍵值變更,而且因為它們也會當做主鍵使用,這會導致實體身分識別變更。 EF Core 尚未完全支援這項功能,而且只有有條件地允許擁有的實體,有時會導致內部狀態變得不一致。

風險降低

您可以指派複本並刪除舊的複本,而不是將相同的擁有實例指派給新的擁有者。

追蹤問題 #24803新功能:預設為隱含擁有權

舊的行為

與其他提供者一樣,相關實體類型已探索為一般(非擁有)類型。

新的行為

相關實體類型現在會由探索到的實體類型所擁有。 只有對應至屬性的 DbSet<TEntity> 實體類型會探索為非擁有。

原因為何

此行為遵循將相關數據內嵌至單一檔中的 Azure Cosmos DB 中模型化數據的常見模式。 Azure Cosmos DB 原生不支持聯結不同的檔,因此將相關實體模型化為非擁有的實體具有有限的實用性。

風險降低

將實體類型設定為非擁有的呼叫 modelBuilder.Entity<MyEntity>();

SQLite:集區 連線

追蹤問題 #13837新功能:預設為隱含擁有權

舊的行為

先前,Microsoft.Data.Sqlite 中的連線並未集區。

新的行為

從 6.0 開始,連線預設會集區。 如此一來,即使關閉 ADO.NET 連接對象之後,進程仍會保持開啟資料庫檔案。

原因為何

共用基礎連線可大幅改善開啟和關閉 ADO.NET 連接物件的效能。 對於開啟基礎連線的案例來說,這特別明顯,就像加密的情況一樣,或在資料庫有許多短期連線的情況下。

風險降低

新增至 連接字串,即可停用 Pooling=False 連線 集區。

某些案例(例如刪除資料庫檔案)現在可能會遇到錯誤,指出檔案仍在使用中。 您可以使用 手動清除連接集區,再執行 SqliteConnection.ClearPool()檔案的作業。

SqliteConnection.ClearPool(connection);
File.Delete(databaseFile);

沒有對應聯結實體的多對多關聯性現在已建立 Scaffold

追蹤問題 #22475

舊的行為

Scaffolding (反向工程) DbContext 來自現有資料庫的 和 實體類型一律會明確對應聯結數據表,以聯結多對多關聯性的實體類型。

新的行為

只包含兩個外鍵屬性至其他數據表的簡單聯結數據表不再對應至明確的實體類型,而是對應為兩個聯結數據表之間的多對多關聯性。

原因為何

EF Core 5.0 中引進了沒有明確聯結類型的多對多關聯性,而且是代表簡單聯結數據表更簡潔、更自然的方式。

風險降低

有兩種緩和措施。 慣用的方法是更新程序代碼,以直接使用多對多關聯性。 聯結實體類型在只包含多對多關聯性的兩個外鍵時,需要直接使用。

或者,可以將明確聯結實體新增回 EF 模型。 例如,假設和 Tag之間Post具有多對多關聯性,請使用部分類別來新增聯結類型和導覽:

public partial class PostTag
{
    public int PostsId { get; set; }
    public int TagsId { get; set; }

    public virtual Post Posts { get; set; }
    public virtual Tag Tags { get; set; }
}

public partial class Post
{
    public virtual ICollection<PostTag> PostTags { get; set; }
}

public partial class Tag
{
    public virtual ICollection<PostTag> PostTags { get; set; }
}

然後將聯結類型和導覽的組態新增至 DbContext 的部分類別:

public partial class DailyContext
{
    partial void OnModelCreatingPartial(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Post>(entity =>
        {
            entity.HasMany(d => d.Tags)
                .WithMany(p => p.Posts)
                .UsingEntity<PostTag>(
                    l => l.HasOne<Tag>(e => e.Tags).WithMany(e => e.PostTags).HasForeignKey(e => e.TagsId),
                    r => r.HasOne<Post>(e => e.Posts).WithMany(e => e.PostTags).HasForeignKey(e => e.PostsId),
                    j =>
                    {
                        j.HasKey("PostsId", "TagsId");
                        j.ToTable("PostTag");
                    });
        });
    }
}

最後,從 Scaffolded 內容中移除多對多關聯性的產生組態。 這是必要的,因為必須從模型移除 Scaffold 聯結實體類型,才能使用明確類型。 每次建構內容時,都需要移除此程序代碼,但因為上述程式代碼位於部分類別中,所以它會保存。

請注意,使用此組態時,可以明確地使用聯結實體,就像舊版 EF Core 一樣。 不過,關聯性也可以當做多對多關聯性使用。 這表示更新這類程式代碼可能是暫時的解決方案,而其餘的程式代碼則會更新為以自然方式將關聯性當成多對多。

低影響變更

清除 DeleteBehavior 與 ON DELETE 值之間的對應

追蹤問題 #21252

舊的行為

在移轉和 Scaffolding 中,關聯性 OnDelete() 行為與外鍵 ON DELETE 行為之間的一些對應不一致。

新的行為

下表說明移轉的變更。

OnDelete() ON DELETE
NoAction NO ACTION
ClientNoAction NO ACTION
限制 RESTRICT
Cascasde CASCADE
ClientCascade 限制無動作
SetNull SET NULL
ClientSetNull 限制無動作

Scaffolding變更如下所示。

ON DELETE OnDelete()
NO ACTION ClientSetNull
RESTRICT ClientSetNullRestrict
CASCADE Cascade
SET NULL SetNull

原因為何

新的對應比較一致。 NO ACTION 的預設資料庫行為現在比限制更嚴格且效能較低的 RESTRICT 行為更慣用。

風險降低

選擇性關聯性的預設 OnDelete() 行為為 ClientSetNull。 其對應已從 RESTRICT 變更為 NO ACTION。 這可能會導致升級至 EF Core 6.0 之後,第一次新增移轉時產生許多作業。

您可以選擇套用這些作業,或手動將其從移轉中移除,因為它們對EF Core 沒有功能影響。

SQL Server 不支援 RESTRICT,因此這些外鍵已使用 NO ACTION 建立。 移轉作業不會影響 SQL Server,且安全移除。

記憶體內部資料庫驗證必要的屬性不包含 Null

追蹤問題 #10613

舊的行為

記憶體內部資料庫允許儲存 Null 值,即使屬性已設定為必要也一樣。

新的行為

記憶體內部資料庫會在Microsoft.EntityFrameworkCore.DbUpdateException呼叫 或 SaveChangesAsyncSaveChanges擲回 ,並將必要的屬性設定為 null。

原因為何

記憶體內部資料庫行為現在符合其他資料庫的行為。

風險降低

設定記憶體內部提供者時,可以還原先前的行為(亦即未檢查 Null 值)。 例如:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
    optionsBuilder
        .UseInMemoryDatabase("MyDatabase", b => b.EnableNullChecks(false));
}

已移除集合聯結時的最後一個 ORDER BY

追蹤問題 #19828

舊的行為

在集合上執行 SQL JOIN 時(一對多關聯性),EF Core 用來為聯結數據表的每個索引鍵數據行新增 ORDER BY。 例如,透過下列 SQL 完成載入所有部落格及其相關文章:

SELECT [b].[BlogId], [b].[Name], [p].[PostId], [p].[BlogId], [p].[Title]
FROM [Blogs] AS [b]
LEFT JOIN [Post] AS [p] ON [b].[BlogId] = [p].[BlogId]
ORDER BY [b].[BlogId], [p].[PostId]

這些順序對於實體的適當具體化是必要的。

新的行為

集合聯結的最後一個 ORDER BY 現在省略:

SELECT [b].[BlogId], [b].[Name], [p].[PostId], [p].[BlogId], [p].[Title]
FROM [Blogs] AS [b]
LEFT JOIN [Post] AS [p] ON [b].[BlogId] = [p].[BlogId]
ORDER BY [b].[BlogId]

不再產生 Post 識別碼數據行的 ORDER BY。

原因為何

每個 ORDER BY 都會在資料庫端施加額外的工作,而且 EF Core 具體化需求不需要最後一個順序。 數據顯示,移除此最後一個順序可能會在某些案例中產生顯著的效能改善。

風險降低

如果您的應用程式預期會以特定順序傳回聯結的實體,請將LINQ OrderBy 運算元新增至查詢來明確表示。

DbSet 不再實作 IAsyncEnumerable

追蹤問題 #24041

舊的行為

DbSet<TEntity>,用來在 DbContext 上執行查詢,用來實作 IAsyncEnumerable<T>

新的行為

DbSet<TEntity> 不再直接實作 IAsyncEnumerable<T>

原因為何

DbSet<TEntity>最初是為了主要是為了允許透過 foreach 建構直接列舉而實IAsyncEnumerable<T>作。 不幸的是,當項目也參考 System.Linq.Async 以撰寫異步 LINQ 運算符用戶端時,這會導致定義於的 IQueryable<T> 運算符與透過 IAsyncEnumerable<T>定義的運算符之間產生模棱兩可的調用錯誤。 C# 9 新增了循環foreach擴充GetEnumerator功能支援,移除參考 IAsyncEnumerable的原始主要原因。

絕大多數DbSet使用方式會繼續依目前運作,因為它們會透過 撰寫 LINQ 運算子DbSet、列舉它等等。唯一中斷的使用方式是嘗試直接IAsyncEnumerable轉換成 DbSet 的用法。

風險降低

如果您需要將 參考 DbSet<TEntity>IAsyncEnumerable<T>,請呼叫 DbSet<TEntity>.AsAsyncEnumerable 以明確轉換它。

TVF 傳回實體類型預設也會對應至數據表

追蹤問題 #23408

舊的行為

當做設定 HasDbFunction之TVF的傳回型別時,實體類型預設不會對應至數據表。

新的行為

作為TVF傳回類型的實體類型會保留預設數據表對應。

原因為何

設定TVF會移除傳回實體類型的預設數據表對應並不直覺。

風險降低

若要移除預設資料表對應, 請呼叫 ToTable(EntityTypeBuilder, String)

modelBuilder.Entity<MyEntity>().ToTable((string?)null));

檢查條件約束名稱唯一性現在已驗證

追蹤問題 #25061

舊的行為

允許在相同數據表上宣告及使用具有相同名稱的檢查條件約束。

新的行為

在相同數據表上明確設定兩個具有相同名稱的檢查條件約束,現在會導致例外狀況。 檢查慣例所建立的條件約束將會指派唯一的名稱。

原因為何

大部分的資料庫不允許在相同的數據表上建立兩個具有相同名稱的檢查條件約束,有些資料庫則要求它們在整個數據表中是唯一的。 這會導致套用移轉時擲回例外狀況。

風險降低

在某些情況下,由於這項變更,有效的檢查條件約束名稱可能會不同。 若要明確指定所需的名稱,請呼叫 HasName

modelBuilder.Entity<MyEntity>().HasCheckConstraint("CK_Id", "Id > 0", c => c.HasName("CK_MyEntity_Id"));

已新增 IReadOnly 元數據介面並移除擴充方法

追蹤問題 #19213

舊的行為

元數據介面有三組: IModelIMutableModelIConventionModel 以及擴充方法。

新的行為

新增一組 IReadOnly 新的介面,例如 IReadOnlyModel。 先前為元數據介面定義的擴充方法已轉換成預設介面方法。

原因為何

默認介面方法可覆寫實作,這是由新的運行時間模型實作所利用,以提供更佳的效能。

風險降低

這些變更不應影響大部分的程序代碼。 不過,如果您透過靜態調用語法使用擴充方法,則必須轉換成實例調用語法。

IExecutionStrategy 現在是單一服務

追蹤問題 #21350

新的行為

IExecutionStrategy 現在是單一服務。 這表示自定義實作中的任何新增狀態都會保留在執行之間,而傳遞至 ExecutionStrategy 的委派只會執行一次。

原因為何

這減少了 EF 中兩個經常性路徑的配置。

風險降低

衍生自 ExecutionStrategy 的實作應該清除 中的任何 OnFirstExecution()狀態。

傳遞至 ExecutionStrategy 之委派中的條件式邏輯應該移至 的自定義實作 IExecutionStrategy

SQL Server:將更多錯誤視為暫時性錯誤

追蹤問題 #25050

新的行為

上述問題中列出的錯誤現在會被視為暫時性。 使用預設 (非重試) 執行策略時,這些錯誤現在會包裝在額外的例外狀況實例中。

原因為何

我們會繼續收集使用者和 SQL Server 小組的意見反應,其中應視為暫時性錯誤。

風險降低

若要變更視為暫時性的錯誤集,請使用可衍生自 SqlServerRetryingExecutionStrategy - 連線 ion Resiliency - EF Core 的自定義執行策略。

Azure Cosmos DB:'id' 值中會逸出更多字元

追蹤問題 #25100

舊的行為

在 EF Core 5 中,只有 '|' 值逸 id 出。

新的行為

在 EF Core 6、'/''?''\''#' 中,也會在 值中id逸出。

原因為何

如 Resource.Id 所述,這些字元無效。在中使用id它們會導致查詢失敗。

風險降低

您可以在實體標示為 Added之前設定它來覆寫產生的值:

var entry = context.Attach(entity);
entry.Property("__id").CurrentValue = "MyEntity|/\\?#";
entry.State = EntityState.Added;

部分單一服務現在已設定範圍

追蹤問題 #25084

新的行為

許多查詢服務和一些已註冊 Singleton 為 的設計時間服務,現在會註冊為 Scoped

原因為何

存留期必須變更,才能允許新功能 - DefaultTypeMapping 以影響查詢。

設計時間服務存留期已經過調整,以符合運行時間服務存留期,以避免同時使用兩者時發生錯誤。

風險降低

使用 TryAdd 以預設存留期註冊 EF Core 服務。 TryAddProviderSpecificServices僅適用於 EF 未新增的服務。

新增或取代服務之延伸模組的新快取 API

追蹤問題 #19152

舊的行為

在 EF Core 5 中, GetServiceProviderHashCodelong 回並直接當做服務提供者快取密鑰的一部分使用。

新的行為

GetServiceProviderHashCode 現在會傳 int 回 ,而且僅用於計算服務提供者快取索引鍵的哈希碼。

此外, ShouldUseSameServiceProvider 還需要實作,以指出目前的物件是否代表相同的服務組態,因此可以使用相同的服務提供者。

原因為何

只要使用哈希程式代碼作為快取索引鍵的一部分,就會導致偶爾發生難以診斷和修正的衝突。 其他方法可確保只有在適當時,才會使用相同的服務提供者。

風險降低

許多擴充功能不會公開任何會影響已註冊服務的選項,而且可以使用 下列 實作 ShouldUseSameServiceProvider

private sealed class ExtensionInfo : DbContextOptionsExtensionInfo
{
    public ExtensionInfo(IDbContextOptionsExtension extension)
        : base(extension)
    {
    }

    ...

    public override bool ShouldUseSameServiceProvider(DbContextOptionsExtensionInfo other)
        => other is ExtensionInfo;
}

否則,應該加入其他述詞,以比較所有相關選項。

新的快照集和設計時間模型初始化程式

追蹤問題 #22031

舊的行為

在 EF Core 5 中,需要叫用特定的慣例,才能準備好使用快照集模型。

新的行為

IModelRuntimeInitializer 引進來隱藏一些必要步驟,並引進了運行時間模型,但沒有任何移轉元數據,因此設計時間模型應該用於模型差異。

原因為何

IModelRuntimeInitializer 會抽象化模型最終化步驟,因此現在可以變更這些步驟,而不會對用戶進行進一步的重大變更。

引進優化運行時間模型以改善運行時間效能。 它有數個優化,其中一項是移除在運行時間未使用的元數據。

風險降低

下列代碼段說明如何檢查目前的模型是否與快照集模型不同:

var snapshotModel = migrationsAssembly.ModelSnapshot?.Model;

if (snapshotModel is IMutableModel mutableModel)
{
    snapshotModel = mutableModel.FinalizeModel();
}

if (snapshotModel != null)
{
    snapshotModel = context.GetService<IModelRuntimeInitializer>().Initialize(snapshotModel);
}

var hasDifferences = context.GetService<IMigrationsModelDiffer>().HasDifferences(
    snapshotModel?.GetRelationalModel(),
    context.GetService<IDesignTimeModel>().Model.GetRelationalModel());

此代碼段示範如何透過在外部建立模型並呼叫 UseModel來實IDesignTimeDbContextFactory<TContext>作 :

internal class MyDesignContext : IDesignTimeDbContextFactory<MyContext>
{
    public TestContext CreateDbContext(string[] args)
    {
        var optionsBuilder = new DbContextOptionsBuilder();
        optionsBuilder.UseSqlServer(Configuration.GetConnectionString("DB"));

        var modelBuilder = SqlServerConventionSetBuilder.CreateModelBuilder();
        CustomizeModel(modelBuilder);
        var model = modelBuilder.Model.FinalizeModel();

        var serviceContext = new MyContext(optionsBuilder.Options);
        model = serviceContext.GetService<IModelRuntimeInitializer>().Initialize(model);
        return new MyContext(optionsBuilder.Options);
    }
}

OwnedNavigationBuilder.HasIndex 立即傳回不同的類型

追蹤問題 #24005

舊的行為

在 EF Core 5 中, HasIndexIndexBuilder<TEntity> 回其中 TEntity 是擁有者類型。

新的行為

HasIndex 現在會傳 IndexBuilder<TDependentEntity>回 ,其中 TDependentEntity 是擁有的類型。

原因為何

傳回的產生器物件未正確輸入。

風險降低

針對最新版 EF Core 重新編譯元件,將足以修正這項變更所造成的任何問題。

DbFunctionBuilder.HasSchema(null) 重寫 [DbFunction(Schema = "schema")]

追蹤問題 #24228

舊的行為

在 EF Core 5 中,使用 null 值呼叫 HasSchema 並不會儲存組態來源,因此DbFunctionAttribute能夠覆寫它。

新的行為

使用 null 值呼叫HasSchema現在會儲存組態來源,並防止屬性覆寫它。

原因為何

使用 ModelBuilder API 指定的組態不應由數據批註覆寫。

風險降低

HasSchema拿掉 呼叫,讓屬性設定架構。

預先初始化的導覽會由來自資料庫查詢的值覆寫

追蹤問題 #23851

舊的行為

設定為空白對象的導覽屬性會保持不變,以便追蹤查詢,但針對非追蹤查詢覆寫。 例如,請考慮下列實體類型:

public class Foo
{
    public int Id { get; set; }

    public Bar Bar { get; set; } = new(); // Don't do this.
}

public class Bar
{
    public int Id { get; set; }
}

包含從Foo.Bar資料庫查詢之實體的無追蹤查詢。FooBar 例如,此程式代碼:

var foo = context.Foos.AsNoTracking().Include(e => e.Bar).Single();
Console.WriteLine($"Foo.Bar.Id = {foo.Bar.Id}");

列印 Foo.Bar.Id = 1的 。

不過,針對追蹤執行的相同查詢不會以從資料庫查詢的實體覆寫 Foo.Bar 。 例如,此程式代碼:

var foo = context.Foos.Include(e => e.Bar).Single();
Console.WriteLine($"Foo.Bar.Id = {foo.Bar.Id}");

列印 Foo.Bar.Id = 0的 。

新的行為

在 EF Core 6.0 中,追蹤查詢的行為現在符合無追蹤查詢的行為。 這表示這兩個程式代碼:

var foo = context.Foos.AsNoTracking().Include(e => e.Bar).Single();
Console.WriteLine($"Foo.Bar.Id = {foo.Bar.Id}");

此程式代碼:

var foo = context.Foos.Include(e => e.Bar).Single();
Console.WriteLine($"Foo.Bar.Id = {foo.Bar.Id}");

列印 Foo.Bar.Id = 1

原因為何

進行這項變更有兩個原因:

  1. 為了確保追蹤和無追蹤查詢的行為一致。
  2. 查詢資料庫時,假設應用程式程式代碼想要取回儲存在資料庫中的值,這是合理的。

風險降低

有兩種緩和措施:

  1. 請勿從資料庫中查詢不應包含在結果中的物件。 例如,在上述代碼段中,如果IncludeFoo.BarBar實例不應該從資料庫傳回並包含在結果中,則不要。
  2. 從資料庫查詢之後,設定導覽的值。 例如,在上述代碼段中,在執行查詢之後呼叫 foo.Bar = new()

此外,請考慮不要將相關的實體實例初始化為默認物件。 這表示相關實例是新的實體,不會儲存至資料庫,且未設定索引鍵值。 如果相關實體確實存在於資料庫中,則程式代碼中的數據基本上與儲存在資料庫中的數據不一致。

查詢時,資料庫中未知的列舉字串值不會轉換成列舉預設值

追蹤問題 #24084

舊的行為

列舉屬性可以使用 或 EnumToStringConverter,對應至資料庫中HasConversion<string>()的字串數據行。 這會導致EF Core 將數據行中的字串值轉換為 .NET 列舉類型的相符成員。 不過,如果字串值不符合 和 列舉成員,則 屬性會設定為列舉的預設值。

新的行為

EF Core 6.0 現在會 InvalidOperationException 擲回 訊息:「無法將字串值 』{value}' 從資料庫轉換成對應 '{enumType}' 列舉中的任何值」。

原因為何

如果實體稍後儲存回資料庫,則轉換成預設值可能會導致資料庫損毀。

風險降低

在理想情況下,請確定資料庫數據行只包含有效的值。 或者,使用舊的行為來實 ValueConverter 作 。

DbFunctionBuilder.HasTranslation 現在提供函式自變數作為 IReadOnlyList,而不是 IReadOnlyCollection

追蹤問題 #23565

舊的行為

使用 HasTranslation 方法來設定使用者定義函式的翻譯時,會將函式的自變數提供為 IReadOnlyCollection<SqlExpression>

新的行為

在 EF Core 6.0 中,自變數現在會提供為 IReadOnlyList<SqlExpression>

原因為何

IReadOnlyList 允許使用索引器,因此自變數現在更容易存取。

風險降低

無。 IReadOnlyList 會實作 IReadOnlyCollection 介面,因此轉換應該很簡單。

當實體對應至數據表值函式時,不會移除預設數據表對應

追蹤問題 #23408

舊的行為

當實體對應至數據表值函式時,會移除對數據表的預設對應。

新的行為

在EF Core 6.0 中,即使實體也對應至數據表值函式,仍會使用預設對應來對應至數據表。

原因為何

傳回實體的數據表值函式通常用來做為協助程式或封裝傳回實體集合的作業,而不是嚴格取代整個數據表。 這項變更的目標是更符合可能的用戶意圖。

風險降低

在模型組態中可以明確停用對應至數據表:

modelBuilder.Entity<MyEntity>().ToTable((string)null);

dotnet-ef 目標為 .NET 6

追蹤問題 #27787

舊的行為

dotnet-ef 命令現在已將 .NET Core 3.1 設為目標。 這可讓您使用較新版本的工具,而不需要安裝較新版本的 .NET 運行時間。

新的行為

在 EF Core 6.0.6 中,dotnet-ef 工具現在以 .NET 6 為目標。 您仍然可以在以舊版 .NET 和 .NET Core 為目標的專案上使用此工具,但您必須安裝 .NET 6 運行時間才能執行此工具。

原因為何

.NET 6.0.200 SDK 更新了 osx-arm64 上的行為 dotnet tool install ,以針對以 .NET Core 3.1 為目標的工具建立 osx-x64 填充碼。 為了維護 dotnet-ef 的工作預設體驗,我們必須將其更新為以 .NET 6 為目標。

風險降低

若要在不安裝 .NET 6 運行時間的情況下執行 dotnet-ef,您可以安裝舊版的工具:

dotnet tool install dotnet-ef --version 3.1.*

IModelCacheKeyFactory 實作可能需要更新以處理設計時間快取

追蹤問題 #25154

舊的行為

IModelCacheKeyFactory 沒有選項可將設計時間模型與運行時間模型分開快取。

新的行為

IModelCacheKeyFactory 具有新的多載,可讓設計時間模型與運行時間模型分開快取。 不實作此方法可能會導致類似下列的例外狀況:

System.InvalidOperationException:'要求的組態不會儲存在讀取優化模型中,請使用 'DbContext.GetService<IDesignTimeModel>()。Model'.'

原因為何

實作已編譯的模型需要分離設計時間(建置模型時使用)和運行時間(在執行查詢等時使用)。 如果運行時間程式代碼需要存取設計時間資訊,則必須快取設計時間模型。

風險降低

實作新的多載。 例如:

public object Create(DbContext context, bool designTime)
    => context is DynamicContext dynamicContext
        ? (context.GetType(), dynamicContext.UseIntProperty, designTime)
        : (object)context.GetType();

查詢中的 'Include' 會忽略流覽 '{navigation}',因為修正會自動填入它。 如果之後在 「包含」中指定任何進一步導覽,則會忽略它們。 不允許回到 Include 樹狀結構。

追蹤問題 #4315

舊的行為

事件 CoreEventId.NavigationBaseIncludeIgnored 預設會記錄為警告。

新的行為

事件 CoreEventId.NavigationBaseIncludeIgnored 預設會記錄為錯誤,並導致擲回例外狀況。

原因為何

不允許這些查詢模式,因此 EF Core 現在會擲回以指出應該更新查詢。

風險降低

將事件設定為警告,即可還原舊的行為。 例如:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    => optionsBuilder.ConfigureWarnings(b => b.Warn(CoreEventId.NavigationBaseIncludeIgnored));