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

C# 应该从服务层返回什么类型的结果?,c#,architecture,domain-driven-design,C#,Architecture,Domain Driven Design,例如,假设我有一个PostsService,它创建了一个post: public class PostsService : IPostsService { public bool Create(Post post) { if(!this.Validate(post)) { return false; } try { this.repository.Ad

例如,假设我有一个
PostsService
,它创建了一个post:

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;
        }
    }
}
这样做的问题是,如果在存储库操作期间抛出异常,它就会被吞没
Create()
返回
false
,消费者只知道没有添加
Post
,但不知道原因

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

public class ServiceResult
{
    public bool Success { get; private set; }
    public Exception Exception { get; private set; }
}
这是一个好的设计吗?或者我甚至需要向消费者报告这些错误吗?只说“添加帖子时发生错误”,然后将异常记录在服务内部,这样就足够了吗


任何其他建议都非常感谢。

如果您使用的是WCF,我建议您不要使用
ServiceResult
,或任何类似的方法来处理SOA层中的异常

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

这个


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

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

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

另一方面,如果存在某种内部DB问题,则不应将细节告知消费者(因为这是服务的专属责任;而消费者无论如何也不能对此做任何事情)

考虑到这一点,我认为在很多情况下返回布尔值是不够的。因此,使用某种
servicesult
类型听起来不是个坏主意。不过,我不确定是否会包含
异常。但也许您可以创建某种特定于您的服务的ServiceExpection。有时,错误代码也适用于此。

(免责声明:根据我的经验,我在环境间公开/使用服务方面做了很多工作,在这些环境中,WCF之类的内置功能并不总是有用的,也不总是能够控制使用服务的客户端。)

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

继续您的想法,
servicesult
类将作为这样一个
BaseResponse
的开始。它不一定需要实际的异常,但我倾向于包括以下几点:

  • 表示任何请求成功或失败的
    bool
    ,即使是成功返回实际结果的请求
  • 自定义
    消息
    对象的
    IList
    ,这些对象本身包含某种类型的消息(信息、警告、错误等)以及消息文本(可能还有一个用于UI显示需要的附加用户友好消息文本)、堆栈跟踪(如果相关)等
  • 更进一步,我还希望在
    BaseRequest
    中包含一些内容,例如原始用户、主机等


    其思想是服务本身不应该抛出原始异常。无论是谁在使用该服务,都应该始终期望得到有效的响应,即使该响应指示某种类型的故障。它应该期望每次都会返回一些基本信息,并知道如何处理这些信息。

    是的,ServiceResult类是个好主意,但我不会包含异常,有时您希望报告错误,但不希望/需要创建/抛出/捕获异常,相反,我会包含一组错误消息或一个枚举类型,指示出了什么地方出了问题,比如


    另外,不要捕捉,它会使bug难以发现。

    请记住,面向服务的体系结构的一个关键好处是将系统的组件解耦。调用层对服务的了解越多,层之间的耦合就越紧密。按照这个逻辑,最好让调用层尽可能少地了解错误,它需要知道的只是服务调用失败


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

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

    如果你不想让你的用户看到一个可怕的堆栈跟踪,我明白了,但你确实希望它死掉,死掉得可怕,所以
    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.");
    }