4

EF Core 2.x 升級 3.x 問題兩則

 2 years ago
source link: https://blog.darkthread.net/blog/efcore-2-upgrade-3-issues/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

EF Core 2.x 升級 3.x 問題兩則

2022-08-14 10:37 PM 0 176

我的部落格這兩天悄悄從 .NET 5 升級到 .NET 6。

過程遇到 EF Core 2.2 升 6.0 產生的奇怪問題,原本以為要搞很久,但修掉兩個 Issue 後意外沒再發現其他問題,簡單測試主要功能都正常,我就當改好了直接上線全民公測,大家若有發現問題請再回報唄。

【本文開始】

2020 年底部落格從 ASP.NET Core 3.1 升到 5.0,.NET 6 在去年 11 月推出,拖了大半年,眼看 .NET 7 都快出來了,總算抽出時間把部落格平台再從 .NET 5 升級到 .NET 6。(明明是 .NET 5 升 6,這文章標題怎麼是 EF Core 2 升 3?就繼續看下去吧)

心想 .NET 5 到 6 改動不大,預期不需要改太多程式,不料升級之後,EF Core 部分直接爆炸,問題出在我的 EF Core 相關程式庫還在 2.2 版,用 .NET 5 可以跑,升到 .NET 6,一執行 ctx.Database.GetDbConnection() 便冒出錯誤,完全沒法連資料庫:

Exception thrown: 'System.TypeInitializationException' in Microsoft.EntityFrameworkCore.dll
The type initializer for 'Microsoft.EntityFrameworkCore.Query.ResultOperators.Internal.TrackingExpressionNode' threw an exception.
TypeInitializationException: The type initializer for 'Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions' threw an exception.
InvalidOperationException: Sequence contains more than one matching element

簡單,把 EF Core 2.2 升到 6.0 不就好了?於是我將幾個 EF Core 相關的 2.2.2 版程式庫升到 6.0.8:

Fig1_637960847600198546.png

升級後,資料庫可以連線了,但變成以下這段程式出錯:

ctx.Posts.FirstOrDefault(p => 
    p.Slug.Equals(slug, StringComparison.OrdinalIgnoreCase));

錯誤訊息指出 EF Core 6 無法將 string.Equals(value, StringComparison.OrdinalIgnoreCase) 轉譯成對映的 SQL 比對語法。媽呀,該不會有一堆不相容吧?

System.InvalidOperationException: 'The LINQ expression 'DbSet<Post>()
    .Where(p => p.Slug.Equals(
        value: __slug_0, 
        comparisonType: OrdinalIgnoreCase))' could not be translated. 
        Additional information: Translation of the 'string.Equals' overload with a 'StringComparison' parameter is not supported. 
        See https://go.microsoft.com/fwlink/?linkid=2129535 for more information. 
        Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly 
        by inserting a call to 'AsEnumerable', 'AsAsyncEnumerable', 'ToList', or 'ToListAsync'. 
        See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.'

本到官方文件,由於比對是否區分大小寫實務上是由 DB Collation 決定,更何況 string.Equals() 還有國別區域相關參數,C# 很難依據 DB 設定對映出正確邏輯;另一方面,若要強轉 Collation 再比對將導致無法使用 Index 加速,故新版 EF Core 只接受 Equals 轉成 SQL =。更進一步發現,這是 2.2 轉 3.1+ 就會遇到的差異,我撐到今天才遇到,噗。

SQLite 資料庫預設不分大小寫,故這段改成 p.Slug == slug 就算過關。

接著著冒出更離奇的錯誤。

post.Comments = ctx.Comments.Where(o => o.PostKey == post.PostKey)
    .OrderByDescending(o => o.PubDate).ToList();

冒出 Microsoft.Data.Sqlite.SqliteException: 'SQLite Error 1: 'no such column: c.PostKey1'.' 錯誤,不知為何 EF Core 在轉譯這段成 SQL 語法時會出現 c.PostKey 及 c.PostKey1 兩個欄位,後者並非 Comment 包含的欄位,是 EF Core 產生出來的。

爬文查到相似案例,一樣是 2.2 升 3.1 就該遇到的問題。推敲關鍵在於我的 Post 定義了 IList<Comment> Comments 屬性,Comment 有個 PostKey 欄位指向 Post 的 PostKey,但我沒明確定義 Post 與 Comment 的 Primary Key / Foreign Key 對映,EF Core 試著自己產生時出了差錯。參考 Stackoverflow 討論,在 DbContext 的 OnModelCreating 加入以下這段,問題解決。

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    // ... 略 ...
    modelBuilder.Entity<Post>().HasMany(p => p.Comments).WithOne().HasPrincipalKey("PostKey").HasForeignKey("PostKey");
    // ... 略 ...       
}

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK