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)
这两种方式都是有效的。
使用异常还是使用返回值null来指示不存在的记录是不同的重点。
存在异常以表示错误状态(发生了异常的事情)。catch-handler 中的代码专注于如何处理错误而不是包含业务逻辑。
如果您返回,null那么它将是您模型中的正常和“非异常”状态。
因此,null如果它可能发生在您的域中,则返回该记录不存在(根据我的经验,这是最常见的情况)。如果您希望记录存在但不存在,则抛出exception.
我不同意另一个答案。对于 GetyById 方法,我不会说返回 null 而不是抛出,因为您可能会认为可能不存在具有所请求 id 的记录是“预期”的。这种“特殊情况下的例外”虽然经常被提及,但我并不认为这是考虑该方法契约的最佳方式。理想情况下,API 应该具有语义意义。
相反,我建议每当该方法无法执行所要求的操作时就抛出异常。因此,如果系统中不存在具有所请求 ID 的此类记录,则 GetById 方法应该抛出异常。Find 方法可能应该返回一个枚举,如果没有记录与给定的条件匹配,该枚举当然可以为空。
具有 FindById 方法的 API 让我觉得很奇怪;如果您为 API 提供 ID,则意味着调用者可能已在之前的 API 调用中了解了该 ID,因此 API 不需要“查找”已知存在的记录。它应该提供一种直接通过 id 获取记录的方法。相反,“查找”应该用于在不确定记录是否存在时查找记录,并使用其他一些条件。
考虑到 Web 服务调用,我将使用调用 GetById 方法的服务,因为 Web 服务调用者也以某种方式了解了 ID。如果结果证明 id 不存在,则库可以抛出 RecordNotFoundException,这会导致服务调用返回 404。