# 引出`Result`模式 通常的方式 ``` public Customer GetCustomer(int customerId) { // more logic return customer; } public Customer CreateCustomer(string firstName, string lastName) { // more logic return customer; } ``` 在逻辑中,我们可能会判断`customerId`不存在,判断`lastName`没有提供,甚至当前用户是否有权限创建用户。 我们在处理正常和异常这两种情况。 `Result`模式用来对这两种情况进行封装,并且统一了返回结果的格式。 ``` public async Task> UpdateAsync(BlogCategory blogCategory) { if (Guid.Empty == blogCategory.BlogCategoryId) return Result.NotFound();//没有找到 var validator = new BlogCategoryValidator(); var validation = await validator.ValidateAsync(blogCategory); if (!validation.IsValid) { return Result.Invalid(validation.AsErrors());//验证异常 } var itemToUpdate = (await GetByIdAsync(blogCategory.BlogCategoryId)).Value; if (itemToUpdate == null) { return Result.NotFound(); } itemToUpdate.Update(blogCategory.Name, blogCategory.ParentId); return Result.Success(await _blogCategoryRepository.UpdateAsync(itemToUpdate)); } ``` 结果 ![ddd12](F:\SourceCodes\DDWiki\专题\后端\DDD\ddd12.png) # 接口 ``` [ApiController] [Route("mediatr/[controller]")] public class WeatherForecastController : ControllerBase { private readonly IMediator _mediator; private readonly ILogger _logger; public WeatherForecastController( IMediator mediator, ILogger logger) { _mediator = mediator; _logger = logger; } /// /// This uses a filter to convert an Ardalis.Result return type to an ActionResult. /// This filter could be used per controller or globally! /// /// /// [TranslateResultToActionResult]//把Result的结果转换成ActionResult的结果,可以定义我们自己的特性,这点特别重要 [HttpPost("Create")] public Task>> CreateForecast([FromBody] NewForecastCommand model) { // One might try to perform translation from Result to an appropriate IActionResult from within a MediatR pipeline // Unfortunately without having Result depend on IActionResult there doesn't appear to be a way to do this, so this // example is still using the TranslateResultToActionResult filter. return _mediator.Send(model); } public class NewForecastCommand : IRequest>> { [Required] public string PostalCode { get; set; } } public class NewForecastHandler : IRequestHandler>> { private readonly WeatherService _weatherService; public NewForecastHandler(WeatherService weatherService) { _weatherService = weatherService; } public Task>> Handle(NewForecastCommand request, CancellationToken cancellationToken) { var result = _weatherService.GetForecastAsync(new ForecastRequestDto { PostalCode = request.PostalCode }); return result; } } } ``` 在`WeatherService`中 ``` public class WeatherService { public WeatherService(IStringLocalizer stringLocalizer) { _stringLocalizer = stringLocalizer; } private static readonly string[] Summaries = new[] { "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" }; private IStringLocalizer _stringLocalizer; public Task>> GetForecastAsync(ForecastRequestDto model) { return Task.FromResult(GetForecast(model)); } public Result> GetForecast(ForecastRequestDto model) { if (model.PostalCode == "NotFound") return Result>.NotFound();//没有找到 // validate model if (model.PostalCode.Length > 10) { return Result>.Invalid(new List { new ValidationError { Identifier = nameof(model.PostalCode), ErrorMessage = _stringLocalizer["PostalCode cannot exceed 10 characters."].Value } });//模型验证失败 } // test value if (model.PostalCode == "55555") { return new Result>(Enumerable.Range(1, 1)//返回成功的结果 .Select(index => new WeatherForecast { Date = DateTime.Now, TemperatureC = 0, Summary = Summaries[0] })); } var rng = new Random(); return new Result>(Enumerable.Range(1, 5)//返回成功的结果 .Select(index => new WeatherForecast { Date = DateTime.Now.AddDays(index), TemperatureC = rng.Next(-20, 55), Summary = Summaries[rng.Next(Summaries.Length)] }) .ToArray()); } } ``` 如何影射`Result`结果呢? ``` public class TranslateResultToActionResultAttribute : ActionFilterAttribute { public override void OnActionExecuted(ActionExecutedContext context) { if (!((context.Result as ObjectResult)?.Value is IResult result)) return; if (!(context.Controller is ControllerBase controller)) return; //判断IResult的ResultStatus if (result.Status == ResultStatus.NotFound) context.Result = controller.NotFound(); if (result.Status == ResultStatus.Invalid) { foreach (var error in result.ValidationErrors) { // TODO: Fix after updating to 3.0.0 (context.Controller as ControllerBase)?.ModelState.AddModelError(error.Identifier, error.ErrorMessage); } context.Result = controller.BadRequest(controller.ModelState); } if (result.Status == ResultStatus.Ok) { context.Result = new OkObjectResult(result.GetValue()); } } } ``` `IResult` ``` public interface IResult { ResultStatus Status { get; } IEnumerable Errors { get; } List ValidationErrors { get; } Type ValueType { get; } Object GetValue(); } ``` `ResultStatus` ``` public enum ResultStatus { Ok, Error, Forbidden, Invalid, NotFound } ```