用于业务规则验证的引发UserFriendlyException和异常处理的替代方法

时间:2018-07-24 08:02:45

标签: validation exception-handling aspnetboilerplate business-rules service-layer

考虑引发异常的代价,另一种方法是这样的:

public interface IValidationDictionary
{
    void AddError(string key, string message);
    bool IsValid { get; }
}

public class ModelStateWrapper : IValidationDictionary
{
    private ModelStateDictionary _modelState;

    public ModelStateWrapper(ModelStateDictionary modelState)
    {
        _modelState = modelState;
    }

    public void AddError(string key, string errorMessage)
    {
        _modelState.AddModelError(key, errorMessage);
    }

    public bool IsValid
    {
        get { return _modelState.IsValid; }
    }
}
public interface IApplicationService
{
    void Initialize(IValidationDictionary validationDictionary);
}
public interface IUserService : IApplicationService
{
    Task CreateAsync(UserCreateModel model);
}
public class UserService : IUserService
{
    private readonly IUnitOfWork _uow;
    private IValidationDictionary _validationDictionary;

    public UserService(IUnitOfWork uow)
    {
        _uow = uow ?? throw new ArgumentNullException(nameof(uow));
    }

    public void Initialize(IValidationDictionary validationDictionary)
    {
        _validationDictionary = validationDictionary ?? throw new ArgumentNullException(nameof(validationDictionary));
    }

    public Task CreateAsync(UserCreateModel model)
    {
        //todo: logic for create new user

        if (condition)
            //alternative: throw new UserFriendlyException("UserFriendlyMessage");
            _validationDictionary.AddError(string.Empty, "UserFriendlyMessage");

        if (other condition)
            //alternative: throw new UserFriendlyException("UserFriendlyMessage");
            _validationDictionary.AddError(string.Empty, "UserFriendlyMessage");
    }
}

public class UsersController : Controller
{
    private readonly IUserService _service;

    public UsersController(IUserService service)
    {
        _service = service ?? throw new ArgumentNullException(nameof(service));
        _service.Initialize(new ModelStateWrapper(ModelState));
    }

    [HttpPost]
    public async Task<IActionResult> Create([FromForm]UserCreateModel model)
    {
        if (!ModelState.IsValid) return View(model);

        await _service.CreateAsync(model);

        //todo: Display ModelState's Errors
    }
}

考虑输入验证(如验证DTO)与业务规则验证之间的区别 https://ayende.com/blog/2278/input-validation-vs-business-rules-validation

  

对我来说,输入验证与验证用户输入有关。一些   人们认为“名称一定不能为空”是我的业务规则   作为输入验证。业务规则验证更为复杂,   因为对我来说,业务规则不是“名称不能为空”,所以   系统中需要采取措施的状态的定义。这是   业务规则的定义:

     

订单应在30天内支付,有效期为   扩展到最大三倍。

是否存在发送一些在应用程序服务方法的逻辑之间出现的业务规则验证错误消息的想法

1 个答案:

答案 0 :(得分:0)

Another approach

public class Result
{
    public bool Success { get; private set; }
    public string Error { get; private set; }
    public bool Failure { /* … */ }
    protected Result(bool success, string error) { /* … */ }
    public static Result Fail(string message) { /* … */ }
    public static Result<T> Ok<T>(T value) {  /* … */ }
}

public class Result<T> : Result
{
    public T Value { get; set; }
    protected internal Result(T value, bool success, string error)
        : base(success, error)
    {
        /* … */
    }
}

该方法是一个命令,它不会失败:

public void Save(Customer customer)

该方法是一个查询,它不会失败:

public Customer GetById(long id)

该方法是命令,可能会失败:

public Result Save(Customer customer)

该方法是一个查询,它可能会失败

public Result<Customer> GetById(long id)