RedisOM for .NET

了解如何使用 Redis Stack 和 .NET 构建

Redis OM .NET 是一个专门为处理 Redis Stack 中的文档而设计的库。在本教程中,我们将构建一个简单的 ASP.NET Core Web-API 应用程序,用于对简单的 Person 和 Address 模型执行 CRUD 操作,并且我们将使用 Redis OM .NET 完成所有这些操作。

先决条件

  • .NET 6 SDK
  • 任何用于编写 .NET 的 IDE(Visual Studio、Rider、Visual Studio Code)。
  • RediSearch 必须作为 Redis Stack 配置的一部分安装。
  • 可选:Docker Desktop 用于在 docker 中运行 redis-stack 以进行本地测试。

跳至代码

如果您想跳过本教程并直接跳至代码,所有源代码都可以在 GitHub 中找到。

运行 Redis Stack

有许多方法可以运行 Redis Stack。一种方法是使用 docker 镜像

docker run -d -p 6379:6379 -p 8001:8001 redis/redis-stack

创建项目

要创建项目,只需运行

dotnet new webapi -n Redis.OM.Skeleton --no-https --kestrelHttpPort 5000

然后在您选择的 IDE 中打开 Redis.OM.Skeleton.csproj 文件。

配置应用程序

在您的 appsettings.json 文件中添加一个 REDIS_CONNECTION_STRING 字段以配置应用程序。将该连接字符串设置为 Redis 实例的 URI。如果使用前面提到的 docker 命令,您的连接字符串将是 redis://localhost:6379

连接字符串规范

Redis URI 的规范位于 此处。您可以使用 :password@host:portdefault:password@host:port 用于不包含 username 的连接字符串。

创建模型

确保将 Redis.OM 包添加到您的项目中。此包使创建模型和查询 Redis 域对象变得容易。

dotnet add package Redis.OM     

现在该创建应用程序将用于存储/检索人员的Person/Address模型了。创建一个名为Model的新目录,并将文件Address.csPerson.cs添加到其中。在Address.cs中,添加以下内容

using Redis.OM.Modeling;

namespace Redis.OM.Skeleton.Model;

public class Address
{
    [Indexed]
    public int? StreetNumber { get; set; }
    
    [Indexed]
    public string? Unit { get; set; }
    
    [Searchable]
    public string? StreetName { get; set; }
    
    [Indexed]
    public string? City { get; set; }
    
    [Indexed]
    public string? State { get; set; }
    
    [Indexed]
    public string? PostalCode { get; set; }
    
    [Indexed]
    public string? Country { get; set; }
    
    [Indexed]
    public GeoLoc Location { get; set; }
}

在这里,你会注意到除了标记为SearchableStreetName之外,所有字段都用Indexed属性装饰。这些属性(SearchableIndexed)告诉Redis OM 你希望能够在 Redis Stack 中查询文档时在查询中使用这些字段。Address本身不会成为一个文档,因此顶级类不会用任何东西装饰;相反,Address模型将嵌入到我们的Person模型中。

为此,请将以下内容添加到Person.cs

using Redis.OM.Modeling;

namespace Redis.OM.Skeleton.Model;

[Document(StorageType = StorageType.Json, Prefixes = new []{"Person"})]
public class Person
{    
    [RedisIdField] [Indexed]public string? Id { get; set; }
    
    [Indexed] public string? FirstName { get; set; }

    [Indexed] public string? LastName { get; set; }
    
    [Indexed] public int Age { get; set; }
    
    [Searchable] public string? PersonalStatement { get; set; }
    
    [Indexed] public string[] Skills { get; set; } = Array.Empty<string>();    
    
    [Indexed(CascadeDepth = 1)] public Address? Address { get; set; }
    
}

