# Redis OM .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)
  • 可选:Docker Desktop 用于在 docker 中运行 redis-stack 以进行本地测试。

# 跳到代码

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

# 运行 Redis 堆栈

有多种方法可以运行 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

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

# 配置应用程序

添加一个"REDIS_CONNECTION_STRING" field to yourappsettings.json file to configure the application. Set that connection string to be the URI of your Redis instance. If using the docker command mentioned earlier, your connection string will beredis://localhost:6379`。

# 创建模型

现在是时候创建应用程序将用于存储/检索人员的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; }
}

在这里,您会注意到除了StreetName标记为之外的所有字段都使用该属性Searchable进行修饰。Indexed这些属性 (SearchableIndexed) 告诉 Redis OM,当您在 Redis Stack 中查询文档时,您希望能够在查询中使用这些字段。Address不会是 Document 本身,所以顶级类没有装饰任何东西;相反,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)] 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;
    }
}

# 注入 RedisConnectionProvider

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

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

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

# 创建人员控制器

最后一个难题是为我们的 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 的第一个过滤器路由将让用户按最小和最大年龄进行过滤。使用 LINQ 可用的接口RedisCollection,这是一个简单的操作:

[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、likeStreetAddressPersonalStatement时,您可以执行全文搜索,请查看 and 的过滤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 中的文档可以通过首先实现 person 对象,进行所需的更改,然后调用Save集合来完成。该集合负责跟踪对其中实现的实体所做的更新;因此,它将跟踪并应用您在其中所做的任何更新。例如,添加以下路由来更新给定 Id 的 Person 的年龄:

[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();
}

# 删除一个人

从 Redis 中删除文档可以使用Unlink. 所需要的只是调用 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

接受 EULA

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

添加 Redis 数据库按钮

  1. 输入你的 redis 服务器的主机名和端口名。如果您使用的是 docker 映像,这是localhost并且6379给您的数据库一个别名

配置 Redis Insight 数据库

  1. 点击Add Redis Database.

# 资源

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

Last Updated: 4/18/2023, 8:45:33 AM