怪奇物语

怪奇物语

首页 新随笔 联系 管理

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
posted on 2025-02-28 08:00  超级无敌美少男战士  阅读(14)  评论(0)    收藏  举报