on
[DotNet] MartenDB介紹01
最近在看紀錄log的資料,偶然看到MartenDB
這個把postgres魔改成document database的的套件,蠻有趣的就來寫一下介紹。MartenDB希望可以把Postgres對於Jsonb的支援發揮到極致,官方文件有兩個部份,第一個部份是介紹如何透過MartenDB來把postgres當成mongoDB用,第二個部份是如何進一步用這個框架實做Event Sourcing,通常第二部份我應該會斷更哈哈....
文件資料庫與PostgresSQL
在系統開發中,關聯式資料庫通常被視為資料儲存首選方案。這種做法使得大多數開發者從資料表結構設計開始,並將整個系統設計納入關聯式資料庫的特性限制中。然而,使用文件式資料庫具有許多優點,例如不受資料表結構限制的資料模型設計。尤其在開發初期無法確定商業邏輯的情況下,設計資料庫模式變得困難,往往發生在後期發現規劃與實際需求不符時,無法輕易修改資料表結構而不得不容忍錯誤的情況。因此,許多新創公司開始採用像是 MongoDB 這樣的文件資料庫來進行開發。隨著此類需求的增加,RDBMS陣營也開始提供對 JSON 格式的支援,例如 PostgresSQL 提供了 JSONB 類型,並進一步加強了對 JSON 資料的查詢支援。
MartenDB簡介
MartenDB 是一個針對 PostgresSQL 進行改進的框架,希望能夠充分利用 PostgresSQL 對於 JSONB 的支援。大多數開發者和DBA對於 JSONB 的操作可能不太熟悉,Marten 希望能夠讓Jsonb的操作能夠簡單易用,有興趣可以看一下這個影片。就算不選擇使用 MartenDB,仍然很推薦讀一下官方的文件,裡面包含了關於 PostgresSQL 的 JSONB 的小技巧,例如如何針對鍵值進行索引等等,都十分有幫助。
MartenDB基本操作
-
基本設定
先安裝marten的nuget套件,如果使用微軟提供的相依性注入的話請要加入設定services.AddMarten(opts => { // 放入Postgre的連線字串 opts.Connection(connectionString); // 可以放入自訂的logger opts.Logger(); }) // 這個設定會依照環境不同 // 開發環境下自動生成需要的資料表 // production環境一律不新增 .OptimizeArtifactWorkflow() // 通常情況下可以開啟這個設定 .UseLightweightSessions();
如果不使用相依性注入的話只要簡短一行
var store = DocumentStore.For(connectionString);
-
DocumentStore
類似於EFCore中的DbContext
一樣,Marten中有一個DocumentStore
物件控管資料庫連線IDocumentStore
:
生命週期為Singleton
,用來管理連線、操作設定、資料遷移等等IDocumentSession
:
生命週期為Scoped
,用來查詢與寫入,預設情況下會使用identity map來快取每次查詢的資料,前面提到的UseLightweightSessions就是用來取消這樣的行為,因為通常情況下一次Request內不太會需要重複查詢,而快取會在Request結束後隨著物件一起釋放。IQuerySession
:
針對只讀的情況下建議使用。
-
其他設定
Marten還提供一些其他的設定,這邊只列處幾點- 預設使用JSON.NET進行序列化,可以改用System.Text或者自訂
- 可以設定重試策略
- 可以對欄位做設定,但對於資料表別名自訂有限,所有的資料表都要加上mt_前綴( 依照這個issue作者看起來完全不打算開放)
- 多資料庫連線(應該是搭配Event Source功能)
-
範例
以下只是稍微改了一下文件上的範例,移植到WebAPI專案上[ApiController] [Route("[controller]")] public class UserController : ControllerBase { [HttpPost] public async Task<Guid> Post( [FromBody] CreateUserRequest request, // 因為要寫入所以使用IDocumentSession [FromServices] IDocumentSession session) { var user = new User { FirstName = request.FirstName, LastName = request.LastName, Internal = request.Internal }; session.Insert(user); await session.SaveChangesAsync(); // 注意一定要有Id // 使用上可以用階層物件的方式 // 將Id以外的資料用record的方式做成value object return user.Id; } [HttpGet("/{id:guid}")] public async Task<User?> Get( [FromRoute] Guid id, // 因為是只讀所以可以使用IQuerySession [FromServices] IQuerySession session) => await session.LoadAsync<User>(id); // 也支援Linq查詢 // await session.LoadAsync<User>().FirstOrDefaultAsync(x => x.Id == id); }
還有一些進一步的用法像是upsert或批次操作,這邊就不特別秀出來,接下來我們看一下它生成的表
table_name: mt_doc_user
id data mt_last_modified mt_version mt_dotnet_type 0188d468-8526-4c4e-ac0a-d142363918ab {"Id": "0188d468-8526-4c4e-ac0a-d142363918ab", "Internal": true, "LastName": "string", "FirstName": "string"} 2023-06-19 16:06:39.785208 +00:00 0188d468-8528-4889-8a47-bdc600e5e039 User 自動生成的資料表叫做
mt_doc_user
,可以看到User完全被序列化記入data,並且Id被當成資料的Id,文件上也有說明針對需要容易查找的欄位也可以拉成獨立的column,還有下index的技巧,整個使用上還算簡單。
小結
總體而言martenDB還算容易使用,然後僅僅只能使用於PostgresSQL。支援自行定義與db的綁定關係,有點類似EFCore的code-first,也有針對資料庫併發的一些設定,畢竟主力還是把它作為低成本的event-store使用,希望下一篇不要拖太久。