【问题标题】:Exception management in an ASP.NET MVC Application using Enterprise Library [closed]使用企业库的 ASP.NET MVC 应用程序中的异常管理 [关闭]
【发布时间】:2012-01-05 18:54:18
【问题描述】:

我需要一些有关异常管理最佳实践的信息。不是如何编码,有很多关于它的信息,但更像是在不同的层中如何处理它(我们在 .NET 中有 MVC 应用程序),要包装什么等等。

只是理论信息。我们想使用企业库。

编辑

感谢您的回答,但我正在寻找更多架构信息。我已经阅读了很多网络上的文章,比如 codeproject 上的这篇文章,msdn 上的一些文章等等,但大多数都是在编码级别解决这个问题 :(

我确定我会使用企业库,但我必须弄清楚场景(我希望我说的 id 正确) 这个挺好用的

【问题讨论】:

  • 这是一个非常广泛的问题......
  • I'm looking more for architectural information 嗯?不应该这样计划异常处理。捕获您可以处理的异常并让所有其他人独自一人。很简单。
  • 这个架构方案是一个有趣的想法,但是 IMO 它有两个缺点: 1. 它会影响性能 - 单个异常会导致另外两个异常被抛出和三个日志消息吗? 2. 很复杂。我坚信越简单越好。

标签: c# .net asp.net-mvc exception-handling enterprise-library


【解决方案1】:

我更喜欢在 global.asax 的“Application_Error”中使用错误记录。这使您可以捕获 Web 应用程序中发生的任何异常。当从不可靠的来源(例如数据库访问)获取输入时,我也会使用 try-catch。

我记得这是一篇精彩的读物: http://www.codeproject.com/KB/architecture/exceptionbestpractices.aspx

关于我的产品场景的一点点: 我们让 IIS 服务器动态地上下移动,以适应系统负载并节省开支。我将错误记录到专用数据库,因为我无法从已关闭的服务器获取日志。从多台服务器收集日志也很不舒服。我记录了 e.ToString(),因为它同时保存了消息和堆栈跟踪。另一个巧妙的技巧:我对 e.ToString() 执行 md5 哈希并将其保存在错误表的单独列中。如果这个散列已经存在,我增加一个计数器。 IE。我将错误分组以使我能够轻松了解频率。这还允许您在登录后将用户重定向到您选择的错误页面。我这样做:

Response.Redirect("ErrorPage.html?e=" + errorHash);

在 html 文件中,我从查询字符串中显示错误的哈希值。

【讨论】:

    【解决方案2】:

    我写了几篇关于异常处理的博客文章。你可以在这里找到它们:http://blog.gauffin.org/tag/exceptions/

    我的基本意思是:

    不要捕获该异常!

    了解在大多数情况下您不应该捕获异常非常重要,除非您真的可以处理它们(或在顶层)。

    至于 ASP.NET MVC,我在这里写过处理错误的正确方法:http://blog.gauffin.org/2011/11/how-to-handle-errors-in-asp-net-mvc/

    【讨论】:

      【解决方案3】:

      如果您想要一个应用程序范围的错误记录工具,我会指导您查看elmah。基本上,它是一个不显眼的 IIS 插件,可为您的应用程序提供异常处理。它还提供了许多查看这些异常的方法。

      如果您想要一个可用于异常报告和应用程序日志记录的通用日志框架,请查看 log4net。

      除此之外,真正的问题是当异常发生时您需要记录什么。对于初学者,我会记录所有必要的信息,以重现导致错误的事件过程。

      每个应用程序/公司都不同,因此请与您的团队、同事交谈,并在此基本规则之上讨论您还需要什么。

      【讨论】:

        【解决方案4】:

        可以为每个 dll 甚至每个类创建自定义异常类。对于每个抛出的自定义异常,创建一个自定义 Id 以及用于跟踪的描述以及用于控制向用户显示的消息的描述。 ID 或 ID 范围决定了向用户显示的友好消息。通过这种方式,您可以确定已捕获和处理的异常、未捕获和未处理的异常、自定义异常,还可以跟踪、分类和查找引发异常的代码等等。要将日志记录添加到组合中,您可以使用 log4net 或 common.logging(与 Entlib 一起使用)。这使得日志记录变得容易,因为它易于使用和实现,并且内置了用于记录所有有用信息的异常的功能,并且可以在应用程序范围内使用。

        添加: 我想我可能把 Base Exception Class 弄错了,你会注意到 DebugFormat 和 ErrorFormat,这是我们修改后的 log4net 代码,但我认为你明白了。

        namespace Playing.Service
        {
            public class UserService
            {
                private static readonly ILog log = LogManager.GetLogger(typeof(UserService));
        
                public void SaveUser(string username, string firstName, string lastName)
                {
                    try
                    {
                        Playing.Repository.UserRepository repository = new Repository.UserRepository();
        
                        repository.SaveUser(username, firstName, lastName);
        
                        log.DebugFormat("Saved User Info");
                    }
                    catch (Repository.RepositoryException rex)
                    {
                        log.ErrorFormat("Repository Could Not Save User Information: {0}\n Error Message: {1}\nStack Trace: {2}", rex.Message, rex, rex.StackTrace);
                        throw new ServiceException(rex.Message, 2400);
                    }
                    catch (Exception ex)
                    {
                        log.ErrorFormat("Could Not Save User Information: {0}\n Error Message: {1}\nStack Trace: {2}", ex.Message, ex, ex.StackTrace);
                        throw new ServiceException(ex.Message, 12400);
                    }
                }
            }
        
        
            public class ServiceException : Playing.Common.BaseException
            {
                public ServiceException(string errorMessage) : base(errorMessage) {}
                public ServiceException(string errorMessage, int errorID) : base(errorMessage, errorID) {}
                public ServiceException(string errorMessage, params string[] args) : base(errorMessage, args) { }
            }
        }
        
        namespace Playing.Repository
        {
            public class UserRepository
            {
                private static readonly ILog log = LogManager.GetLogger(typeof(UserRepository));
        
                public void SaveUser(string username, string firstName, string lastName)
                {
                    try
                    {
        
                        //Save data in Database
                        log.DebugFormat("Saved User Info Into Databse");
        
                    }
                    catch (Exception ex)
                    {
                        log.ErrorFormat("Could Not Save User Information: {0}\n Error Message: {1}\nStack Trace: {2}", ex.Message, ex, ex.StackTrace);
                        throw new RepositoryException(ex.Message, 3400);
                    }
                }
            }
        
        
            public class RepositoryException : Playing.Common.BaseException
            {
                public RepositoryException(string errorMessage) : base(errorMessage) {}
                public RepositoryException(string errorMessage, int errorID) : base(errorMessage, errorID) {}
                public RepositoryException(string errorMessage, params string[] args) : base(errorMessage, args)  {}
            }
        }
        
        namespace Playing.Common
        {
            public class BaseException : Exception
            {
                public BaseException(string errorMessage) : base(errorMessage) {}
        
                public BaseException(string errorMessage, int errorID) : base(errorMessage) 
                { 
                    StringBuilder error = new StringBuilder();
        
                    error.Append("(");
                    error.Append(errorID);
                    error.Append("): ");
                    error.Append(errorMessage);
        
                    errorMessage = error.ToString();
                }
        
                public BaseException(string errorMessage, params string[] args) : base(errorMessage) 
                {
                    errorMessage = string.Format(errorMessage, args);
                }
            }
        }
        

        【讨论】:

        • 您是否真的使用过这个方案来解决生产中的实际问题?它看起来很复杂且容易出错。
        • 是的,文本使它听起来很难和复杂,但是您所做的只是创建自己的例外。通过创建您自己的例外,您可以添加对您最有用的任何信息,例如错误 ID 和/或消息 ID,其名称和命名空间对于 conetxt 是唯一的。然后,您可以捕获自己的通用异常,然后以不同的方式处理它们。无论如何,您都会捕获,记录并重新抛出异常,为什么不以更有用的方式抛出自己的异常。当团队中有数百个代码文件并实施 IOC 时,对此的需求变得更加清晰。
        • 实际上,catch、log 和 rethrow 是一个可怕的想法。在记录之前让异常传播到顶部,您将获得更清晰的异常跟踪。
        • -1:更糟。当您抛出一个新异常时,您不会将捕获的异常作为 InnerException 传递。非常糟糕。
        • 实际上在我上面添加的代码中,这可以很好地追溯到源头。特别是如果您让 log4net 将此数据存储到数据库中。您可以添加线程 ID 或会话 ID 以帮助将它们重新组合在一起。只是一个强大的工作环境。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-10-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多