Redis OM .NET是Redis官方针对.NET平台推出的对象映射库,它让开发人员能够以强类型方式操作Redis数据,而不必陷入手工序列化、键名拼接和索引维护等重复劳动。与Entity Framework Core类似,Redis OM .NET通过声明式模型和自动索引同步,将Redis的底层命令封装成直观的对象操作和可读的LINQ查询,特别适合需要快速接入Redis Stack的现代.NET应用。

该库的核心价值在于减少样板代码和降低出错概率。传统方案中,开发者需要自行选择存储格式(如JSON字符串或哈希表),并编写序列化逻辑;每次新增查询维度还要手动执行FT.CREATE命令维护索引。Redis OM .NET将这些细节全部抽象掉,只需在类属性上标注特性,即可自动完成映射和索引创建。接下来我们从安装开始,逐步深入具体用法。
安装与连接配置
首先确保运行环境已安装Redis Stack或Redis Enterprise,因为Redis OM .NET依赖RediSearch和RedisJSON两个核心模块。普通开源Redis服务器(不带模块)无法使用该库的高级查询和聚合功能。如果使用本地开发环境,推荐直接通过Docker运行redis/redis-stack镜像,或使用Redis Cloud提供的免费实例。
在.NET项目中添加NuGet包非常简单,执行以下命令即可:
dotnet add package Redis.OM
安装完成后,需要在代码中创建RedisConnectionProvider实例。该对象负责管理底层连接池和集合操作入口。一个典型的最小配置如下:
using Redis.OM;
var provider = new RedisConnectionProvider("redis://localhost:6379");
var connection = provider.Connection; // 获取StackExchange.Redis连接复用
var persons = provider.RedisCollection<Person>();
连接字符串格式与StackExchange.Redis完全兼容,支持密码、数据库编号、SSL等参数。例如连接带密码的远程实例可以写成redis://:yourpassword@yourhost:6379。RedisConnectionProvider内部维护了一个多路复用连接,建议在应用生命周期内以单例模式注册,避免频繁创建带来开销。在ASP.NET Core中,可以通过依赖注入容器注册为Singleton服务,并在启动时调用索引初始化逻辑。
定义实体模型与创建索引
Redis OM .NET通过特性来标记实体类和属性。一个类若希望存储为JSON文档,需要使用[Document]特性,默认存储格式就是JSON;主键字段用[RedisIdProperty]标注,其余需要参与查询的属性可以添加[Indexed]或[Searchable]特性。Indexed适用于精确匹配或范围查询,如数字、日期和枚举;Searchable适用于全文搜索,如长文本字段。
下面定义一个Person实体,包含Id、Name、Age、Email和City属性:
using Redis.OM.Modeling;
[Document(StorageType = StorageType.Json)]
public class Person
{
[RedisIdProperty]
public string Id { get; set; }
[Indexed]
public string Name { get; set; }
[Indexed]
public int Age { get; set; }
[Searchable]
public string Email { get; set; }
[Indexed]
public string City { get; set; }
}
定义好实体后,需要通过连接对象创建对应的RediSearch索引。Redis OM会将实体特性的元数据翻译为FT.CREATE命令,并自动处理字段类型和权重。创建索引的操作是幂等的,重复执行不会产生错误。调用方式如下:
connection.CreateIndex(typeof(Person));
索引创建完成后,才能对相关字段执行条件查询。如果跳过该步骤直接使用Where筛选,会抛出异常提示索引不存在。值得注意的是,Redis OM默认使用JSON路径存储属性,因此底层文档结构为JSON对象,而非哈希表。这种方式支持嵌套对象和数组,灵活性更高,但存储空间略大于哈希。如果偏好哈希存储,可以将StorageType设置为Hash,但需要注意部分复杂类型可能不被支持。
基本CRUD操作示例
获取集合操作对象可以使用provider.RedisCollection<Person>(),也可以直接从connection获取connection.GetCollection<Person>()。RedisCollection封装了增删改查、异步方法和条件查询,其接口设计参考了常见ORM,上手成本很低。插入操作使用Insert或InsertAsync,更新使用Update或UpdateAsync,删除使用Delete或DeleteAsync,按Id获取使用Get或GetAsync。
以下代码演示了一套完整的CRUD流程:
var person = new Person
{
Name = "张三",
Age = 28,
Email = "zhangsan@ipipp.com",
City = "北京"
};
// 插入,自动生成ULID作为Id
var id = await persons.InsertAsync(person);
// 按Id获取
var loaded = await persons.GetAsync(id);
// 更新属性后保存
loaded.Age = 29;
await persons.UpdateAsync(loaded);
// 删除
await persons.DeleteAsync(loaded);
插入时如果不手动指定Id,Redis OM会生成一个ULID字符串,既保证全局唯一,又带有时间排序特性。也可以自定义Id值,例如使用业务主键。批量插入则可以使用InsertAsync的IEnumerable<Person>重载,内部会通过管道批量发送命令,提升吞吐量。对于需要原子性要求的场景,Redis OM支持通过连接发起事务或Lua脚本,但常规单条操作已经足够应对大多数业务。
高级查询与聚合
Redis OM .NET最大的亮点是将LINQ查询翻译为RediSearch的查询语法。开发者可以编写熟悉的Where、OrderBy、Select、Skip和Take等表达式,库会在运行时动态解析表达式树,生成对应的FT.SEARCH命令。这使得复杂条件组合、排序和分页变得非常直观,无需手动拼接查询字符串。
下面的示例查询年龄大于30且姓名包含“张”的记录,按年龄降序排列并分页获取前10条:
var results = await persons
.Where(p => p.Age > 30 && p.Name.Contains("张"))
.OrderByDescending(p => p.Age)
.Take(10)
.ToListAsync();
这里Where中的&&在字符串中实际是逻辑与,需要注意在C#代码中写为&&。如果使用变量构建动态查询,Redis OM也支持通过Expression手动构造,但可读性会下降,建议优先使用静态LINQ表达式。
聚合功能同样强大。通过GroupBy和聚合方法,可以统计各年龄段的用户数量。例如:
var ageGroups = await persons
.GroupBy(p => p.Age)
.Select(g => new { Age = g.Key, Count = g.Count() })
.ToListAsync();
底层会翻译为FT.AGGREGATE命令,将聚合计算下推到Redis服务器,避免将大量数据拉回客户端处理。需要注意的是,聚合查询同样依赖于索引,且聚合字段必须事先声明为Indexed或Searchable。在设计实体模型时,应权衡索引数量和写入性能:每个索引字段都会增加写入时的索引更新时间,因此不建议将所有属性都标记为索引,只保留实际查询条件会用到的字段。
Redis OM .NET还支持嵌套对象查询和地理空间索引等高级特性,例如在公司实体中按地理位置搜索附近门店。这些功能依赖RedisJSON和RediSearch的完整能力,适合构建地理位置服务、实时排行榜和全文检索等场景。合理使用Redis OM .NET,可以显著减少数据访问层的代码量,同时保留Redis的高性能和低延迟优势。