简体   繁体   English

错误处理我应该抛出异常吗? 或者在源头处理?

[英]Error Handling Should I throw exception? Or handle at the source?

I have this sort of format 我有这种格式

asp.net MVC View -> Service Layer -> Repository. asp.net MVC视图 - >服务层 - >存储库。

So the view calls the service layer which has business/validation logic in it which in turns calls the Repository. 因此,视图调用服务层,其中包含业务/验证逻辑,而后者又调用存储库。

Now my service layer method usually has a bool return type so that I can return true if the database query has gone through good. 现在我的服务层方法通常有一个bool返回类型,所以如果数据库查询已经完成,我可以返回true。 Or if it failed. 或者如果失败了。 Then a generic message is shown to the user. 然后向用户显示通用消息。

I of course will log the error with elmah. 我当然会用elmah记录错误。 However I am not sure how I should get to this point. 但是我不确定我应该怎么做到这一点。

Like right now my Repository has void return types for update,create,delete. 就像现在我的Repository有更新,创建,删除的void返回类型。

So say if an update fails should I have a try/catch in my repository that throws the error, Then my service layer catches it and does elmah signaling and returns false? 所以说如果更新失败,我应该在我的存储库中有一个try / catch引发错误,然后我的服务层捕获它并执行elmah信令并返回false?

Or should I have these repository methods return a "bool", try/catch the error in the repository and then return "true" or "false" to the service layer what in turn returns "true" or "false" to the view? 或者我应该让这些存储库方法返回“bool”,尝试/捕获存储库中的错误,然后将“true”或“false”返回到服务层,然后又向视图返回“true”或“false”?

Exception handling still confuses me how handle the errors and when to throw and when to catch the error. 异常处理仍然让我感到困惑的是如何处理错误以及何时抛出以及何时捕获错误。

The rule of thumb I always use is: 我经常使用的经验法则是:

  • At low levels, throw when an operation cannot complete due to exceptional circumstances. 在低水平时,由于特殊情况而无法完成操作时抛出。
  • In middle layers, catch multiple exception types and rewrap in a single exception type. 在中间层中,捕获多个异常类型并重新包装在一个异常类型中。
  • Handle exceptions at the last responsible moment. 在最后一个负责任的时刻处理异常。
  • DOCUMENT! 文献!

Here's an example in pseudocode for a multi-layer ASP.NET MVC app (UI, Controller, Logic, Security, Repository): 以下是多层ASP.NET MVC应用程序(UI,Controller,Logic,Security,Repository)的伪代码示例:

  1. User clicks submit button. 用户点击提交按钮。
  2. Controller action is executed and calls into the Logic (business) layer. 执行控制器操作并调用逻辑(业务)层。
  3. Logic method calls into Security with the current User credentials 逻辑方法使用当前用户凭据调用Security
    • User is invalid 用户无效
      • Security layer throws SecurityException 安全层抛出SecurityException
      • Logic layer catches, wraps in LogicException with a more generic error message 逻辑层捕获,在LogicException中包含更通用的错误消息
      • Controller catches LogicException, redirects to Error page. Controller捕获LogicException,重定向到Error页面。
    • User is valid and Security returns 用户有效且安全性返回
  4. Logic layer calls into the Repository to complete action 逻辑层调用存储库以完成操作
    • Repository fails 存储库失败
      • Repository throws RepositoryException 存储库抛出RepositoryException
      • Logic layer catches, wraps in LogicException with a more generic error message 逻辑层捕获,在LogicException中包含更通用的错误消息
      • Controller catches LogicException, redirects to Error page. Controller捕获LogicException,重定向到Error页面。
    • Repository succeeds 存储库成功
  5. Logic layer returns 逻辑层返回
  6. Controller redirects to the Success view. Controller重定向到Success视图。

Notice, the Logic layer only throws a single exception type -- LogicException. 注意,Logic层只抛出一个异常类型 - LogicException。 Any lower-level exceptions that bubble up are caught, wrapped in a new instance of LogicException, which is thrown. 冒泡的任何低级异常都会被捕获,包含在抛出的LogicException的新实例中。 This gives us many advantages. 这给了我们很多好处。

First, the stack trace is accessible. 首先,可以访问堆栈跟踪。 Second, callers only have to deal with a single exception type rather than multiple exceptions. 其次,调用者只需要处理单个异常类型而不是多个异常。 Third, technical exception messages can be massaged for display to users while still retaining the original exception messages. 第三,可以按摩技术异常消息以显示给用户,同时仍保留原始异常消息。 Lastly, only the code responsible for handling user input can truly know what the user's intent was and determine what an appropriate response is when an operation fails. 最后,只有负责处理用户输入的代码才能真正知道用户的意图是什么,并确定操作失败时的适当响应。 The Repository doesn't know if the UI should display the error page or request the user try again with different values. 存储库不知道UI是否应显示错误页面或请求用户使用不同的值再次尝试。 The controller knows this. 控制器知道这一点。


By the way, nothing says you can't do this: 顺便说一句,没有什么说你不能这样做:

try
{
  var result = DoSomethingOhMyWhatIsTheReturnType();
}
catch(LogicException e)
{
  if(e.InnerException is SqlException)
  {
    // handle sql exceptions
  }else if(e.InnerException is InvalidCastException)
  {
    // handle cast exceptions
  }
  // blah blah blah
}

While returning an error (or success) code is often the better way, exceptions have one huge advantage over returning codes or silently suppressing errors: at least you can't just ignore them! 虽然返回错误(或成功)代码通常是更好的方法,但异常比返回代码或静默抑制错误有一个巨大的优势:至少你不能忽略它们!