这里有几点需要注意

  1. [Document(StorageType = StorageType.Json, Prefixes = new []{"Person"})] 表示 Redis OM 用于在 Redis 中存储文档的数据类型是 JSON,并且 Person 类键的前缀将是Person

  2. [Indexed(CascadeDepth = 1)] Address? Address { get; set; } 是使用 Redis OM 对嵌入对象进行索引的两种方法之一。这种方法指示索引级联到对象图中的对象,CascadeDepth 为 1 表示它只遍历一层,将对象索引为从头开始构建索引一样。另一种方法使用要搜索的单个索引字段的JsonPath属性。这种更精确的方法限制了索引的大小。

  3. Id 属性被标记为RedisIdField。这表示该字段将用于在文档存储到 Redis 中时生成文档的键名。

创建索引

构建模型后,下一步是在 Redis 中创建索引。管理此操作的最正确方法是将索引创建分解成一个托管服务,该服务将在应用程序启动时运行。创建一个HostedServices目录,并将IndexCreationService.cs添加到其中。在该文件中,添加以下内容,它将在启动时创建索引。

using Redis.OM.Skeleton.Model;

namespace Redis.OM.Skeleton.HostedServices;

public class IndexCreationService : IHostedService
{
    private readonly RedisConnectionProvider _provider;
    public IndexCreationService(RedisConnectionProvider provider)
    {
        _provider = provider;
    }
    
    public async Task StartAsync(CancellationToken cancellationToken)
    {
        await _provider.Connection.CreateIndexAsync(typeof(Person));
    }

    public Task StopAsync(CancellationToken cancellationToken)
    {
        return Task.CompletedTask;
    }
}

接下来,将以下内容添加到Program.cs中,以便在启动时注册服务

builder.Services.AddHostedService<IndexCreationService>();

注入 RedisConnectionProvider

Redis OM 使用RedisConnectionProvider类来处理与 Redis 的连接,并提供可用于与 Redis 交互的类。要使用它,只需将RedisConnectionProvider的实例注入到应用程序中即可。在你的Program.cs文件中,添加

builder.Services.AddSingleton(new RedisConnectionProvider(builder.Configuration["REDIS_CONNECTION_STRING"]));

这会将你的连接字符串从配置中拉取出来,并初始化提供程序。提供程序现在将在你的控制器/服务中可用以供使用。

创建 PeopleController

最后一块拼图是为我们的 People API 编写实际的 API 控制器。在controllers目录中,添加文件PeopleController.csPeopleController类的骨架将是

using Microsoft.AspNetCore.Mvc;
using Redis.OM.Searching;
using Redis.OM.Skeleton.Model;

namespace Redis.OM.Skeleton.Controllers;

[ApiController]
[Route("[controller]")]
public class PeopleController : ControllerBase
{

}

注入 RedisConnectionProvider

要与 Redis 交互,请注入RedisConnectionProvider。在此依赖注入期间,请提取一个RedisCollection<Person>实例,它将允许使用流畅的接口来查询 Redis 中的文档。

private readonly RedisCollection<Person> _people;
private readonly RedisConnectionProvider _provider;
public PeopleController(RedisConnectionProvider provider)
{
    _provider = provider;
    _people = (RedisCollection<Person>)provider.RedisCollection<Person>();
}

添加创建人员的路由

要添加到 API 的第一个路由是用于创建人员的 POST 请求,使用RedisCollection,这与调用InsertAsync并传入人员对象一样简单

[HttpPost]
public async Task<Person> AddPerson([FromBody] Person person)
{
    await _people.InsertAsync(person);
    return person;
}

添加按年龄筛选的路由

要添加到 API 的第一个筛选路由将允许用户按最小年龄和最大年龄进行筛选。使用RedisCollection提供的 LINQ 接口,这是一个简单的操作

[HttpGet("filterAge")]
public IList<Person> FilterByAge([FromQuery] int minAge, [FromQuery] int maxAge)
{        
    return _people.Where(x => x.Age >= minAge && x.Age <= maxAge).ToList();
}

按地理位置筛选

Redis OM 有一个GeoLoc数据结构,该结构的实例由Address模型索引,使用RedisCollection,可以使用GeoFilter方法以及要筛选的字段来查找具有特定位置半径的所有对象

