.NET C#中Repository模式的自定义错误返回问题
.NET C# Repository模式下服务层错误区分的推荐方案
针对你遇到的服务层返回null无法区分错误类型的问题,以下是两种工业界常用的优雅解决方案:
方案一:自定义业务异常+全局异常处理
通过定义特定业务异常,服务层抛出对应异常,配合全局过滤器统一处理,避免控制器冗余代码。
1. 定义业务异常类
// 账户不存在异常 public class AccountNotFoundException : Exception { public AccountNotFoundException(string message) : base(message) { } } // 余额不足异常 public class InsufficientBalanceException : Exception { public InsufficientBalanceException(string message) : base(message) { } }
2. 服务层抛出对应异常
public async Task<Account> WithdrawAsync(Guid accountId, decimal amount) { var account = await _accountRepository.GetByIdAsync(accountId); if (account == null) { throw new AccountNotFoundException($"账户 {accountId} 不存在"); } if (account.Balance < amount) { throw new InsufficientBalanceException($"账户 {accountId} 余额不足,当前余额:{account.Balance}"); } account.Balance -= amount; await _accountRepository.UpdateAsync(account); return account; }
3. 全局异常过滤器处理
在Program.cs中注册全局过滤器:
builder.Services.AddControllers(options => { options.Filters.Add<GlobalExceptionFilter>(); }); // 全局异常处理过滤器 public class GlobalExceptionFilter : IExceptionFilter { public void OnException(ExceptionContext context) { var (statusCode, response) = context.Exception switch { AccountNotFoundException ex => (StatusCodes.Status404NotFound, new { Message = ex.Message }), InsufficientBalanceException ex => (StatusCodes.Status400BadRequest, new { Message = ex.Message }), _ => (StatusCodes.Status500InternalServerError, new { Message = "服务器内部错误" }) }; context.Result = new ObjectResult(response) { StatusCode = statusCode }; context.ExceptionHandled = true; } }
方案二:Result模式(自定义结果类)
通过封装包含状态、数据、错误信息的通用结果类,显式返回操作状态,适合需要更精准控制返回逻辑的场景。
1. 定义通用Result类和错误枚举
public enum BusinessErrorType { None, AccountNotFound, InsufficientBalance } public class Result<T> { public bool IsSuccess { get; } public T Data { get; } public string ErrorMessage { get; } public BusinessErrorType ErrorType { get; } private Result(bool isSuccess, T data, string errorMessage, BusinessErrorType errorType) { IsSuccess = isSuccess; Data = data; ErrorMessage = errorMessage; ErrorType = errorType; } // 成功结果构造方法 public static Result<T> Success(T data) => new(true, data, null, BusinessErrorType.None); // 失败结果构造方法 public static Result<T> Failure(string message, BusinessErrorType errorType) => new(false, default, message, errorType); }
2. 服务层返回Result对象
public async Task<Result<Account>> WithdrawAsync(Guid accountId, decimal amount) { var account = await _accountRepository.GetByIdAsync(accountId); if (account == null) { return Result<Account>.Failure($"账户 {accountId} 不存在", BusinessErrorType.AccountNotFound); } if (account.Balance < amount) { return Result<Account>.Failure($"账户 {accountId} 余额不足,当前余额:{account.Balance}", BusinessErrorType.InsufficientBalance); } account.Balance -= amount; await _accountRepository.UpdateAsync(account); return Result<Account>.Success(account); }
3. 控制器处理Result
[HttpPost("withdraw")] public async Task<IActionResult> Withdraw(Guid accountId, decimal amount) { var result = await _accountService.WithdrawAsync(accountId, amount); if (!result.IsSuccess) { return result.ErrorType switch { BusinessErrorType.AccountNotFound => NotFound(result.ErrorMessage), BusinessErrorType.InsufficientBalance => BadRequest(result.ErrorMessage), _ => StatusCode(StatusCodes.Status500InternalServerError, result.ErrorMessage) }; } return Ok(result.Data); }
方案对比
- 自定义异常方案:代码简洁,适合业务错误属于“异常场景”的情况,全局处理避免重复代码。
- Result模式:更显式,无需依赖异常机制,适合需要精准控制返回逻辑的场景,并非不良实践,很多成熟.NET框架(如MediatR、ABP)都有类似实现。
不建议在控制器中逐个方法写try-catch,会导致代码冗余,全局异常处理或Result模式是更优选择。
内容的提问来源于stack exchange,提问作者user3265447
相关产品推荐
相关产品推荐

