.NET?6开发TodoList应用之使用MediatR实现POST请求 |
需求需求很简单:如何创建新的 初学者按照教程去实现的话,应该分成以下几步:创建 对于稍微正式一些的项目,.NET工程上习惯的实现是通过使用一些比较成熟的类库框架,有效地对业务逻辑进行分类管理、消除冗余代码,以达到业务逻辑职责清晰简洁的目的 。在这个阶段我们经常使用的两个类库分别是AutoMapper和MediatR,本文结合 目标合理组织并使用MediatR,完成 原理与思路首先来简单地介绍一下这个类库 。 关于CQRS模式、中介者模式和MediatR CQRS模式CQRS模式全称是“Command Query Responsibility Segregation”,正如字面意思,CQRS模式的目的在于将读取操作和写入操作的指责区分开,并使用不同的Model去表示 。从CRUD的角度来说,就是把
这个模式可以有效地应用到具有主从分离的数据库架构中,当需要获取数据时,从只读数据库(一般是从库)中读取数据,当需要写入或更新数据时,向主库进行操作 。 CQRS模式旨在解决的问题是:为了屏蔽数据库层面“写优先”还是“读优先”的优化设计策略,在业务逻辑侧进行解耦 。 任何设计模式都是对解决特定问题的一个Trade off,自然也带来了一些缺点,首先就是服务内部的组件复杂度上升了,因为需要创建额外的类来实现CQRS模式;其次如果数据层是分离的,那么可能会有数据的状态不一致问题 。 中介者Mediator模式这是23种基本设计模式中的一个,属于行为型设计模式,它给出了组件之间交互的一种解耦的方式 。简单参考下图,具体内容就不过多解释了,任何一篇介绍设计模式的文章都有介绍 。
这种设计模式实际上是一种采用依赖倒置(Inversion of Control, IoC)的方式,实现了图中蓝色组件的松耦合 。 MediatR这是在开发中被广泛采用的实现以上两种设计模式的类库,更准确的说法是,它通过应用中介者模式,实现了进程内CQRS 。基本思想是所有来自API接口和数据存储之间的逻辑,都需要通过MediatR来组织(即所谓的“中介者”) 。 从实现上看,MediatR提供了几组用于不同场景的接口,我们在本文中处理的比较多的是 实现所有需要使用MediatR的地方都集中在Application项目中 。 引入MediatR$ dotnet add src/TodoList.Application/TodoList.Application.csproj package MediatR.Extensions.Microsoft.DependencyInjection 为了适配CQRS的模式,我们在Application项目中的TodoLists和TodoItems下相同地创建几个文件夹:
在
using System.Reflection;
using Microsoft.Extensions.DependencyInjection;
namespace TodoList.Application;
public static class DependencyInjection
{
public static IServiceCollection AddApplication(this IServiceCollection services)
{
services.AddMediatR(Assembly.GetExecutingAssembly());
return services;
}
}
并在Api项目中使用: // 省略其他... // 添加应用层配置 builder.Services.AddApplication(); // 添加基础设施配置 builder.Services.AddInfrastructure(builder.Configuration); 实现Post请求在本章中我们只实现 POST TodoList 在
using MediatR;
using TodoList.Application.Common.Interfaces;
namespace TodoList.Application.TodoLists.Commands.CreateTodoList;
public class CreateTodoListCommand : IRequest<Guid>
{
public string? Title { get; set; }
}
public class CreateTodoListCommandHandler : IRequestHandler<CreateTodoListCommand, Guid>
{
private readonly IRepository<Domain.Entities.TodoList> _repository;
public CreateTodoListCommandHandler(IRepository<Domain.Entities.TodoList> repository)
{
_repository = repository;
}
public async Task<Guid> Handle(CreateTodoListCommand request, CancellationToken cancellationToken)
{
var entity = new Domain.Entities.TodoList
{
Title = request.Title
};
await _repository.AddAsync(entity, cancellationToken);
return entity.Id;
}
}
有一些实践是将 接下来在TodoListController里实现对应的POST方法,
using MediatR;
using Microsoft.AspNetCore.Mvc;
using TodoList.Application.TodoLists.Commands.CreateTodoList;
namespace TodoList.Api.Controllers;
[ApiController]
[Route("/todo-list")]
public class TodoListController : ControllerBase
{
private readonly IMediator _mediator;
// 注入MediatR
public TodoListController(IMediator mediator)
=> _mediator = mediator;
[HttpPost]
public async Task<Guid> Create([FromBody] CreateTodoListCommand command)
{
var createdTodoList = await _mediator.Send(command);
// 出于演示的目的,这里只返回创建出来的TodoList的Id,
// 实际使用中可能会选择IActionResult作为返回的类型并返回CreatedAtRoute对象,
// 因为我们还没有去写GET方法,返回CreatedAtRoute会报错(找不到对应的Route),等讲完GET后会在那里更新
return createdTodoList.Id;
}
}
POST TodoItem 类似TodoListController和CreateTodoListCommand的实现,这里我直接把代码贴出来了 。
using MediatR;
using TodoList.Application.Common.Interfaces;
using TodoList.Domain.Entities;
using TodoList.Domain.Events;
namespace TodoList.Application.TodoItems.Commands.CreateTodoItem;
public class CreateTodoItemCommand : IRequest<Guid>
{
public Guid ListId { get; set; }
public string? Title { get; set; }
}
public class CreateTodoItemCommandHandler : IRequestHandler<CreateTodoItemCommand, Guid>
{
private readonly IRepository<TodoItem> _repository;
public CreateTodoItemCommandHandler(IRepository<TodoItem> repository)
{
_repository = repository;
}
public async Task<Guid> Handle(CreateTodoItemCommand request, CancellationToken cancellationToken)
{
var entity = new TodoItem
{
// 这个ListId在前文中的代码里漏掉了,需要添加到Domain.Entities.TodoItem实体上
ListId = request.ListId,
Title = request.Title,
Done = false
};
await _repository.AddAsync(entity, cancellationToken);
return entity.Id;
}
}
using MediatR;
using Microsoft.AspNetCore.Mvc;
using TodoList.Application.TodoItems.Commands.CreateTodoItem;
namespace TodoList.Api.Controllers;
[ApiController]
[Route("/todo-item")]
public class TodoItemController : ControllerBase
{
private readonly IMediator _mediator;
// 注入MediatR
public TodoItemController(IMediator mediator)
=> _mediator = mediator;
[HttpPost]
public async Task<Guid> Create([FromBody] CreateTodoItemCommand command)
{
var createdTodoItem = await _mediator.Send(command);
// 处于演示的目的,这里只返回创建出来的TodoItem的Id,理由同前
return createdTodoItem.Id;
}
}
验证运行Api项目,通过Hoppscotch发送对应接口请求: 创建TodoList验证请求
返回
数据库
第一条数据是种子数据,第二条是我们刚才创建的 。 创建TodoItem验证继续拿刚才创建的这个TodoList的Id来创建新的TodoItem: 请求
返回
数据库
最后一条是我们新创建的,其余是种子数据 。 总结我们已经通过演示在POST请求中实现MediatR库带来的CQRS模式,在这篇文章里我留了一个坑 。就是领域事件的Handler并没有任何演示,只是创建了一个文件夹,结合在这篇文章中留下来的发布领域事件的坑,会在DELETE的文章中填完 。 看起来使用CQRS模式使得我们的代码结构变得更加复杂了,但是对于一些再复杂一些的实际项目中,正确使用CQRS模式有助于你分析和整理业务需求,并将相关的业务需求以及相关模型梳理到统一的位置进行管理,包括在后续的文章里我们会陆续向其中加入诸如入参校验、出参类型转换等逻辑 。认真思考并运用习惯之后,大家可以自行体会这样做的“权衡” 。 参考资料MediatR Mediator? 以上就是.NET 6开发TodoList应用之使用MediatR实现POST请求的详细内容,更多关于.NET 6 MediatR实现POST请求的资料请关注其它相关文章! |