Redis OM .NET Redis物件對映框架

MASA技術團隊發表於2022-01-07

Redis OM

Redis OM 是 Redis 官方推出的物件對映框架,即:Object Mapping。讓開發人員更簡單、方便的操作 Redis 資料。Redis 儲存的資料抽象為物件對映,支援基於物件的 Redis 資料持久化、流式查詢操作。

目前只支援 4 種開發語言:

  • Redis OM for Spring
  • Redis OM for .NET
  • Redis OM for Node.js
  • Redis OM for Python

Redis OM .NET

Redis OM .NET 是.Net 平臺的 Redis OM,依賴 StackExchange.Redis 實現。藉助 Redis OM .NET 可以實現物件操作的方式操作 Redis 資料,脫離 key/value 的操作方式。
查詢支援大部分.Neter 最愛的 LINQ。

快速開始

安裝對應包

dotnet add package Redis.OM

Redis 環境準備

直接使用 Docker 的方式安裝 Redis 環境。

docker run -p 6379:6379 redislabs/redismod:preview

標準的官方映象是無法支援 Redis OM,需要 Redis Modules 支援,Redis OM 核心建立索引、查詢資料依賴RediSearch這個 Module 實現。依賴的 Module 有:RediSearch、RedisJSON。
RedisJSON 的依賴不是必須的,但是會缺少相應的功能,如:模型巢狀、複雜查詢(只支援 key 查詢)

Coding

增加抽象物件定義

[Document]
public class Customer
{
    [RedisIdField]
    public string Id { get; set; }
    [Indexed(Sortable = true, Aggregatable = true)]
    public string FirstName { get; set; }
    [Indexed(Sortable = true, Aggregatable = true)]
    public string LastName { get; set; }
    [Indexed]
    public string Email { get; set; }
    [Indexed(Sortable = true)]
    public int Age { get; set; }
}

Document、Indexed、Searchable 等特性介紹,介紹參考 Github -> document-attribute

獲取抽象物件的操作集合、建立索引

var provider = new RedisConnectionProvider("redis://localhost:6377");
var connection = provider.Connection;
var customers = provider.RedisCollection<Customer>();
connection.CreateIndex(typeof(Customer));

查詢資料、聚合操作等需要依據索引,所以一定要先呼叫connection.CreateIndex建立索引,對應 RediSearch 的FT.CREATE命令。

connection.CreateIndex(typeof(Customer)) 建立索引重複執行會丟擲異常Index already exists。雖然可以通過connection.Execute("FT.INFO", $"customer-idx")獲取索引資訊,但是第一次索引不存在時會丟擲Unknown Index name。所以實際使用中可能需要一個 try-catch 包住CreateIndex方法避免異常。

插入資料

var id = customers.Insert(new Customer { FirstName = "Steve", Email = "xxxx@masa.com", Age = 1 });
var id2 = customers.Insert(new Customer { FirstName = "FirstName", LastName = "LastName", Email = "xxxx@masa.com" });

id,id2 為插入資料的 key,沒有指定 Document 的 Prefixes 和 IdGenerationStrategy,則預設為 ULID 格式為{DocumentName}:{Ulid},如:`Cust

插入資料時需要注意的是,如果對沒有明確指定欄位的值,如 LastName 不明確賦值:customers.Insert(new Customer { FirstName = "Steve", Email = "xxxx@masa.com", Age = 1 });,檢視 Redis 中存的資料可以發現當前 key 儲存的 json 資料沒有未指定的欄位對應的 key(此時 Query 或 Aggregations 會有些奇怪的錯誤)。可以根據自己需要,顯示的為欄位賦個零值或者在定義實體時使用public string LastName { get; set; } = string.Empty;的方式。

查詢資料

var customer = customers.Where(x => x.Age == 0).OrderBy(a => a.FirstName).FirstOrDefault();
var customerById = customers.FindById(id);//根據Id查詢
var emails = customers.Select(x => x.Email);//僅查詢指定欄位
var takes = customers.Where(x => x.Age > 0).Take(10);//獲取指定條數
var adults = customers.Where(x => x.Age >= 0).Skip(5);//查詢偏移

對於空值的判斷,x.FirstName == ""【語法錯誤】 或 string.IsNullOrEmpty(x.FirstName)【不支援】。Redis 雜湊中不能有空字串,所以類似的查詢應該通過聚合操作的Exists方法實現

foreach (var agg in customerAggregations.Apply(x => ApplyFunctions.Exists(x.RecordShell.LastName), "LastNameExists"))
{
    Console.WriteLine($"{agg["LastNameExists"]}");
}

聚合操作

流水線(Pipelining)同時傳送多個請求,從而減輕延遲。結果的查詢和轉化都在 Redis 端完成。

RecordShell 是遠端 Index 型別的結構,RecordShell 應該只在聚合操作流水線內部使用,執行時並沒有真正的值。

拼湊 FirstName 和 LastName,返回 FullName

var customerAggregations = provider.AggregationSet<Customer>();
var age = customerAggregations.Average(x => x.RecordShell.Age);
var sets = customerAggregations.Where(a => a.RecordShell.FirstName == "Steve").Apply(x => string.Format("{0} {1}", x.RecordShell.FirstName, x.RecordShell.LastName), "FullName");
foreach (var item in sets)
{
    Console.WriteLine(item["FullName"].ToString());
}

聚合分組

通過 GroupBy 方法,依據不同屬性進行分組聚合(支援單欄位分組和多欄位分組)。

var res = customerAggregations
               .GroupBy(x => x.RecordShell.FirstName)
               .GroupBy(x => x.RecordShell.LastName)
               .ToArray();

var res1 = customerAggregations.GroupBy(x => x.RecordShell.FirstName).CloseGroup().ToArray();

CloseGroup 可以關閉分組,轉換為正常的聚合操作,即 GroupedAggregationSet 到 RedisAggregationSet 的一個轉換。

public static RedisAggregationSet<T> CloseGroup<T>(this GroupedAggregationSet<T> source)
{
    return new RedisAggregationSet<T>(source, source.Expression);
}

結尾

本文只是對 Redis OM .NET 用法的簡單梳理和可用性驗證。
更多用法以及用法更新參考Github

我們正在行動,新的框架、新的生態

我們的目標是自由的易用的可塑性強的功能豐富的健壯的

所以我們借鑑Building blocks的設計理念,正在做一個新的框架MASA Framework,它有哪些特點呢?

  • 原生支援Dapr,且允許將Dapr替換成傳統通訊方式
  • 架構不限,單體應用、SOA、微服務都支援
  • 支援.Net原生框架,降低學習負擔,除特定領域必須引入的概念,堅持不造新輪子
  • 豐富的生態支援,除了框架以外還有元件庫、許可權中心、配置中心、故障排查中心、報警中心等一系列產品
  • 核心程式碼庫的單元測試覆蓋率90%+
  • 開源、免費、社群驅動
  • 還有什麼?我們在等你,一起來討論

經過幾個月的生產專案實踐,已完成POC,目前正在把之前的積累重構到新的開源專案中

目前原始碼已開始同步到Github(文件站點在規劃中,會慢慢完善起來):

MASA.BuildingBlocks

MASA.Contrib

MASA.Utils

MASA.EShop

BlazorComponent

MASA.Blazor

QQ群:7424099

微信群:加技術運營微信(MasaStackTechOps),備註來意,邀請進群

masa_stack_tech_ops.png

​ ------ END ------

作者簡介

馬躍:MASA技術團隊成員。

相關文章