如何处理使用 DTO 时的异常

本文关键字:DTO 异常 何处理 处理 | 更新日期: 2023-09-27 18:35:56

我有一个解决方案结构,其中合同(数据/服务等)与业务实体位于不同的项目中,并且我正在使用Automapper在第三个服务实现项目之间进行映射。

WCFProject.Service.BusinessLayer
WCFProject.Service.Contracts
WCFProject.Service.Impl

My ServiceImpl 引用了这两个其他项目,并且从 DataContract 到 BusinessEntity 的自动映射在这里完成,然后在 BusinessEntity 对象上调用正确的方法

现在,我想添加一些FaultContracts,然后在业务逻辑中使用它们来引发正确的异常。但是,如果我将它们添加到合同项目中(这是理想的,因为我想将所有合同放在一起),那么我需要从业务层到合同的引用才能在业务层中使用它们。如果可能的话,我想保持这些独立,只处理这两层之间的 DTO。这是否是我想要保持两个项目独立的有效保证?你也映射异常吗?或者有没有更好的方法来解决这个问题。

如何处理使用 DTO 时的异常

您的业务层应该不知道上述层。因此,它不知道您顶部有一个 wcf 层。抛出错误是来自 wcf 层的东西,在那里捕获你的异常并决定你想要做什么。业务异常可以映射到 wcf 错误,但如果具有 nullpointer 异常的连接,则只想给出一个一般错误,指出某些错误。

可以在此处找到服务行为中的处理/映射错误示例:WCF 异常处理

在问题中,您指定:

我想添加一些错误合约,然后在我的业务逻辑中使用它们来抛出正确的异常。

正如您正确识别的那样 - 这在服务的公共 API(服务、数据和错误协定)和业务逻辑之间引入了耦合。 理想情况下,您的业务逻辑应该与服务调用的事实无关,因此对协定程序集的引用令人不安。

合约程序集应列出客户端对服务的公开可用信息:

namespace Contracts
{
    [ServiceContract]
    interface IMyService
    {
        [OperationContract]
        [FaultContract(typeof(MyFaultContract))]
        [FaultContract(typeof(AnotherFaultContract))]
        void MyOperation();
    }
    [DataContract]
    public class MyFaultContract
    {
        [DataMember]
        public string Problem { get; set; }
    }
    [DataContract]
    public class AnotherFaultContract
    {
        [DataMember]
        public string Description { get; set; }
    }
}

与软件开发中的许多问题一样,您的问题可以通过间接层来解决。 尽管您在问题中指定了什么 - 您不希望将业务逻辑耦合到合约程序集。 不这样做的好处是显而易见的 - 它允许公共合同和"内部"业务逻辑独立发展。

下面显示了一个示例,其中服务实现用于将协定耦合到业务逻辑。 业务层中的异常映射到返回给客户端的错误合同:

namespace Service
{
    class MyService: IMyService
    {
        public void MyOperation()
        {
            try
            {
                var businessLogic = new BusinessLogic();
                businessLogic.DoOperation();
            }
            catch (KeyNotFoundException)
            {
                throw new FaultException<MyFaultContract>(new MyFaultContract
                {
                    Problem = "A key issue occurred in the service"
                });
            }
            catch (Exception)
            {
                throw new FaultException<AnotherFaultContract>(new AnotherFaultContract
                {
                    Description = "Something BAD happened in the service"
                });
            }
        }
    }
}

顺便说一句,值得仔细考虑您在客户端中公开的错误合同,以及当服务器端出现问题时客户端需要哪些信息。 公开太多有关服务异常的信息可能会带来安全风险。

如果您正在经历创建DTO层的麻烦,那么您可能希望保护外部世界免受域内部工作的影响。

同样,您应该保护外部世界免受域内可能出现的所有异常的影响。

恕我直言,您应该做捕获域异常的工作,决定您想要(或不希望)公开的内容,并将其映射到 DTO 异常格式中的正确错误。 例如,您可能不希望在域外部公开堆栈跟踪。

作为一般规则,我尝试只公开客户端可以处理的异常。 如果数据库关闭,那么客户端将如何处理它?所以,也许客户不需要那个SqlException,而是需要一个500 Internal Server Error