MediatR CQRS
最重要的两个接口:
IRequest
IRequestHandler
public class GetMoviesQuery : IRequest<IList<GetMovieDto>> { }
public class GetMoviesQueryHandler : IRequestHandler<GetMoviesQuery, IList<GetMovieDto>>{
public async Task<IList<GetMovieDto>> Handle()
}
程序整体架构图
graph LR
subgraph 客户端 [客户端]
direction LR
Client((HTTP请求))
end
subgraph 控制层 [控制层]
MovieController["MovieController<br>处理HTTP请求"]
end
subgraph 应用层 [应用层]
MediatR["MediatR<br>中介者模式"]
CreateMovieCommand["CreateMovieCommand<br>创建电影命令"]
GetMoviesQuery["GetMoviesQuery<br>获取电影列表查询"]
GetMovieQuery["GetMovieQuery<br>根据ID获取电影查询"]
CreateUserCommandHandler["CreateUserCommandHandler<br>创建电影处理器"]
GetMoviesQueryHandler["GetMoviesQueryHandler<br>获取电影列表处理器"]
GetMovieQueryHandler["GetMovieQueryHandler<br>根据ID获取电影处理器"]
end
subgraph 领域层 [领域层]
subgraph 领域实体 [领域实体]
Movie["Movie<br>电影实体"]
end
subgraph DTOs [数据传输对象]
CreateMovieDto["CreateMovieDto"]
GetMovieDto["GetMovieDto"]
end
subgraph 枚举 [枚举]
MovieGenre["MovieGenre<br>电影类型"]
end
end
subgraph 基础设施层 [基础设施层]
ApplicationDbContext["ApplicationDbContext<br>数据库上下文"]
SQLServer["SQL Server<br>数据库"]
end
Client --> MovieController
MovieController --> MediatR
MediatR --> CreateMovieCommand
MediatR --> GetMoviesQuery
MediatR --> GetMovieQuery
CreateMovieCommand --> CreateUserCommandHandler
GetMoviesQuery --> GetMoviesQueryHandler
GetMovieQuery --> GetMovieQueryHandler
CreateUserCommandHandler --> Movie
GetMoviesQueryHandler --> Movie
GetMovieQueryHandler --> Movie
Movie --> ApplicationDbContext
ApplicationDbContext --> SQLServer
程序流程图
sequenceDiagram
participant Client
participant MovieController
participant MediatR
participant CreateUserCommandHandler
participant GetMovieQueryHandler
participant ApplicationDbContext
participant Movie
%% 创建电影过程
Client->>MovieController: POST /api/movie (CreateMovieRequest)
MovieController->>MediatR: Send(CreateMovieCommand)
MediatR->>CreateUserCommandHandler: Handle(CreateMovieCommand)
CreateUserCommandHandler->>Movie: CreateMovie()
CreateUserCommandHandler->>ApplicationDbContext: AddAsync(Movie), SaveChangesAsync()
CreateUserCommandHandler-->>MediatR: CreateMovieDto
MediatR-->>MovieController: CreateMovieDto
MovieController-->>Client: HTTP 200 OK (CreateMovieDto)
%% 获取电影信息过程
Client->>MovieController: GET /getmovies/{id}
MovieController->>MediatR: Send(GetMovieQuery)
MediatR->>GetMovieQueryHandler: Handle(GetMovieQuery)
GetMovieQueryHandler->>ApplicationDbContext: FindByIdAsync(id)
ApplicationDbContext-->>GetMovieQueryHandler: Movie
GetMovieQueryHandler->>Movie: MapTo() as GetMovieDto
GetMovieQueryHandler-->>MediatR: GetMovieDto
MediatR-->>MovieController: GetMovieDto
MovieController-->>Client: HTTP 200 OK (GetMovieDto)
CQRS-and-MediatR-demo-master\MediatR_Demo\appsettings.Development.json
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}
CQRS-and-MediatR-demo-master\MediatR_Demo\appsettings.json
{
"ConnectionStrings": {
"Standard": "Server=localhost;Database=mediatr-demo;Trusted_Connection=True;"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}
CQRS-and-MediatR-demo-master\MediatR_Demo\MediatR_Demo.csproj
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="MediatR" Version="10.0.1" />
<PackageReference Include="MediatR.Extensions.Microsoft.DependencyInjection" Version="10.0.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="6.0.6" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="6.0.6" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="6.0.6">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.2.3" />
</ItemGroup>
</Project>
CQRS-and-MediatR-demo-master\MediatR_Demo\Program.cs
using MediatR;
using MediatR_Demo.Repository.Context;
using Microsoft.EntityFrameworkCore;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("Standard"))
);
builder.Services.AddMediatR(typeof(Program));
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
CQRS-and-MediatR-demo-master\MediatR_Demo\Application\Movies\Commands\CreateMovie\CreateMovieCommand.cs
using MediatR;
using MediatR_Demo.Core.Enums;
using MediatR_Demo.Domain.DTOs.Responses.Movie;
namespace MediatR_Demo.Application.Movies.Commands.CreateMovie;
public class CreateMovieCommand : IRequest<CreateMovieDto>
{
public CreateMovieCommand(string? title, string? description, MovieGenre? genre, int? rating)
{
Title = title;
Description = description;
Genre = genre;
Rating = rating;
}
public string? Title { get; set; }
public string? Description { get; set; }
public MovieGenre? Genre { get; set; }
public int? Rating { get; set; }
}
CQRS-and-MediatR-demo-master\MediatR_Demo\Application\Movies\Commands\CreateMovie\CreateUserCommandExtension.cs
using MediatR_Demo.Domain.Entities.Movie;
namespace MediatR_Demo.Application.Movies.Commands.CreateMovie
{
public static class CreateUserCommandExtension
{
public static Movie CreateMovie(this CreateMovieCommand command)
{
var movie = new Movie
(
command.Title,
command.Description,
command.Genre,
command.Rating
);
return movie;
}
}
}
CQRS-and-MediatR-demo-master\MediatR_Demo\Application\Movies\Commands\CreateMovie\CreateUserCommandHandler.cs
using MediatR;
using MediatR_Demo.Domain.DTOs.Responses.Movie;
using MediatR_Demo.Repository.Context;
namespace MediatR_Demo.Application.Movies.Commands.CreateMovie
{
public class CreateUserCommandHandler : IRequestHandler<CreateMovieCommand, CreateMovieDto>
{
private readonly ApplicationDbContext _dbContext;
public CreateUserCommandHandler(ApplicationDbContext dbContext)
{
_dbContext = dbContext;
}
public async Task<CreateMovieDto> Handle(CreateMovieCommand request, CancellationToken cancellationToken)
{
Domain.Entities.Movie.Movie movie = request.CreateMovie();
await _dbContext.Movies.AddAsync(movie);
await _dbContext.SaveChangesAsync();
return new CreateMovieDto(movie.Id);
}
}
}
CQRS-and-MediatR-demo-master\MediatR_Demo\Application\Movies\Queries\GetMovie\GetMovieQuery.cs
using MediatR;
using MediatR_Demo.Domain.DTOs.Responses.Movie;
namespace MediatR_Demo.Application.Movies.Queries.GetMovie;
public class GetMovieQuery : IRequest<GetMovieDto>
{
public long? Id { get; set; }
public GetMovieQuery(long? id)
{
Id = id;
}
}
CQRS-and-MediatR-demo-master\MediatR_Demo\Application\Movies\Queries\GetMovie\GetMovieQueryExtensions.cs
using MediatR_Demo.Domain.DTOs.Responses.Movie;
using MediatR_Demo.Domain.Entities.Movie;
namespace MediatR_Demo.Application.Movies.Queries.GetMovie;
public static class GetMovieQueryExtensions
{
public static GetMovieDto MapTo(this Movie movie)
{
return new GetMovieDto
{
Id = movie.Id,
Title = movie.Title,
Description = movie.Description,
Genre = movie.Genre,
Rating = movie.Rating
};
}
}
CQRS-and-MediatR-demo-master\MediatR_Demo\Application\Movies\Queries\GetMovie\GetMovieQueryHandler.cs
using MediatR;
using MediatR_Demo.Domain.DTOs.Responses.Movie;
using MediatR_Demo.Repository.Context;
using Microsoft.EntityFrameworkCore;
namespace MediatR_Demo.Application.Movies.Queries.GetMovie;
public class GetMovieQueryHandler : IRequestHandler<GetMovieQuery, GetMovieDto>
{
private readonly ApplicationDbContext _dbContext;
public GetMovieQueryHandler(ApplicationDbContext dbContext)
{
_dbContext = dbContext;
}
public async Task<GetMovieDto> Handle(
GetMovieQuery request,
CancellationToken cancellationToken
)
{
var movie = await _dbContext.Movies.Where(x => x.Id == request.Id).FirstOrDefaultAsync();
if (movie != null)
{
var movieItem = movie.MapTo();
return movieItem;
}
return null;
}
}
CQRS-and-MediatR-demo-master\MediatR_Demo\Application\Movies\Queries\GetMovies\GetMoviesQuery.cs
using MediatR;
using MediatR_Demo.Domain.DTOs.Responses.Movie;
namespace MediatR_Demo.Application.Movies.Queries.GetMovies;
public class GetMoviesQuery : IRequest<IList<GetMovieDto>> { }
CQRS-and-MediatR-demo-master\MediatR_Demo\Application\Movies\Queries\GetMovies\GetMoviesQueryExtensions.cs
using MediatR_Demo.Domain.DTOs.Responses.Movie;
using MediatR_Demo.Domain.Entities.Movie;
namespace MediatR_Demo.Application.Movies.Queries.GetMovies;
public static class GetMoviesQueryExtensions
{
public static GetMovieDto MapTo(this Movie movie)
{
return new GetMovieDto
{
Id = movie.Id,
Title = movie.Title,
Description = movie.Description,
Genre = movie.Genre,
Rating = movie.Rating
};
}
}
CQRS-and-MediatR-demo-master\MediatR_Demo\Application\Movies\Queries\GetMovies\GetMoviesQueryHandler.cs
using MediatR;
using MediatR_Demo.Domain.DTOs.Responses.Movie;
using MediatR_Demo.Repository.Context;
using Microsoft.EntityFrameworkCore;
namespace MediatR_Demo.Application.Movies.Queries.GetMovies;
public class GetMoviesQueryHandler : IRequestHandler<GetMoviesQuery, IList<GetMovieDto>>
{
private readonly ApplicationDbContext _dbContext;
public GetMoviesQueryHandler(ApplicationDbContext dbContext)
{
_dbContext = dbContext;
}
public async Task<IList<GetMovieDto>> Handle(
GetMoviesQuery request,
CancellationToken cancellationToken
)
{
var movies = await _dbContext.Movies.ToListAsync();
var movieList = new List<GetMovieDto>();
foreach (var movieItem in movies)
{
var movie = movieItem.MapTo();
movieList.Add(movie);
}
return movieList;
}
}
CQRS-and-MediatR-demo-master\MediatR_Demo\Controllers\MovieController.cs
using MediatR;
using MediatR_Demo.Application.Movies.Commands.CreateMovie;
using MediatR_Demo.Application.Movies.Queries.GetMovie;
using MediatR_Demo.Application.Movies.Queries.GetMovies;
using MediatR_Demo.Domain.DTOs.Requests.Movie;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
namespace MediatR_Demo.Controllers;
[Route("api/[controller]")]
[ApiController]
public class MovieController : ControllerBase
{
private readonly IMediator _mediator;
public MovieController(IMediator mediator)
{
_mediator = mediator;
}
[HttpGet]
public async Task<IActionResult> GetMovies()
{
var movies = await _mediator.Send(new GetMoviesQuery());
if (movies != null)
{
return Ok(movies);
}
return NotFound("No movies in database. Please add a movie first.");
}
[HttpGet("/getmovies/{id}")]
public async Task<IActionResult> GetMovie(long id)
{
var movie = await _mediator.Send(new GetMovieQuery(id));
if (movie != null)
{
return Ok(movie);
}
return NotFound($"No movie in database with ID: {id}.");
}
[HttpPost]
public async Task<IActionResult> CreateMovie([FromBody] CreateMovieRequest request)
{
var movie = await _mediator.Send(
new CreateMovieCommand(
request.Title,
request.Description,
request.Genre,
request.Rating
)
);
return Ok(movie);
}
}
CQRS-and-MediatR-demo-master\MediatR_Demo\Core\Enums\MovieGenre.cs
namespace MediatR_Demo.Core.Enums;
public enum MovieGenre
{
Action,
Comedy,
Drama,
Fantasy,
Horror,
Mystery,
Romance,
Thriller,
Western
}
CQRS-and-MediatR-demo-master\MediatR_Demo\Domain\DTOs\Requests\Movie\CreateMovieRequest.cs
using MediatR_Demo.Core.Enums;
namespace MediatR_Demo.Domain.DTOs.Requests.Movie
{
public class CreateMovieRequest
{
public string? Title { get; set; }
public string? Description { get; set; }
public MovieGenre? Genre { get; set; }
public int? Rating { get; set; }
}
}
CQRS-and-MediatR-demo-master\MediatR_Demo\Domain\DTOs\Responses\Movie\CreateMovieDto.cs
namespace MediatR_Demo.Domain.DTOs.Responses.Movie
{
public class CreateMovieDto
{
public CreateMovieDto(long id)
{
Id = id;
}
public long Id { get; set; }
}
}
CQRS-and-MediatR-demo-master\MediatR_Demo\Domain\DTOs\Responses\Movie\GetMovieDto.cs
using MediatR_Demo.Core.Enums;
namespace MediatR_Demo.Domain.DTOs.Responses.Movie
{
public class GetMovieDto
{
public long Id { get; set; }
public string? Title { get; set; }
public string? Description { get; set; }
public MovieGenre? Genre { get; set; }
public int? Rating { get; set; }
}
}
CQRS-and-MediatR-demo-master\MediatR_Demo\Domain\Entities\Movie\Movie.cs
using MediatR_Demo.Core.Enums;
namespace MediatR_Demo.Domain.Entities.Movie
{
public class Movie
{
public Movie(string? title, string? description, MovieGenre? genre, int? rating)
{
Title = title;
Description = description;
Genre = genre;
Rating = rating;
}
public long Id { get; set; }
public string? Title { get; set; }
public string? Description { get; set; }
public MovieGenre? Genre { get; set; }
public int? Rating { get; set; }
}
}
CQRS-and-MediatR-demo-master\MediatR_Demo\Properties\launchSettings.json
{
"$schema": "https://json.schemastore.org/launchsettings.json",
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:14989",
"sslPort": 44321
}
},
"profiles": {
"MediatR_Demo": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"launchUrl": "swagger",
"applicationUrl": "https://localhost:7001;http://localhost:7000",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"launchUrl": "swagger",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}
CQRS-and-MediatR-demo-master\MediatR_Demo\Repository\Context\ApplicationDbContext.cs
using MediatR_Demo.Domain.Entities.Movie;
using Microsoft.EntityFrameworkCore;
namespace MediatR_Demo.Repository.Context
{
public class ApplicationDbContext : DbContext
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options) { }
public DbSet<Movie> Movies { get; set; }
}
}
CQRS-and-MediatR-demo-master\MediatR_Demo.sln
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.32014.148
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediatR_Demo", "MediatR_Demo\MediatR_Demo.csproj", "{40AF7C5A-7DFD-452F-BB6F-47E42F59F27D}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{40AF7C5A-7DFD-452F-BB6F-47E42F59F27D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{40AF7C5A-7DFD-452F-BB6F-47E42F59F27D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{40AF7C5A-7DFD-452F-BB6F-47E42F59F27D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{40AF7C5A-7DFD-452F-BB6F-47E42F59F27D}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {932BD9F0-738E-40A5-8E0B-276918C9FF07}
EndGlobalSection
EndGlobal
浙公网安备 33010602011771号