如何在具有多个图层的程序中重新抛出异常?

时间:2012-12-27 19:19:56

标签: c# entity-framework exception exception-handling

我有一个MVC EF5设置,有类:

  • Program - 这是控制器
  • UserInterface - 这是视图,负责显示和提示数据。
  • DataAccess - 模型,这可以在我的EF模型类中创建,读取,更新和删除数据

当DataAccess类尝试对我的数据库执行CRUD操作时,如果它捕获错误,则需要处理它,我的UserInterface类需要向用户打印消息,如果需要则报告任何错误。因此,当发生错误时,它需要先通过程序类,然后再通过UserInterface类,因为数据层不应直接与表示层通信。

有人建议我不要将异常传递给函数或将其返回给调用函数,但是我应该“向上面的层添加一个更简单的新例外”。所有关于异常的讨论都让我感到困惑,因为我对异常的体验仅限于这种格式:

try
{
    // stuff
}
catch (exception ex)
{
    console.writeline(ex.ToString());
}

我已经做了一些自己的研究,试图找到这个问题的答案,我已经学到了一些东西,但不知道如何把它们放在一起:

我了解到:

  • throw;重新抛出异常并保留堆栈跟踪
  • throw ex抛出一个现有的异常,例如catch块中捕获的异常。并重置堆栈跟踪。
  • 有一个名为Exception.StackTrace的属性。我知道每次抛出异常时,调用堆栈中的帧都会记录到Exception.StackTrace属性中。

但是,我不知道在哪里放置我的try / catch块以利用重新抛出

是否类似于以下代码?或者我错过了关于它是如何工作的观点?

编辑:(增加了一点,以便对其他人的猜测有所了解)

        void MethodA()  
        {
            try
            {
                MethodB();
            }
            catch (MyExceptionType ex)
            {
                // Do stuff appropriate for MyExceptionType
                throw;
            }
        }
        void MethodB()  
        {
            try
            {
                MethodC();
            }
            catch (AnotherExceptionType ex)
            {
                // Do stuff appropriate for AnotherExceptionType
                throw;
            }
        }
        void MethodC()  
        {
            try
            {
                // Do Stuff
            }
            catch (YetAnotherExceptionType ex)
            {
                // Do stuff appropriate for YetAnotherExceptionType
                throw;
            }
        }

2 个答案:

答案 0 :(得分:2)

不仅仅是如何使用不同类型的异常处理。在功能上你应该定义哪些层必须做什么,但有例外。

like data layer =>除了DataException或SQLException之外,不要抛出任何东西。记录它们并将通用数据库异常返回给UI。

业务层=>记录并重新抛出简单的业务异常 UI图层=>仅捕获业务异常并在业务异常内的消息中提醒它

一旦定义了所有这些,您就可以使用您学到的和总结的内容来构建它。

答案 1 :(得分:2)

通过抛出一个新的更简单的异常建议您做什么(我认为)是您将较低层的异常转换为新的,更高级别的异常,以便在外层使用。较低级别的例外情况不适合在该计划的高层消费。

例如,在LINQ to Entities中,当序列没有元素时,方法Single()将抛出InvalidOperationException。但是,此异常类型非常常见,因此很难在用户界面级别中捕获它:如何区分抛出此异常的不同可能性(例如,修改只读集合)?解决方案是将转换异常转换为应用程序可以轻松处理的另一种(新的,用户定义的)类型。

这是一个简单的例子:

public class MyUserService {
    public User GetById(int id) {
        try {
            using(var ctx = new ModelContainer()) {
                return ctx.Where(u => u.Id == id).Single();
            }
        }
        catch(InvalidOperationException) {
            // OOPs, there is no user with the given id!
            throw new UserNotFoundException(id);
        }
    }
}

然后Program层可以捕获UserNotFoundException并立即知道发生了什么,从而找到向用户解释错误的最佳方法。 细节将取决于程序的确切结构,但这样的东西可以在ASP.NET MVC应用程序中使用:

public class MyUserController : Controller {
    private MyUserService Service = new MyUserService();
    public ActionResult Details(int id) {
        User user;
        try {
            user = Service.GetById(id);
        }
        catch(UserNotFoundException) {
            // Oops, there is no such user. Return a 404 error
            // Note that we do not care about the InvalidOperationException
            // that was thrown inside GetById
            return HttpNotFound("The user does not exist!");
        }
        // If we reach here we have a valid user
        return View(user);
     }
}