应该从服务层返回什么类型的结果?

比方说,我有一个创建post的PostsService

 public class PostsService : IPostsService { public bool Create(Post post) { if(!this.Validate(post)) { return false; } try { this.repository.Add(post); this.repository.Save(); } catch(Exception e) { return false; } } } 

这样做的问题是,如果在存储库操作期间抛出exception,则会被吞下。 Create()返回false ,消费者知道的所有内容都是没有添加Post ,但不知道原因

相反,我想到了一个ServiceResult类:

 public class ServiceResult { public bool Success { get; private set; } public Exception Exception { get; private set; } } 

这会是一个好的设计吗? 或者我甚至需要向消费者报告这些错误? 是否足以说“添加post时发生错误”。 然后在服务内部记录exception?

任何其他建议表示赞赏。

我认为这取决于原因的范围。 在我看来,一些失败原因不应该与消费者有关,这些应该记录在服务中。 另一方面,有些情况下应该告知消费者这些原因,因此除了记录它们之外,还应该以某种方式指出错误的原因。

例如,假设用户注册服务的一个非常常见的用例。 您很可能拥有一个标识用户的唯一属性(用户句柄,电子邮件等)。 这种唯一性必须由服务强制执行(也可能在数据库级别)。 现在,如果消费者试图注册用户并且由于违反了约束而失败,则应该通知消费者这个原因(因此它可以尝试其他用户处理)。 在大多数情况下,失败的validation都属于同一场景。

另一方面,如果存在某种内部数据库问题,则不应告知消费者细节(因为这是服务的专有责任;并且消费者无论如何都不能做任何事情)。

考虑到这一点,我会说在许多情况下返回布尔值是不够的。 所以有一些ServiceResult类型听起来不是一个坏主意。 我不确定我是否会包含Exception 。 但也许您可以创建某种特定于您的服务的ServiceExpection。 有时错误代码对此也很好。

是的, ServiceResult类是一个好主意,但我不会包含exception,有时你想报告错误,但你不想/需要创建/抛出/捕获exception,而是我会包含一个集合错误消息或指示出错的枚举类型,如MembershipCreateStatus 。

此外,不要捕获exception ,它可以使bug很难找到。

(免责声明:根据我的经验,我在环境之间暴露/消费服务方面做了很多工作,其中像WCF这样的内置function并不总是有用的,并且人们并不总是能够控制服务于服务的客户端。)

我的服务设计越来越多,我已经转向了请求/响应系统。 (实际上,我在这方面大量使用了agatha-rrsl库。)在该设计中,我有一个BaseRequest对象和一个BaseResponse对象,所有请求和响应类型都从该对象inheritance。

继续你的想法, ServiceResult类将作为这样一个BaseResponse的开始。 它不一定需要实际的例外,但我倾向于包括的一些事情是:

  1. bool指示任何请求成功或失败,甚至是成功返回实际结果的bool
  2. 一个IList<>自定义Message对象,它们本身包含某种消息类型(Info,Warn,Error等)以及消息文本(可能还有一个用于UI显示需要的用户友好消息文本),如果相关的堆栈跟踪等

更进一步,我还想在BaseRequest包含一些内容,例如原始用户,主机等。

这个想法是服务本身不应该抛出原始exception。 任何使用该服务的人都应该总是期望得到有效的响应,即使该响应表明某种类型的失败。 它应该期望每次都能返回一些基本信息并知道如何处理这些信息。

请记住,面向服务的体系结构的一个主要优点是解耦系统组件。 调用层必须知道的服务越多,层的耦合就越紧密。 遵循这个逻辑,最好让调用层尽可能少地了解错误,所有需要知道的是服务调用失败。

调用层真的需要知道服务失败的原因吗? 它能真正做些什么呢? 是否只是为了向用户显示更好的信息? 在这种情况下,用户真的关心细节吗? 很容易认为用户想知道更多,只是因为你和开发人员一样。 但是开发人员应该从日志而不是最终用户消息中获取有关错误消息的信息。

捕获所有exception是一个非常糟糕的主意。 你只能抓住你可以合理处理的东西。 你无法处理所有exception,所以为什么要抓住它? 为了防止堆栈跟踪?

如果你不希望你的用户看到一个可怕的堆栈跟踪,我会得到它,但你确实希望它死掉并且死得很厉害以至于某些东西没有被破坏。

假设服务引发OutOfMemoryException,你刚刚抓住它,并且不知道你的应用程序是否像筛子一样泄漏内存或者是否正在分配大型对象。

那是件坏事。

如果您不希望您的用户看到错误,那么您应该更改您的捕获:

 catch (Exception e) { //send it to yourself, log it. Just don't swallow it. LogErrorAndEmailDevTeam(e); throw new SaveFailedException("We're sorry. \n" + "It's not your fault, but something bad happened.\n\n" + "We've been notified and will fix it as soon as possible."); } 

但更好的办法是让应用程序死于可怕的死亡而不是捕获exception,以便在出现问题时迅速知道。

您不希望您的应用程序继续处于损坏状态,但这就是catch (Exception)作用。 你真的相信你可以处理抛出的任何东西吗?

如果你正在使用WCF,我建议不要使用ServiceResult ,或类似的东西来处理SOA层中的exception。

您应该为您的方法定义FaultContract 。 这将允许使用代码来了解可抛出的exception类型,并使用传统的try/catch块相应地处理它们。

此StackOverflow问题具有故障合同的完整实现 。

您仍然可以让您的前端简单地说“发生了错误”,但如果将来需要,使用错误合同将允许在代码的UI层上更好地处理错误。