Как мне выполнить запрос, а затем команду, основанную на результате запроса с MediatR? - PullRequest
0 голосов
/ 04 февраля 2019

Я пытаюсь использовать библиотеку MediatR для реализации шаблона команды в сетевом веб-API моего ядра, однако я не уверен, что делать дальше.

У меня есть ситуация, когда пользователь пытается зарегистрироватьучетной записи, API должен проверить базу данных для компании с доменом, который совпадает с указанным адресом электронной почты, затем прикрепить идентификатор компании к объекту пользователя в качестве внешнего ключа или вернуть ошибку, если с этим доменом не существует никакой компании.

У меня есть все необходимые команды и обработчик для их отдельного выполнения:

GetCompanyByDomainHandler.cs

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);
        }
    }
}

GetCompanyByDomainQuery.cs

using Application.Domain.Entities;
using MediatR;

namespace Application.Application.Companies.Queries.GetCompanyByDomain
{
    public class GetCompanyByDomainQuery : IRequest<Company>
    {
        public string Domain { get; set; }
    }
}

CreateUserCommand.cs

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; }
    }
}

CreateUserCommandHandler.cs

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");
        }
    }
}

CreateUserCommandValidator.cs

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");
        }
    }
}

AuthenticationController.cs

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));
        }
    }
}

Но как мне их сделатьчасть одного запроса?

Ответы [ 2 ]

0 голосов
/ 12 февраля 2019

Мне удалось решить эту проблему с помощью функции поведения MediatR, в частности RequestPresProcessBehavior, которая позволяет выполнить действие до обработки CreateUserCommand:

CreateUserCommandGetMatchingCompanyPreProcess.cs

using System.Threading;
using System.Threading.Tasks;
using Application.Persistence;
using MediatR;
using MediatR.Pipeline;
using Microsoft.EntityFrameworkCore;

namespace Application.Application.Users.Commands.CreateUser
{
    public class CreateUserCommandGetMatchingCompanyPreProcess: IRequestPreProcessor<CreateUserCommand>
    {
        private readonly ApplicationDbContext _context;

        public GetMatchingCompanyPreProcessCommand(ApplicationDbContext context)
        {
            _context = context;
        }

        public async Task Process(CreateUserCommand request, CancellationToken cancellationToken)
        {
            var domain = new MailAddress(request.EmailAddress).Host;
            var companyId = await _context.Companies.Where(c => c.Domain == domain).Select(c => c.Id).FirstOrDefaultAsync();
            request.CompanyId = companyId;
        }
    }
}
0 голосов
/ 04 февраля 2019

Прежде всего, измените GetCompanyByDomainHandler на НЕ выбрасывать исключение, если компания не найдена.
Не найденная компания не является исключением, это результат запроса.Просто верните null
Узнайте, почему

Теперь вы можете получить результат запроса, действовать по нему (а не пытаться поймать)

//todo: implement a way of getting the domain from an email address - regex, or string.split('.') ??
var domain = GetDomainFromEmail(command.EmailAddress);

//now get the company (or null, if it doesn't exist)
var getCompanyByDomainQuery = new GetCompanyByDomainQuery() { Domain = domain}
var company = await _mediator.SendAsync(getCompanyByDomainQuery);

//if theres a company, attach the id to the createUserCommand
if(company != null)
{
    command.CompanyId = company.Id;
}

//now save the user
var createdUser = await _mediator.SendAsync(command);

Вы можете обернуть это в другой Handler - или трактовать метод действия API как «оркестратор» (мои предпочтения)

...