【问题标题】:How to surface the exception details to the user for resolution in a multi layered application如何在多层应用程序中向用户显示异常详细信息以便解决
【发布时间】:2012-09-25 17:15:14
【问题描述】:

我们的服务层中有以下代码,我们使用 EF 将新用户添加到数据库中。

    public User AddUser(User user)
    {
        using (var context = DataObjectFactory.CreateContext())
        {
            var userEntity = Mapper.Map(user);

            context.AddObject("UserEntities", userEntity);

            context.SaveChanges();

            return Mapper.Map(userEntity);
        }         
    }

这是由服务层通过这种方法调用的:

    public UserResponse GetSubmitUser(UserRequest request)
    {
        var response = new UserResponse(request.RequestId);

        var user = Mapper.FromDataTransferObject(request.User);

        response.User = Mapper.ToDataTransferObject(UserDao.AddUser(user));

        return response;
    }

在到达 AddUser 之前,我们会进行某些客户端和服务器验证,但是我不确定如何处理未处理的异常。

例如,如果 context.SaveChanges() 以某种方式抛出错误,我们如何将错误详细信息返回到表示层?

我正在考虑将 try / catch 放在 AddUser 中,如下所示:

    public User AddUser(User user)
    {
        try
        {
            using (var context = DataObjectFactory.CreateContext())
            {
                var userEntity = Mapper.Map(user);

                context.AddObject("UserEntities", userEntity);

                context.SaveChanges();

                return Mapper.Map(userEntity);
            }  
        }
        catch (Exception)
        {
            return ??
        }      
    }

但是 AddUser 的返回类型是 User 并且我不确定发生异常时应该返回什么。如果我可以将异常详细信息返回给 UserResponse 方法,我可以在我们的响应对象中填充一些字段来保存错误详细信息,但现在确定从 catch 部分中返回什么。

我试图了解如何识别应该在多层应用程序中捕获和处理的异常,一个具有数据访问层、服务层、表示层等的应用程序。主要是想弄清楚如何将异常详细信息呈现给用户进行解析。

谢谢

【问题讨论】:

    标签: c# entity-framework error-handling service-layer data-layer


    【解决方案1】:

    我认为“BusinessExceptions”是您正在寻找的东西。

    在 n 层应用程序中,很难从服务层获取或创建业务规则异常是很常见的。 我猜您正在尝试使用您的示例验证一些业务规则,也许用户名是有效的,或者它在数据库中尚不存在。

    对于此类问题,我总是使用 FaultExceptions 和自定义对象向客户端发送“可理解”的消息。这样我就可以区分系统异常、错误异常和业务规则异常(用户想知道的那些)。

    这就是我通常的做法。我有一个 BusinessFault 对象,其中包含将由客户端处理并在必要时向用户显示的业务规则的数据:

    [DataContract]
    public class BusinessFault
    {
        [DataMember]
        public BusinessFaultType Type { get; set; }
    
        [DataMember]
        public string Details { get; set; }
    
        [DataMember]
        public ErrorCode ErrorCode { get; set; }
    
        public BusinessFault(BusinessFaultType type, ErrorCode errorCode)
        {
            Type = type;
            ErrorCode = errorCode;
        }
    
        public BusinessFault(BusinessFaultType type, string details, ErrorCode errorCode)
            : this(type, errorCode)
        {
            Details = details;
        }
    
    }
    

    因此,每当我需要检查规则时,我都会像这样使用它(使用您的示例):

    public void AddUser(User user)
    {
    if(!IsValidUser(user))
     throw new FaultException<BusinessFault>(new BusinessFault(BusinessFaultType.Validation, "Username",ErrorCode.AlreadyExists); 
    
    using (var context = DataObjectFactory.CreateContext())
        {
            var userEntity = Mapper.Map(user);
    
            context.AddObject("UserEntities", userEntity);
    
            context.SaveChanges();
    
            return Mapper.Map(userEntity);
        } 
    }
    

    在我的服务合同中,我应该将我的 BusinessFault 指定为 knowntype。这样错误就会到达客户端:

        [OperationContract]
        [FaultContract(typeof(BusinessFault))]
        void AddUser(User user);                              
    

    所以在客户端,我只需要一个针对 BusinessFault 的 try-catch:

    try
    {
        //WebService call
    }
    catch (FaultException<BusinessFault> ex)
    {
    var exception = ex.ToBusinessExeption();  //I use extensions Methods to convert it in a custom Exception with the parameters I sent. 
                throw exception;
    }     
    

    关于这个有一篇很好的文章:http://www.c-sharpcorner.com/UploadFile/afenster/wcf-error-handling-and-faultexceptions/

    【讨论】:

    • 感谢您的详细解释。
    【解决方案2】:

    如果你打算对它做点什么,你只需要在你的底层捕获异常。例如,您可能会捕获它,写入异常日志,然后重新抛出它。

    至于返回值,在我看来,你不应该从多个地方返回。

    相反,我认为这样更好:

    User user = null;
    
    try
    {
       //Try to set up user
    }
    catch(Exception ex)
    {
       //Log exception
       throw;
    }
    
    return user;
    

    【讨论】:

      【解决方案3】:

      但 AddUser 的返回类型是用户,我不确定我是什么 发生异常时应该返回

      这取决于 - 通常,您不想返回值,但您会记录异常,然后调用 throw;(或者您将抛出自己的异常,即包装原始异常,您的选择)并将异常冒泡,直到您在某个地方捕获它,向用户显示好消息并记录它。

      如果您想向用户显示好消息(和/或在上层某处进行日志记录),请使用throw new ApplicationException("Failed to create user", ex); 这样您可以获得好消息,并使用堆栈跟踪冒泡异常,以便您可以了解并稍后解决问题。

      在某些情况下,您可以返回 null 并只记录异常。通常,当你不想冒泡异常时,如果它不是关键的,你不想因为它退出当前的代码路径,你会这样做。

      祝你好运!

      【讨论】:

        【解决方案4】:

        你不需要在数据访问层捕获异常,让它们自然抛出。规则是,如果您需要对其进行业务处理,您只需捕获异常。如果没有,那就顺其自然吧。

        因此,所有异常都会在应用程序的顶层捕获,以便记录故障排除。

        如果您的应用程序是 web,您可以使用一些支持日志处理程序的日志库,如 Elmah 来拦截请求级别。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2019-05-17
          • 2019-09-23
          • 2014-01-24
          • 1970-01-01
          • 2020-05-01
          • 1970-01-01
          相关资源
          最近更新 更多