您好,登錄后才能下訂單哦!
本篇內(nèi)容介紹了“如何使用MediatR實(shí)現(xiàn)POST請(qǐng)求”的有關(guān)知識(shí),在實(shí)際案例的操作過(guò)程中,不少人都會(huì)遇到這樣的困境,接下來(lái)就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!
需求很簡(jiǎn)單:如何創(chuàng)建新的TodoList和TodoItem并持久化。
初學(xué)者按照教程去實(shí)現(xiàn)的話,應(yīng)該分成以下幾步:創(chuàng)建Controller并實(shí)現(xiàn)POST方法;實(shí)用傳入的請(qǐng)求參數(shù)new一個(gè)數(shù)據(jù)庫(kù)實(shí)體對(duì)象;調(diào)用IRepository<T>完成數(shù)據(jù)庫(kù)的寫入,最多會(huì)在中間加一層Service。這個(gè)做法本身沒(méi)有問(wèn)題,也是需要從初學(xué)階段開始扎實(shí)地掌握開發(fā)技能的必經(jīng)之路,有助于幫助理解邏輯調(diào)用的過(guò)程。
對(duì)于稍微正式一些的項(xiàng)目,.NET工程上習(xí)慣的實(shí)現(xiàn)是通過(guò)使用一些比較成熟的類庫(kù)框架,有效地對(duì)業(yè)務(wù)邏輯進(jìn)行分類管理、消除冗余代碼,以達(dá)到業(yè)務(wù)邏輯職責(zé)清晰簡(jiǎn)潔的目的。在這個(gè)階段我們經(jīng)常使用的兩個(gè)類庫(kù)分別是AutoMapper和MediatR。
合理組織并使用MediatR,完成POST請(qǐng)求。
首先來(lái)簡(jiǎn)單地介紹一下這個(gè)類庫(kù)。
關(guān)于CQRS模式、中介者模式和MediatR
CQRS模式全稱是“Command Query Responsibility Segregation”,正如字面意思,CQRS模式的目的在于將讀取操作和寫入操作的指責(zé)區(qū)分開,并使用不同的Model去表示。從CRUD的角度來(lái)說(shuō),就是把R和CUD區(qū)分開來(lái)對(duì)待。如下圖所示:
這個(gè)模式可以有效地應(yīng)用到具有主從分離的數(shù)據(jù)庫(kù)架構(gòu)中,當(dāng)需要獲取數(shù)據(jù)時(shí),從只讀數(shù)據(jù)庫(kù)(一般是從庫(kù))中讀取數(shù)據(jù),當(dāng)需要寫入或更新數(shù)據(jù)時(shí),向主庫(kù)進(jìn)行操作。
CQRS模式旨在解決的問(wèn)題是:為了屏蔽數(shù)據(jù)庫(kù)層面“寫優(yōu)先”還是“讀優(yōu)先”的優(yōu)化設(shè)計(jì)策略,在業(yè)務(wù)邏輯側(cè)進(jìn)行解耦。
任何設(shè)計(jì)模式都是對(duì)解決特定問(wèn)題的一個(gè)Trade off,自然也帶來(lái)了一些缺點(diǎn),首先就是服務(wù)內(nèi)部的組件復(fù)雜度上升了,因?yàn)樾枰獎(jiǎng)?chuàng)建額外的類來(lái)實(shí)現(xiàn)CQRS模式;其次如果數(shù)據(jù)層是分離的,那么可能會(huì)有數(shù)據(jù)的狀態(tài)不一致問(wèn)題。
這是23種基本設(shè)計(jì)模式中的一個(gè),屬于行為型設(shè)計(jì)模式,它給出了組件之間交互的一種解耦的方式。簡(jiǎn)單參考下圖,具體內(nèi)容就不過(guò)多解釋了,任何一篇介紹設(shè)計(jì)模式的文章都有介紹。
這種設(shè)計(jì)模式實(shí)際上是一種采用依賴倒置(Inversion of Control, IoC)的方式,實(shí)現(xiàn)了圖中藍(lán)色組件的松耦合。
這是在開發(fā)中被廣泛采用的實(shí)現(xiàn)以上兩種設(shè)計(jì)模式的類庫(kù),更準(zhǔn)確的說(shuō)法是,它通過(guò)應(yīng)用中介者模式,實(shí)現(xiàn)了進(jìn)程內(nèi)CQRS?;舅枷胧撬衼?lái)自API接口和數(shù)據(jù)存儲(chǔ)之間的邏輯,都需要通過(guò)MediatR來(lái)組織(即所謂的“中介者”)。
從實(shí)現(xiàn)上看,MediatR提供了幾組用于不同場(chǎng)景的接口,我們?cè)诒疚闹刑幚淼谋容^多的是IRequest<T>/IRequestHandler<T>以及INotification<T>/INotificationHander<T>兩組接口,更多的請(qǐng)參考官方文檔和例子。
所有需要使用MediatR的地方都集中在Application項(xiàng)目中。
$ dotnet add src/TodoList.Application/TodoList.Application.csproj package MediatR.Extensions.Microsoft.DependencyInjection
為了適配CQRS的模式,我們?cè)贏pplication項(xiàng)目中的TodoLists和TodoItems下相同地創(chuàng)建幾個(gè)文件夾:
Commands:用于組織CUD相關(guān)的業(yè)務(wù)邏輯;
Queries:用于組織R相關(guān)的業(yè)務(wù)邏輯;
EventHandlers:用于組織領(lǐng)域事件處理的相關(guān)業(yè)務(wù)邏輯。
在Application根目錄下同樣創(chuàng)建DependencyInjection.cs用于該項(xiàng)目的依賴注入:
DependencyInjection.cs
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項(xiàng)目中使用:
// 省略其他... // 添加應(yīng)用層配置 builder.Services.AddApplication(); // 添加基礎(chǔ)設(shè)施配置 builder.Services.AddInfrastructure(builder.Configuration);
在本章中我們只實(shí)現(xiàn)TodoList和TodoItem的Create接口(POST),剩下的接口后面的文章中逐步涉及。
POST TodoList
在Application/TodoLists/Commands/下新建一個(gè)目錄CreateTodoList用于存放創(chuàng)建一個(gè)TodoList相關(guān)的所有邏輯:
CreateTodoListCommand.cs
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; } }
有一些實(shí)踐是將Request和RequestHandler分開兩個(gè)文件,我更傾向于像這樣將他倆放在一起,一是保持簡(jiǎn)潔,二是當(dāng)你需要順著一個(gè)Command去尋找它對(duì)應(yīng)的Handler時(shí),不需要更多的跳轉(zhuǎn)。
接下來(lái)在TodoListController里實(shí)現(xiàn)對(duì)應(yīng)的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); // 出于演示的目的,這里只返回創(chuàng)建出來(lái)的TodoList的Id, // 實(shí)際使用中可能會(huì)選擇IActionResult作為返回的類型并返回CreatedAtRoute對(duì)象, // 因?yàn)槲覀冞€沒(méi)有去寫GET方法,返回CreatedAtRoute會(huì)報(bào)錯(cuò)(找不到對(duì)應(yīng)的Route),等講完GET后會(huì)在那里更新 return createdTodoList.Id; } }
POST TodoItem
類似TodoListController和CreateTodoListCommand的實(shí)現(xiàn),這里我直接把代碼貼出來(lái)了。
CreateTodoItemCommand.cs
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 { // 這個(gè)ListId在前文中的代碼里漏掉了,需要添加到Domain.Entities.TodoItem實(shí)體上 ListId = request.ListId, Title = request.Title, Done = false }; await _repository.AddAsync(entity, cancellationToken); return entity.Id; } }
TodoItemController.cs
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); // 處于演示的目的,這里只返回創(chuàng)建出來(lái)的TodoItem的Id,理由同前 return createdTodoItem.Id; } }
運(yùn)行Api項(xiàng)目,通過(guò)Hoppscotch發(fā)送對(duì)應(yīng)接口請(qǐng)求:
請(qǐng)求
返回
數(shù)據(jù)庫(kù)
第一條數(shù)據(jù)是種子數(shù)據(jù),第二條是我們剛才創(chuàng)建的。
繼續(xù)拿剛才創(chuàng)建的這個(gè)TodoList的Id來(lái)創(chuàng)建新的TodoItem:
請(qǐng)求
返回
數(shù)據(jù)庫(kù)
最后一條是我們新創(chuàng)建的,其余是種子數(shù)據(jù)。
“如何使用MediatR實(shí)現(xiàn)POST請(qǐng)求”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí)可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。