[HttpGet("filterGeo")]
public IList<Person> FilterByGeo([FromQuery] double lon, [FromQuery] double lat, [FromQuery] double radius, [FromQuery] string unit)
{
    return _people.GeoFilter(x => x.Address!.Location, lon, lat, radius, Enum.Parse<GeoLocDistanceUnit>(unit)).ToList();
}

按精确字符串筛选

当模型中的字符串属性标记为Indexed时,例如FirstNameLastName,Redis OM 可以对它们执行精确文本匹配。例如,以下两个路由按PostalCode和姓名进行筛选,演示了精确字符串匹配。

[HttpGet("filterName")]
public IList<Person> FilterByName([FromQuery] string firstName, [FromQuery] string lastName)
{
    return _people.Where(x => x.FirstName == firstName && x.LastName == lastName).ToList();
}

[HttpGet("postalCode")]
public IList<Person> FilterByPostalCode([FromQuery] string postalCode)
{
    return _people.Where(x => x.Address!.PostalCode == postalCode).ToList();
}

当模型中的属性标记为Searchable时,例如StreetAddressPersonalStatement,可以执行全文搜索,请查看针对PersonalStatementStreetAddress的过滤器

[HttpGet("fullText")]
public IList<Person> FilterByPersonalStatement([FromQuery] string text){
    return _people.Where(x => x.PersonalStatement == text).ToList();
}

[HttpGet("streetName")]
public IList<Person> FilterByStreetName([FromQuery] string streetName)
{
    return _people.Where(x => x.Address!.StreetName == streetName).ToList();
}

按数组成员资格进行筛选

当字符串数组或列表标记为Indexed时,Redis OM 可以使用数组或列表的Contains方法筛选包含给定字符串的所有记录。例如,我们的Person模型有一个技能列表,你可以通过添加以下路由来查询它。

[HttpGet("skill")]
public IList<Person> FilterBySkill([FromQuery] string skill)
{
    return _people.Where(x => x.Skills.Contains(skill)).ToList();
}

更新人员

使用 Redis OM 更新 Redis Stack 中的文档可以通过首先实例化人员对象,进行所需更改,然后对集合调用Save来完成。集合负责跟踪对其实例化的实体所做的更新;因此,它将跟踪并应用你在其中所做的任何更新。例如,添加以下路由以更新给定 ID 的人员的年龄

[HttpPatch("updateAge/{id}")]
public IActionResult UpdateAge([FromRoute] string id, [FromBody] int newAge)
{
    foreach (var person in _people.Where(x => x.Id == id))
    {
        person.Age = newAge;
    }
    _people.Save();
    return Accepted();
}

删除人员

使用Unlink从 Redis 中删除文档。所需做的只是调用 Unlink 并传入键名。给定一个 ID,我们可以使用前缀和 ID 重新构建键名

[HttpDelete("{id}")]
public IActionResult DeletePerson([FromRoute] string id)
{
    _provider.Connection.Unlink($"Person:{id}");
    return NoContent();
}

运行应用程序

现在剩下要做的就是运行应用程序并对其进行测试。你可以通过运行dotnet run来完成此操作,应用程序现在在端口 5000 上公开,并且应该有一个 Swagger UI,你可以使用它在 http://localhost:5000/swagger 上与 API 进行交互。在GitHub 仓库中,有一些脚本以及一些数据文件,可用于使用 API 将一些人员插入到 Redis 中

使用 Redis Insight 查看数据

你可以安装 Redis Insight GUI 或使用在 http://localhost:8001/ 上运行的 Redis Insight GUI。

你可以按照以下步骤查看数据

  1. 接受 EULA

Accept EULA

  1. 单击“添加 Redis 数据库”按钮

Add Redis Database Button

  1. 输入 Redis 服务器的主机名和端口名。如果你使用的是 Docker 镜像,则为localhost6379,并为你的数据库提供别名

Configure Redis Insight Database

  1. 单击“添加 Redis 数据库”。

资源

本教程的源代码可在GitHub中找到。

RATE THIS PAGE
Back to top ↑