Asp.net core 如何执行查询,然后根据查询执行命令';MediatR的结果如何?

Asp.net core 如何执行查询,然后根据查询执行命令';MediatR的结果如何?,asp.net-core,command-pattern,mediatr,Asp.net Core,Command Pattern,Mediatr,我正在尝试使用MediatR库在我的net core web API中实现命令模式,但是,我不确定如何继续 我遇到这样一种情况:当用户试图注册帐户时,API应该检查某个公司的数据库,该公司的域与提供的电子邮件地址的域匹配,然后将该公司id作为外键附加到用户对象,或者如果该域中不存在任何公司,则返回错误 我有所有必要的命令和处理程序来分别执行这些操作: GetCompanyByDomainHandler.cs 使用System.Linq; 使用系统线程; 使用System.Threading.Ta

我正在尝试使用MediatR库在我的net core web API中实现命令模式,但是,我不确定如何继续

我遇到这样一种情况:当用户试图注册帐户时,API应该检查某个公司的数据库,该公司的域与提供的电子邮件地址的域匹配,然后将该公司id作为外键附加到用户对象,或者如果该域中不存在任何公司,则返回错误

我有所有必要的命令和处理程序来分别执行这些操作:

GetCompanyByDomainHandler.cs
使用System.Linq;
使用系统线程;
使用System.Threading.Tasks;
使用Application.Application.Exceptions;
使用Application.Domain.Entities;
使用应用程序。持久性;
使用MediatR;
使用Microsoft.EntityFrameworkCore;
命名空间Application.Application.Companys.Queries.GetCompanyByDomain
{
公共类GetCompanyByDomainHandler
IRequestHandler
{
私有只读应用程序的bContext\u上下文;
public GetCompanyByDomainHandler(ApplicationDbContext上下文)
{
_上下文=上下文;
}
公共异步任务句柄(GetCompanyByDomainQuery请求,
取消令牌(取消令牌)
{
var company=wait_context.companys.Where(c=>c.Domain==
SingleOrDefaultAsync();
如果(公司!=null){
返回公司;
}
抛出新的NotFoundException(公司名称,request.Domain);
}
}
}
GetCompanyByDomainQuery.cs
使用Application.Domain.Entities;
使用MediatR;
命名空间Application.Application.Companys.Queries.GetCompanyByDomain
{
公共类GetCompanyByDomainQuery:IRequest
{
公共字符串域{get;set;}
}
}
CreateUserCommand.cs
使用MediatR;
命名空间Application.Application.Users.Commands.CreateUser
{
公共类CreateUserCommand:IRequest
{
公共字符串名{get;set;}
公共字符串LastName{get;set;}
公共字符串电子邮件地址{get;set;}
公共字符串密码{get;set;}
公共字符串ConfirmPassword{get;set;}
public int CompanyId{get;set;}
}
}
CreateUserCommandHandler.cs
使用MediatR;
使用Application.Domain.Entities.Identity;
使用Microsoft.AspNetCore.Identity;
使用系统线程;
使用System.Threading.Tasks;
使用制度;
命名空间Application.Application.Users.Commands.CreateUser
{
公共类CreateUserCommandHandler:IRequestHandler
{
私有只读用户管理器_UserManager;
公共CreateUserCommandHandler(UserManager UserManager)
{
_userManager=userManager;
}
公共异步任务句柄(CreateUserCommand请求、CancellationToken CancellationToken)
{
var entity=新用户
{
FirstName=request.FirstName,
LastName=request.LastName,
Email=request.EmailAddress,
用户名=request.EmailAddress,
CompanyId=request.CompanyId
};
var createUserResult=await _userManager.CreateAsync(实体、请求、密码);
if(createUserResult.successed)
{
返回实体Id;
}
抛出新异常(“未能创建用户”);
}
}
}
CreateUserCommandValidator.cs
使用FluentValidation;
命名空间Application.Application.Users.Commands.CreateUser
{
公共类CreateUserCommandValidator:AbstractValidator
{
公共CreateUserCommandValidator()
{
规则(v=>v.Password)
.Equal(v=>v.ConfirmPassword).WithName(“密码”).WithMessage(“密码不匹配”);
规则(v=>v.ConfirmPassword)
.Equal(v=>v.Password).WithName(“confirmPassword”).WithMessage(“密码不匹配”);
规则(v=>v.EmailAddress)
.NotEmpty().WithName(“电子邮件地址”).WithMessage(“需要电子邮件地址”)
.EmailAddress().WithName(“EmailAddress”).WithMessage(“无效电子邮件地址”);
RuleFor(v=>v.FirstName)
.NotEmpty().WithName(“名字”).WithMessage(“需要名字”);
规则(v=>v.LastName)
.NotEmpty().WithName(“lastName”).WithMessage(“需要姓氏”);
}
}
}
AuthenticationController.cs
使用System.Threading.Tasks;
使用Application.Application.Users.Commands.CreateUser;
使用Microsoft.AspNetCore.Mvc;
命名空间Application.WebUI.Controllers
{
公共类身份验证控制器:ControllerBase
{
[HttpPost]
公共异步任务寄存器([FromBody]CreateUserCommand)
{
返回Ok(Mediator.Send(command));
}
}
}

但是如何使它们成为单个请求的一部分呢?

首先,更改您的
GetCompanyByDomainHandler
,使其在未找到公司时不会引发异常。
找不到的公司不是例外,它是查询的结果。只需返回
null

现在,您可以获得查询的结果,对其进行操作(而不是尝试捕获)


您可以将其包装到另一个
处理程序中,或者将API操作方法视为“orchestrator”(我的首选项)

我通过使用MediatR行为功能,特别是RequestPresProcessBehavior来解决此问题,该功能允许在处理CreateUserCommand之前执行操作:

CreateUserCommandGetMatchingCompanyPreProcess.cs
使用系统线程;
使用System.Threading.Tasks;
使用应用程序。持久性;
使用MediatR;
使用MediatR.管道;
使用Microsoft.EntityFrameworkCore;
命名空间Application.Application.Users.Commands.Creat
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));
        }
    }
}
//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);
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;
        }
    }
}