I am attempting to use the MediatR library to implement a command pattern in my net core web API, however, I am unsure how to proceed.
I have a situation where when a user attempts to registers an account, the API should check the database for a company with a domain that matches that of the provided email address then attach the company id to the user object as a foreign key or return an error if no company exists with that domain.
I have all the necessary commands and handler to perform these separately:
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Application.Application.Exceptions;
using Application.Domain.Entities;
using Application.Persistence;
using MediatR;
using Microsoft.EntityFrameworkCore;
namespace Application.Application.Companies.Queries.GetCompanyByDomain
{
public class GetCompanyByDomainHandler
IRequestHandler<GetCompanyByDomainQuery, Company>
{
private readonly ApplicationDbContext _context;
public GetCompanyByDomainHandler(ApplicationDbContext context)
{
_context = context;
}
public async Task<Company> Handle(GetCompanyByDomainQuery request,
CancellationToken cancellationToken)
{
var company = await _context.Companies.Where(c => c.Domain ==
request.Domain).SingleOrDefaultAsync();
if (company != null) {
return company;
}
throw new NotFoundException(nameof(Company), request.Domain);
}
}
}
using Application.Domain.Entities;
using MediatR;
namespace Application.Application.Companies.Queries.GetCompanyByDomain
{
public class GetCompanyByDomainQuery : IRequest<Company>
{
public string Domain { get; set; }
}
}
using MediatR;
namespace Application.Application.Users.Commands.CreateUser
{
public class CreateUserCommand : IRequest<int>
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string EmailAddress { get; set; }
public string Password { get; set; }
public string ConfirmPassword { get; set; }
public int CompanyId { get; set; }
}
}
using MediatR;
using Application.Domain.Entities.Identity;
using Microsoft.AspNetCore.Identity;
using System.Threading;
using System.Threading.Tasks;
using System;
namespace Application.Application.Users.Commands.CreateUser
{
public class CreateUserCommandHandler : IRequestHandler<CreateUserCommand, int>
{
private readonly UserManager<User> _userManager;
public CreateUserCommandHandler(UserManager<User> userManager)
{
_userManager = userManager;
}
public async Task<int> Handle(CreateUserCommand request, CancellationToken cancellationToken)
{
var entity = new User
{
FirstName = request.FirstName,
LastName = request.LastName,
Email = request.EmailAddress,
UserName = request.EmailAddress,
CompanyId = request.CompanyId
};
var createUserResult = await _userManager.CreateAsync(entity, request.Password);
if (createUserResult.Succeeded)
{
return entity.Id;
}
throw new Exception("failed to create user");
}
}
}
using FluentValidation;
namespace Application.Application.Users.Commands.CreateUser
{
public class CreateUserCommandValidator : AbstractValidator<CreateUserCommand>
{
public CreateUserCommandValidator()
{
RuleFor(v => v.Password)
.Equal(v => v.ConfirmPassword).WithName("password").WithMessage("Passwords do not match");
RuleFor(v => v.ConfirmPassword)
.Equal(v => v.Password).WithName("confirmPassword").WithMessage("Passwords do not match");
RuleFor(v => v.EmailAddress)
.NotEmpty().WithName("emailAddress").WithMessage("Email Address is required")
.EmailAddress().WithName("emailAddress").WithMessage("Invalid email address");
RuleFor(v => v.FirstName)
.NotEmpty().WithName("firstName").WithMessage("First Name is required");
RuleFor(v => v.LastName)
.NotEmpty().WithName("lastName").WithMessage("Last Name is required");
}
}
}
using System.Threading.Tasks;
using Application.Application.Users.Commands.CreateUser;
using Microsoft.AspNetCore.Mvc;
namespace Application.WebUI.Controllers
{
public class AuthenticationController : ControllerBase
{
[HttpPost]
public async Task<IActionResult> Register([FromBody] CreateUserCommand command)
{
return Ok(Mediator.Send(command));
}
}
}
But how do I make them part of a single request?
Many years later, having worked with MediatR
a lot more, I have realised that my question is fundamentally wrong. You shouldn't call a command after a query. If you need to do that, then you should merge the two together as they are performing a single domain driven action. The GetDomainFromEmail
should be something that the CreateUserCommand
uses within its own Handler rather than it being its own query. It could indeed be its own query if an endpoint existed to get the domain from an email, and both the CreateUserCommand
Handler and the GetDomainFromEmailQuery
handler would utilise a common utility that actually extracts the domain from an email.