如果在数据库中找不到记录,则返回 (RecordNotFound) 异常或 null?

bro*_*and 6 c# exception-handling

在处理数据库中未找到的记录时,我不确定什么是首选方法。编写返回 null 的 Find 方法或返回的 Get 方法更好RecordNotFoundException吗?

[AuthenticateFilter(UsernameAndSecretKey)]
[Route("api/v1/activities/emails/{id}")]
[HttpGet]
public IHttpActionResult GetEmailActivity(int id)
{
  try
  {
    // business logic service, could use only db service but this way we can do unit tests (just fill bl service method with fake objects)
    var service = new EmailActivityBlService();

    // 1. use Find method which returns null in case record with provided id does not exist in db
    var model = service.FindActivity(id);
    if( model != null )
      return Ok(model);
    return NotFound();  

   // 2. or is this approach better
   // throws RecordNotFoundException in case row by id is not found in database
   return Ok(service.GetActivity(id));
  }
  catch(RecordNotFoundException e) { return NotFound(); }
  catch(Exception e) { return InternalServerError(e); }
}
Run Code Online (Sandbox Code Playgroud)

EmailActivityBlService 有下一个代码,以防有人感兴趣(仅显示重要部分):

private EmailActivityDbService _dbService;

public EmailActivityModel GetActivity(int id)
{
  var model = this._dbService.GetActivity(id);
  if( model == null )
    throw new RecordNotFoundException(); // I suppose System.Data.ObjectNotFound is also suitable
  return model;
}

public EmailActivityModel FindActivity(int id)
{
  // typical entity framework query
  // using(var context = new ..) { return contect.EmailActivity.Where()..SingleOrDefault().ConvertToModel();
  return this._dbService.GetActivity(id);
}
Run Code Online (Sandbox Code Playgroud)

更新

与我的同事交谈后,我们决定采用此解决方案。至于为什么 GetActivity 返回 null 而不是抛出异常,我更喜欢rboe 的回答:

因此,如果它可能发生在您的域中,则返回 null,该记录不存在(根据我的经验,这是最常见的情况)。如果您希望记录存在但不存在,则抛出异常是有效的。

[AuthenticateFilter(UsernameAndSecretKey)]
[Route("api/v1/activities/emails/{id}")]
[HttpGet]
public IHttpActionResult GetEmailActivity(int id)
{
   var service = new EmailActivityBlService();
   var model = service.GetActivity(id); // returns null in case activity is not found
   if( model != null )
     return Ok(model);
   return NotFound();
}
Run Code Online (Sandbox Code Playgroud)

我们在方法中避免了任何 try-catch 并在发生异常时放置全局过滤器:

文件:App_Start\WebApiConfig.cs

public class WebApiExceptionFilter : ExceptionFilterAttribute
{
  public override void OnException(HttpActionExecutedContext actionExecutedContext)
  {
    actionExecutedContext.Response = actionExecutedContext.Request.CreateErrorResponse(HttpStatusCode.InternalServerError, actionExecutedContext.Exception.Message, actionExecutedContext.Exception);
  }
}
Run Code Online (Sandbox Code Playgroud)

Ral*_*ing 6

这两种方式都是有效的。

使用异常还是使用返回值null来指示不存在的记录是不同的重点。

存在异常以表示错误状态(发生了异常的事情)。catch-handler 中的代码专注于如何处理错误而不是包含业务逻辑。

如果您返回,null那么它将是您模型中的正常和“非异常”状态。

因此,null如果它可能发生在您的域中,则返回该记录不存在(根据我的经验,这是最常见的情况)。如果您希望记录存在但不存在,则抛出exception.


And*_*ndy 5

我不同意另一个答案。对于 GetyById 方法,我不会说返回 null 而不是抛出,因为您可能会认为可能不存在具有所请求 id 的记录是“预期”的。这种“特殊情况下的例外”虽然经常被提及,但我并不认为这是考虑该方法契约的最佳方式。理想情况下,API 应该具有语义意义。

相反,我建议每当该方法无法执行所要求的操作时就抛出异常。因此,如果系统中不存在具有所请求 ID 的此类记录,则 GetById 方法应该抛出异常。Find 方法可能应该返回一个枚举,如果没有记录与给定的条件匹配,该枚举当然可以为空。

具有 FindById 方法的 API 让我觉得很奇怪;如果您为 API 提供 ID,则意味着调用者可能已在之前的 API 调用中了解了该 ID,因此 API 不需要“查找”已知存在的记录。它应该提供一种直接通过 id 获取记录的方法。相反,“查找”应该用于在不确定记录是否存在时查找记录,并使用其他一些条件。

考虑到 Web 服务调用,我将使用调用 GetById 方法的服务,因为 Web 服务调用者也以某种方式了解了 ID。如果结果证明 id 不存在,则库可以抛出 RecordNotFoundException,这会导致服务调用返回 404。