Don't abuse exceptions for simple flow control - that would be the silliest thing to do. 不要滥用异常来进行简单的流量控制 - 这将是最愚蠢的事情。

But if a function of yours really runs into an "exceptional" problem, then definitely throw an execption. 但如果你的某个功能真的遇到了“特殊”问题,那么肯定会抛出一个执行。 The caller must then either handle it explicitly and thus know what's going on, or it'll bomb out on him. 然后调用者必须明确地处理它,从而知道发生了什么,或者它会轰炸他。

Just returning an error code is dangerous since the caller might just not bother inspecting the code and could possibly still go on - even if in your app's logic, there's really something wrong and needs to be dealt with. 只是返回一个错误代码是危险的,因为调用者可能只是不打扰检查代码并且可能仍然继续 - 即使在你的应用程序的逻辑中,确实有问题需要处理。

So: don't abuse exceptions, but if a real exception happens that requires the caller to do something about it, I would definitely recommend using that mechanism for signalling exceptional conditions. 所以:不要滥用异常,但是如果发生真正的异常需要调用者对它做一些事情,我肯定会建议使用该机制来发出异常情况。

As for handling exceptions: handle those that you can really deal with. 至于处理异常:处理那些你真正可以处理的异常。 Eg if you try to save a file and get a security exception, show the user a dialog asking for some other location to save to (since he might not have permissions to save to where he wanted to). 例如,如果您尝试保存文件并获得安全异常,请向用户显示一个对话框,询问要保存的其他位置(因为他可能没有权限保存到他想要的位置)。

However, exceptions you can't really deal with (what do you want to do about a "OutOfMemory exception", really?) should be left untouched - maybe a caller further up the call stack can handle those - or not. 但是,您无法真正处理的异常(您想要对“OutOfMemory异常”做什么,真的吗?)应该保持不变 - 也许调用堆栈中的调用者可以处理这些 - 或者不是。 Marc

I like to think of exception handling this way: You define your method signature, as to what you are expecting to do. 我喜欢以这种方式考虑异常处理:您定义方法签名,以及您希望做什么。 Now if you are not able to do that, then you must throw an exception. 现在,如果你无法做到这一点,那么你必须抛出异常。 So if you are expecting something to fail based on the input data that you have, (ignoring the ambient state), then your method signature must indicate whether an operation has succeeded or failed. 因此,如果您希望根据您拥有的输入数据(忽略环境状态)而失败,那么您的方法签名必须指示操作是成功还是失败。 But if your method is not expecting to fail based on the input you have (again, ignoring all the other ambient state), then an exception is in order when the method fails. 但是如果您的方法不希望基于您的输入(再次忽略所有其他环境状态)而失败,那么当方法失败时,异常就会顺序排列。

Consider these two APIs: 考虑这两个API:

int int.Parse(string integerValue); // In this case, the method will return int
                                    // or it will die! That means your data must be
                                    // valid for this method to function.

bool int.TryParse(string integerValue, out number); // In this case, we expect the data
                                                    // we passed in might not be fully
                                                    // valid, hence a boolean.

First of all there is no one way and there certainly isn't a perfect one so don't overthink it. 首先,没有一种方法,肯定没有一种完美的方式,所以不要过度思考它。

In general you want to use exceptions for exceptional cases (exceptions incur a performance overhead so overusing them especially in "loopy" situations can have a perf impact). 通常,您希望对异常情况使用异常(异常会导致性能开销,因此过度使用它们尤其是在“循环”情况下会产生性能影响)。 So let's say the repository cannot connect to the database server for some reason. 因此,假设存储库由于某种原因无法连接到数据库服务器。 Then you would use an exception. 然后你会使用一个例外。 But if the repository executes a search for some object by id and the object is not found then you would want to return null instead of throwing an exception saying that object with ID x doesn't exist. 但是如果存储库通过id执行搜索某个对象并且找不到该对象,那么您将希望返回null而不是抛出异常,表示ID不存在的对象。

Same thing for the validation logic. 验证逻辑也是如此。 Since it's validating it is assumed that sometimes the input won't validate so in that case it would be good to return false from the validation service (or perhaps a more complex type including some additional information as to why it didn't validate). 由于它正在验证,因此假设有时输入不会验证,因此在这种情况下从验证服务返回false是好的(或者可能是更复杂的类型,包括一些关于它为什么不验证的附加信息)。 But if the validation logic includes checking if a username is taken or not and it can't do this for some reason then you would throw an exception. 但是,如果验证逻辑包括检查是否使用了用户名,并且由于某种原因它无法执行此操作,那么您将抛出异常。

So say if an update fails should I have a try/catch in my repository that throws the error, Then my service layer catches it and does elmah signalling and returns false? 所以说如果更新失败,我应该在我的存储库中有一个try / catch引发错误,然后我的服务层捕获它并执行elmah信令并返回false?

Why would the update fail? 为什么更新会失败? Is there a perfectly fine reason for this happening that's part of the normal process? 有没有一个完美的理由发生这种情况,这是正常过程的一部分? Then don't throw an exception if it happens because of a strange reason (let's say something removed the record being updated before it was updated) then an exception seams logical. 然后如果由于一个奇怪的原因(假设在更新之前删除了正在更新的记录的某些内容),则不抛出异常,然后异常接合逻辑。 There really is no way to recover from this situation. 真的没有办法从这种情况中恢复过来。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM