EF Core 9 (EF9) 的重大變更
此頁面記載的 API 和行為變更,可能會中斷 EF Core 8 更新至 EF Core 9 的現有應用程式。 如果要從舊版 EF Core 更新,請務必檢閱之前的重大變更:
目標 Framework
EF Core 9 是以 .NET 8 為目標。 也就是說,以 .NET 8 為目標的現有應用程式可以繼續操作。 以舊版 .NET、.NET Core 和 .NET Framework 版本為目標的應用程式,必須以 .NET 8 或 .NET 9 為目標才能使用 EF Core 9。
摘要
注意
如果您使用 Azure Cosmos DB,請參閱 Azure Cosmos DB 中斷性變更下方的個別章節。
重大變更 | 影響 |
---|---|
EF.Functions.Unhex() 現在起會傳回 byte[]? |
低 |
SqlFunctionExpression 的可 Null 性引數的 arity 已驗證 | 低 |
ToString() 方法現在會傳回實例的 null 空字串 |
低 |
共享架構相依性已更新為9.0.x | 低 |
低度影響變更
EF.Functions.Unhex()
現在起會傳回 byte[]?
舊的行為
EF.Functions.Unhex()
函式之前標註為會傳回 byte[]
。
新的行為
從 EF Core 9.0 開始,Unhex() 會標註為傳回 byte[]?
。
原因為何
Unhex()
會轉譯為 SQLite unhex
函式,它會對無效的輸入傳回 NULL。 因此,Unhex()
在這些案例中傳回的 null
違反了註釋。
風險降低
如果您確定傳遞至 Unhex()
的文字內容代表有效的十六進位字串,就可以直接將容許 Null 的運算子新增為判斷提示,指出叫用絕不會傳回 Null:
var binaryData = await context.Blogs.Select(b => EF.Functions.Unhex(b.HexString)!).ToListAsync();
否則,請對 Unhex() 傳回值中的 Null 新增執行階段檢查。
SqlFunctionExpression 的可 Null 性引數的 arity 已驗證
舊的行為
之前,您建立的 SqlFunctionExpression
可以具有不同數量的引數和可 Null 性傳播引數。
新的行為
從 EF Core 9.0 開始,EF 會擲回引數數量和可 Null 性傳播引數是否相符。
原因為何
引數數量和可 Null 性傳播引數不符,可能會導致非預期行為。
風險降低
請確保 argumentsPropagateNullability
具有的元素數量與 arguments
相同。 如不確定,請為可 null 性引數使用 false
。
ToString()
方法現在會傳回實例的 null
空字串
舊的行為
先前,當自變數值為 null
時,ToString()
EF 會傳回 方法的不一致結果。 例如ToString()
,在傳null
回null
值的 屬性上bool?
,但針對傳回True
其值null
的非屬性bool?
表達式。 其他數據類型的行為也不一致,例如 ToString()
null
傳回空字串的值列舉。
新的行為
從EF Core 9.0 開始, ToString()
此方法現在在所有自變數值為 null
時,一律會傳回空字串。
原因為何
舊行為在不同數據類型和情況之間不一致,也不符合 C# 行為。
風險降低
若要還原為舊行為,請據以重寫查詢:
var newBehavior = context.Entity.Select(x => x.NullableBool.ToString());
var oldBehavior = context.Entity.Select(x => x.NullableBool == null ? null : x.NullableBool.ToString());
共享架構相依性已更新為9.0.x
舊的行為
使用 Microsoft.NET.Sdk.Web
SDK 和以 net8.0 為目標的應用程式會從共用架構解析 、Microsoft.Extensions.Caching.Memory
、 Microsoft.Extensions.Configuration.Abstractions
Microsoft.Extensions.Logging
和 Microsoft.Extensions.DependencyModel
等System.Text.Json
套件,因此這些元件通常不會與應用程式一起部署。
新的行為
雖然 EF Core 9.0 仍然支援 net8.0,但它現在參考 、、 Microsoft.Extensions.Configuration.Abstractions
Microsoft.Extensions.Logging
和 Microsoft.Extensions.DependencyModel
的 System.Text.Json
Microsoft.Extensions.Caching.Memory
9.0.x 版本。 以 net8.0 為目標的應用程式將無法利用共享架構來避免部署這些元件。
原因為何
相符的相依性版本包含最新的安全性修正程式,並使用它們可簡化EF Core的服務模型。
風險降低
將您的應用程式變更為以 net9.0 為目標,以取得先前的行為。
Azure Cosmos DB 重大變更
在 9.0 中,廣泛的工作已讓 Azure Cosmos DB 提供者變得更好。 這些變更包括一系列高度影響的重大變更;如果您要升級現有應用程式,請仔細閱讀下列內容。
高度影響變更
鑑別子屬性現在名為 $type
,而不是 Discriminator
舊的行為
EF 會自動將鑑別子屬性新增至 JSON 文件,藉此識別文件所代表的實體類型。 在舊版 EF 中,此 JSON 屬性預設會名為 Discriminator
。
新的行為
從 EF Core 9.0 開始,鑑別子屬性會預設稱為 $type
。 如果您在舊版 EF 中有 Azure Cosmos DB 中現有的文件,這些檔案會使用舊的 Discriminator
命名,而且升級至 EF 9.0 之後,對這些文件的查詢將會失敗。
原因為何
新興的 JSON 做法會在需要識別檔類型的情境下使用 $type
屬性。 舉例來說,NET 的 System.Text.Json 將 $type
當作預設鑑別子屬性名稱來使用,也能支援多型 (文件)。 為了與生態系統的其餘部分保持一致,並與外部工具更容易互通,預設值已變更。
風險降低
最簡單的緩和措施是將鑑別子屬性的名稱設為 Discriminator
,就像之前一樣:
modelBuilder.Entity<Session>().HasDiscriminator<string>("Discriminator");
對所有最上層實體類型執行這項操作,會讓 EF 按照過去的行為來運作。
到這個階段,如果您有意願,也可以將所有文件更新為使用新的 $type
命名方式。
id
屬性現在預設只包含 EF 索引鍵屬性
舊的行為
以前,EF 會將實體類型的鑑別子值插入文件的 id
屬性中。 舉例來說,如果您儲存的 Blog
實體類型,其中的 Id
屬性包含 8,則 JSON id
屬性會包含 Blog|8
。
新的行為
從EF Core 9.0 開始,JSON id
屬性不再包含歧視性值,而且只包含索引鍵屬性的值。 在上述範例中,JSON id
屬性就會是 8
。 如果您有舊版 EF 中的 Azure Cosmos DB 中現有的文件,這些檔案在 JSON id
屬性中具有歧視性值,而且升級至 EF 9.0 之後,對這些文件的查詢將會失敗。
原因為何
由於 JSON id
屬性必須是唯一的,所以先前已將歧視性加入其中,以允許具有相同索引鍵值的不同實體存在。 例如,這允許在Blog
相同的容器和分割區中包含值8的 Id
和 Post
屬性。 這更符合關係資料庫數據模型化模式,其中每個實體類型都會對應至自己的數據表,因此有自己的索引鍵空間。
EF 9.0 通常會變更對應,使其更符合常見的 Azure Cosmos DB NoSQL 做法和期望,而不是對應到來自關係資料庫的使用者期望。 此外,id
屬性中有鑑別子值,會使外部工具和系統更難與 EF 產生的 JSON 文件互動;這類外部系統通常不會察覺預設從 .NET 類型衍生的 EF 鑑別子值。
風險降低
最簡單的緩和措施是將 EF 設為包含 JSON id
屬性的鑑別子,就和以前一樣。 基於此目,我們引進新的組態選項:
modelBuilder.Entity<Session>().HasDiscriminatorInJsonId();
對所有最上層實體類型執行這項操作,會讓 EF 按照過去的行為來運作。
至此階段,如果您想要,也可以將所有文件更新為重寫 JSON id
屬性。 請注意,只有在不同類型的實體未在相同容器內共用相同識別碼值時,才能這麼做。
中度影響變更
透過 Azure Cosmos DB 提供者同步 I/O 的功能已不再支援
舊的行為
以前,呼叫 ToList
或 SaveChanges
這類同步方法會導致 EF Core 在對 Azure Cosmos DB SDK 執行非同步呼叫時,使用 .GetAwaiter().GetResult()
進行同步封鎖 。 這可能會導致產生鎖死。
新的行為
從EF Core 9.0 起,在嘗試使用同步 I/O 時,EF 預設會擲回結果。 例外狀況訊息為「Azure Cosmos DB 不支援同步 I/O。 使用 Entity Framework Core 存取 Azure Cosmos DB 時,請務必只使用並正確等候非同步方法。 如需詳細資訊,請參閱:https://aka.ms/ef-cosmos-nosync。」
原因為何
在非同步方法使用同步封鎖可能會導致鎖死,而 Azure Cosmos DB SDK 僅支援非同步方法。
風險降低
在 EF Core 9.0 中,您可以使用下列方式隱藏錯誤:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.ConfigureWarnings(w => w.Ignore(CosmosEventId.SyncNotSupported));
}
儘管如此,應用程式應該停止使用同步 API 來搭配 Azure Cosmos DB,因為 Azure Cosmos DB SDK 不支援此操作。 未來的 EF Core 版本會移除隱藏例外狀況的功能,之後唯一的選項就是使用非同步 API。
SQL 查詢現在起必須直接投影 JSON 值
舊的行為
以前,EF 產生的查詢如下:
SELECT c["City"] FROM root c
這類查詢會導致 Azure Cosmos DB 將每個結果包裝在 JSON 物件中,如下所示:
[
{
"City": "Berlin"
},
{
"City": "México D.F."
}
]
新的行為
從 EF Core 9.0 開始,EF 會將 VALUE
修飾詞新增至查詢,如下所示:
SELECT VALUE c["City"] FROM root c
這類查詢會導致 Azure Cosmos DB 直接傳回值,而不會包裝:
[
"Berlin",
"México D.F."
]
如果您的應用程式使用 SQL 查詢,在升級至 EF 9.0 之後,這類查詢可能會中斷,因為它們不包含 VALUE
修飾詞。
原因為何
將每個結果包裝在額外的 JSON 物件可能會導致某些案例的效能降低、JSON 結果承載膨脹,而且不是使用 Azure Cosmos DB 的自然方式。
風險降低
若要減輕問題,只要將 VALUE
修飾詞新增至 SQL 查詢的投影,如上所示。
未定義的結果現在會自動從查詢結果中篩選
舊的行為
以前,EF 產生的查詢如下:
SELECT c["City"] FROM root c
這類查詢會導致 Azure Cosmos DB 將每個結果包裝在 JSON 物件中,如下所示:
[
{
"City": "Berlin"
},
{
"City": "México D.F."
}
]
如果任何結果未經定義 (亦即文件中不存在 City
屬性),則會傳回空白文件,而 EF 會針對該結果傳回 null
。
新的行為
從 EF Core 9.0 開始,EF 會將 VALUE
修飾詞新增至查詢,如下所示:
SELECT VALUE c["City"] FROM root c
這類查詢會導致 Azure Cosmos DB 直接傳回值,而不會包裝:
[
"Berlin",
"México D.F."
]
Azure Cosmos DB 行為是自動篩選 undefined
結果中的值;這表示如果檔中沒有其中 City
一個屬性,查詢只會傳回單一結果,而不是兩個結果,其中一個是 null
。
原因為何
將每個結果包裝在額外的 JSON 物件可能會導致某些案例的效能降低、JSON 結果承載膨脹,而且不是使用 Azure Cosmos DB 的自然方式。
風險降低
如果您的應用程式必須針對未定義的結果取得 null
值,請使用新的 EF.Functions.Coalesce
運算子將 undefined
值和 null
聯合起來:
var users = await context.Customer
.Select(c => EF.Functions.CoalesceUndefined(c.City, null))
.ToListAsync();
未正確轉譯的查詢已不會再經過轉譯
舊的行為
以前,EF 轉譯的查詢如下:
var sessions = await context.Sessions
.Take(5)
.Where(s => s.Name.StartsWith("f"))
.ToListAsync();
不過,此查詢的 SQL 轉譯並不正確:
SELECT c
FROM root c
WHERE ((c["Discriminator"] = "Session") AND STARTSWITH(c["Name"], "f"))
OFFSET 0 LIMIT @__p_0
在 SQL 中,WHERE
子句會在 OFFSET
和 LIMIT
子句之前接受評估,但在上述 LINQ 查詢中,Take
運算子會出現在 Where
運算子之前。 因此,這類查詢可能傳回不正確的結果。
新的行為
從 EF Core 9.0 開始,這類查詢不會再轉譯,且系統會擲回例外狀況。
原因為何
不正確的轉譯可能會導致無訊息的資料損毀,因而在應用程式造成難以探索的錯誤。 EF 一律會偏好藉由預先擲回來快速檢錯,而不是選擇可能造成資料損毀的方法。
風險降低
如果您偏好之前的行為,且想要執行相同的 SQL,只要交換 LINQ 運算子的順序即可:
var sessions = await context.Sessions
.Where(s => s.Name.StartsWith("f"))
.Take(5)
.ToListAsync();
不幸的是,Azure Cosmos DB 目前 OFFSET
不支援 SQL 子查詢中的 和 LIMIT
子句,這是原始 LINQ 查詢所需的適當轉譯。
低度影響變更
HasIndex
現在會擲回結果,而不是遭忽略
舊的行為
以前,EF Cosmos DB 提供者會忽略 HasIndex 的呼叫。
新的行為
如已指定 HasIndex,現在提供者會擲回結果。
原因為何
在 Azure Cosmos DB 中,所有屬性預設都會編製索引,而且不需要指定任何索引。 雖然您可以定義自訂索引原則,然而 EF 目前不予支援,但您可以透過 Azure 入口網站來執行,不需要 EF 支援。 由於 HasIndex 呼叫不會執行任何行為,因此系統不再允許此動作。
風險降低
移除任何 HasIndex 的呼叫。
IncludeRootDiscriminatorInJsonId
在 9.0.0-rc.2 之後已重新命名為 HasRootDiscriminatorInJsonId
舊的行為
API IncludeRootDiscriminatorInJsonId
是在 9.0.0 rc.1 中引進的。
新的行為
在 EF Core 9.0 的最終版本中,API 已重新命名為 HasRootDiscriminatorInJsonId
原因為何
另一個相關的 API 經過重新命名,開頭改為以 Has
取代 Include
,它的重新命名也是基於一致性。
風險降低
如果您的程式碼使用 IncludeRootDiscriminatorInJsonId
API,只要將它變更為參照 HasRootDiscriminatorInJsonId
即可。