Difference between revisions of "MediatR"
From Logic Wiki
(→Using in Controller) |
|||
| Line 66: | Line 66: | ||
public async Task<ActionResult<int>> CreatePlayer(CreatePlayerCommand command) | public async Task<ActionResult<int>> CreatePlayer(CreatePlayerCommand command) | ||
{ | { | ||
| − | var playerId = | + | var playerId = await _sender.Send(command); |
return Ok(playerId); | return Ok(playerId); | ||
} | } | ||
} | } | ||
</pre> | </pre> | ||
| + | |||
== Query == | == Query == | ||
=== No Parameter === | === No Parameter === | ||
Latest revision as of 12:37, 26 February 2026
Contents
Installation
Install Nuget Package of MediatR (by Jimmy Bogard)
In Program.cs register it. (it's Program assembly because it's the same application. )
builder.Services.AddMediatR(configuration => {
configuration.RegisterServicesFromAssembly(typeof(Program).Assembly);
})
File - Folder Structure
Folders
create folders like
/Features/Players/Queries /Features/Players/Commands
Files
In the Commands folder
CreatePlayerCommand.cs
using MediatR
public class CreatePlayerCommand : IRequest<int>
{
public string Name {get; set;}
public int Level {get; set;}
}
CreatePlayerCommandHandler.cs
using MediatR
public class CreatePlayerCommandHandler : IRequestHandler<CreatePlayerCommand, int>
{
private readonly myDbContext _context;
public CreatePlayerCommandHandler(MyDbContext context)
{
_context = context;
}
public Task<int> Handle(CreatePlayerCommand request, CancellationToken cancellationToken)
{
var player = new Player{Name = request.Name, Level=request.Level};
_context.Players.Add(player);
await _context.SaveChangesAsync();
return player.Id;
}
}
Using in Controller
using MediatR
public class PlayerController: ControllerBase
{
private readonly ISender _sender
public PlayerController(ISender sender)
{
_sender = sender
}
public async Task<ActionResult<int>> CreatePlayer(CreatePlayerCommand command)
{
var playerId = await _sender.Send(command);
return Ok(playerId);
}
}
Query
No Parameter
public class WeatherQuery : IRequest<WeatherForecast[]>
{
}
public class WeatherQueryHandler : IRequestHandler<WeatherQuery ,WeatherForecast[]>
{
}
public async Task<ActionResult<IEnumerable<WeatherForecast>>>Get()
{
var results = await _sender.Send(new WeatherQuery());
return Ok(results);
}
With Parameter
public class SingleQuery : IRequest<WeatherForecast>
{
public int RecordIndex { get; set; }
}
public class SingleQueryHandler : IRequestHandler<SingleQuery, WeatherForecast>
{
}
public async Task<ActionResult<WeatherForecast>>Get(SingleQuery query)
{
var results = await _sender.Send(query);
return Ok(results);
}
Delete Command
Command
using MediatR; public record DeleteItemCommand(int Id) : IRequest<bool>;
Command Handler
using MediatR;
using Microsoft.EntityFrameworkCore;
using System.Threading;
using System.Threading.Tasks;
public class DeleteItemCommandHandler : IRequestHandler<DeleteItemCommand, bool>
{
private readonly AppDbContext _context;
public DeleteItemCommandHandler(AppDbContext context)
{
_context = context;
}
public async Task<bool> Handle(DeleteItemCommand request, CancellationToken cancellationToken)
{
var item = await _context.Items.FirstOrDefaultAsync(i => i.Id == request.Id, cancellationToken);
if (item == null)
return false;
_context.Items.Remove(item);
await _context.SaveChangesAsync(cancellationToken);
return true;
}
}
Controller
using MediatR;
using Microsoft.AspNetCore.Mvc;
using System.Threading.Tasks;
[ApiController]
[Route("api/[controller]")]
public class ItemsController : ControllerBase
{
private readonly IMediator _mediator;
public ItemsController(IMediator mediator)
{
_mediator = mediator;
}
[HttpDelete("{id:int}")]
public async Task<IActionResult> DeleteItem(int id)
{
var success = await _mediator.Send(new DeleteItemCommand(id));
if (!success)
return NotFound(new { Message = $"Item with id {id} not found." });
return NoContent();
}
}
Validation Pipeline
Fluent Validation Packages
Add these nuget packages
FluentValidation FluentValidationDependencyInjectionExtensions
UseFluentValidationExceptionHandler
Create /Extensions/ApplicationBuilderExtensions.cs file
public static class ApplicationBuilderExtensions
{
public static void UseFluentValidationExceptionHandler(this IApplicationBuilder app)
{
app.UseExceptionHandler(x => {
x.Run(async context => {
var errorFeature = context.Features.Get<IExceptionHandlerFeature>();
var exception = errorFeature.Error;
if(!(exception is ValidationException validationException))
{
throw exception;
}
var errors = validationExceptionErrors.Select(err =>
{
err.PropertyName,
err.ErrorMessage
});
var errorText = JsonSerializer.Serialize(errors);
context.Response.StatusCode = 400;
context.Response.ContentType = "application/json";
await context.Response.WriteAsync(errorText, Encoding.UTF8);
});
});
}
}
CreatePlayerCommandValidation.cs
Create this file in the same folder with the CreatePlayerCommand.cs
using FluentValidation;
public class CreatePlayerCommandValidation : AbstractValidator<CreatePlayerCommand>
{
public CreatePlayerCommandValidation()
{
RuleFor(x=> x.Name()
.NotEmpty();
RuleFor(x=> x.Level()
.NotEmpty();
}
}
Add ValidatiorsFromAssembly
in Startup.cs under ConfigureServices
services.AddValidatorsFromAssembly(typeof(Startup).Assembly);
PipelineBehaviour
Create this folder in the root of the project
/PipelineBehaviours
Add ValidationBehaviour.cs
public class ValidationBehaviour<TRequest, TResponse>: IPipelineBehaviour<TRequest, TResponse>
where TRequest : IRequest<TResponse>
{
private readonly IEnumerable<IValidator<TRequest>> _validators;
public ValidationBehaviour(IEnumerable<IValidator<TRequest>> validators)
{
_validators = validators;
}
public Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate<TResponse> next)
{
var context = new ValidationContext(request)
var failures = _validators
.Select(x=> x.Validate(context))
.SelectMany(x=> x.Errors)
.Where(x=> x != null)
.ToList();
if (failures.Any())
{
throw new ValidationException(failures)
}
return next();
}
}
instead of TResponse here it can return a RetVal object. So we do not need to throw an exception here but return an object.
Registering Behaviour
in Startup.cs under ConfigureServices
services.AddTransient(typeof(IPipelineBehaviour<,>), typeof(ValidationBehaviour<,>));