EF Core 6.0 Azure Cosmos DB Provider的新特性

微軟技術棧發表於2021-12-17

EF Core 6.0在今年的11月已經正式釋出了。團隊的成員們一直在努力新增並完善功能。其中一個重點領域是Azure Cosmos DB體驗。我們收到的反饋是,許多開發人員更願意使用Cosmos DB,但仍在等待某些關鍵功能。

行星文件

我在Blazor伺服器上建立了一個使用Azure Cosmos DB 和EF Core的擴充套件程式。它包括搜尋功能、交叉引用實體以及一個可以新建、讀取和更新的介面。我最近升級到最新的EF Core 6.0版本,並能夠簡化和刪除相當多的程式碼!

ae9f6503cd125a08da226dc24fafc4b5.png

功能概述

以下是我們在EF Core 6.0 Azure Cosmos DB provider中新增的一些特性。

隱式的所有權

EF Core被構建成一個物件關係對映器。在關聯式資料庫中,複雜的關係是通過將相關實體儲存在單獨的表中並使用外來鍵引用它們來表示的。EF Core假定在父類中遇到的非實體型別被表示為外來鍵關係。使用HasMany或HasOne配置關係,並且假設例項與配置的關係獨立存在。在文件資料庫中,實體型別的預設行為是假定它們是父類所擁有的嵌入式文件。換句話說,複雜型別的資料存在於父型別的上下文中。在早期版本的EF Core中,必須顯式地配置此行為,使其能夠與Azure Cosmos DB provider一起工作。在EF Core 6.0中,所有權是隱性的。這將儲存配置並確保行為與其他提供者的NoSQL方法一致。

例如,在行星文件中有作者和標籤。這些實體“擁有”一個指向URL和相關文件標題的摘要列表。這樣,當使用者問“什麼文件有標籤X”時,我只需要載入一個文件來回答這個問題(我載入標籤X,然後迭代它擁有的標題集合)。使用EF Core5,我必須明確宣告所有權:

tagModel.OwnsMany(t => t.Documents);
authorModel.OwnsMany(t => t.Documents);

在EF Core 6中,所有權是隱式的,所以除了指定分割槽鍵外,不需要配置實體。

支援原始集合

在關聯式資料庫中,原始集合的建模方法通常是將它們提升為複雜型別,或者將它們轉換為可序列化的東西以儲存在單個列中。比如有一篇部落格,它可以有很多標籤。一種常見的方法是建立一個代表標籤的實體:


public class Tag 
{
    public int Id { get; set; }
    public string Text { get; set; }
}

然後標籤類被引用:

public ICollection<Tag> Tags { get; set; }

接著原始型別被提升為複雜型別並儲存在一個單獨的表中。另一種方法是將標籤組合成一個欄位,該欄位包含一個以逗號分隔的列表。這種方法需要一個值轉換器將列表編組到欄位中以進行更新,並將欄位分解為列表以進行讀取。這也使得回答諸如“有多少貼子有X標籤?”這樣的問題變得困難。在使用EF Core 5時,我選擇了單列方法。我在寫入時將列表序列化為JSON,在讀取時將其反序列化。這是序列化程式碼:

private static string ToJson<T>(T item) => JsonSerializer.Serialize(item);
private static T FromJson<T>(string json) => JsonSerializer.Deserialize<T>(json);

我配置EF Core來進行轉換:

docModel.Property(d => d.Tags)

.HasConversion(
    t => ToJson(t),
    t => FromJson<List<string>>(t));

結果檔案看起來是這樣的:

{
    "tags" : "[\"one\", \"two\", \"three\"]"
}

在EF Core 6.0中,我只是刪除了程式碼然後利用原始型別的內建處理方式,結果是這樣的文件:


{
    "tags" : [ 
        "one",
        "two",
        "three"
    ]
}

這導致了schema發現改變,但是Azure Cosmos DB沒有問題處理。另一方面,當使用標籤作為陣列的當前模型遇到使用標籤作為欄位的舊記錄時,c#程式碼將丟擲異常。當EF Core沒有NoSQL遷移的概念時,我們如何處理這個問題?

Raw SQL

一個很常見的請求是允許開發人員為資料訪問編寫自己的SQL。這正是我處理程式碼遷移所需要的特性。要使Raw SQL工作,它必須投射到一個現有的模型。它是實體的DbSet<T>的擴充套件。在我的例子中,它支援就地遷移。在更新程式碼之後,嘗試載入文件將會失敗。文件只有一個字串屬性用於“tag”,但c#模型是一個陣列,因此JSON序列化會丟擲一個異常。為了解決這個問題,我使用了Azure Cosmos DB的一個內建特性,它將字符串解析為陣列。使用查詢,我將實體投影到匹配當前schema的文件中,然後將其儲存回來。這是遷移程式碼:

var docs = await Documents.FromSqlRaw(
    "select c.id, c.Uid, c.AuthorAlias, c.Description, c.Html, c.Markdown, c.PublishDate, c.Title, STRINGTOARRAY(c.Tags) as Tags from c").ToListAsync();
foreach (var doc in docs)
{
    Entry(doc).State = EntityState.Modified;
}

這個特性使開發人員能夠建立LINQ provider可能不支援的複雜查詢。

其他增強功能

除了我已經介紹過的內容之外,這些增強功能也包含在其中。

總結

我對即將到來的變化感到興奮,希望你們也一樣。你正在使用Cosmos DB provider嗎? 如果還沒有,在我們新增了以上功能之後你願意使用嗎? 或者還有更多需求和建議,請在本文下面留言,謝謝!


歡迎關注微軟中國MSDN訂閱號,獲取更多最新發布!
image.png

相關文章