在我处理的应用程序中,任何业务逻辑错误都会导致抛出异常,并且调用代码会处理异常。这种模式在整个应用程序中使用并且运行良好。
我遇到的情况是,我将尝试从业务层内部执行许多业务任务。对此的要求是一个任务的失败不应导致进程终止。其他任务仍然可以执行。换句话说,这不是原子操作。我遇到的问题是,在操作结束时,我希望通过抛出异常来通知调用代码确实发生了异常。请考虑以下psuedo-code代码段:
function DoTasks(MyTask[] taskList)
{
foreach(MyTask task in taskList)
{
try
{
DoTask(task);
}
catch(Exception ex)
{
log.add(ex);
}
}
//I want to throw something here if any exception occurred
}
我扔什么?我在职业生涯之前遇到过这种模式。在过去,我保留了所有异常的列表,然后抛出包含所有捕获的异常的异常。这似乎不是最优雅的方法。重要的是要从每个异常中保留尽可能多的细节以呈现给调用代码。
思想?
编辑:解决方案必须用.Net 3.5编写。我不能使用任何beta库,或者Bradley Grainger(下面)中提到的.Net 4.0中的AggregateException对于抛出的集合异常是一个很好的解决方案。
答案 0 :(得分:38)
.NET的Task Parallel Library extensions(will become part of .NET 4.0)遵循其他答案中建议的模式:收集所有抛出到AggregateException类中的异常。
通过始终抛出相同的类型(子工作中是否存在一个异常,或许多异常),处理异常的调用代码更容易编写。
在.NET 4.0 CTP中,AggregateException
有一个公共构造函数(需要IEnumerable<Exception>
);它可能是您申请的不错选择。
如果您的目标是.NET 3.5,请考虑在您自己的代码中克隆您需要的System.Threading.AggregateException
类的部分,例如,某些构造函数和InnerExceptions属性。 (您可以将克隆放置在程序集内的System.Threading
命名空间中,如果您公开公开它可能会导致混淆,但稍后会更容易升级到4.0。)当.NET 4.0发布时,您应该能够通过从项目中删除包含克隆的源文件,更改项目以定位新框架版本和重建来“升级”到Framework类型。当然,如果你这样做,你需要仔细跟踪这个类的更改,因为Microsoft发布了新的CTP,这样你的代码就不会变得不兼容。 (例如,这似乎是一个有用的通用类,他们可以将它从System.Threading
移动到System
。)在最坏的情况下,您可以重命名类型并将其移回到您的自己的命名空间(大多数重构工具都很容易)。
答案 1 :(得分:13)
我头脑中的两种方法是创建一个自定义异常并将异常添加到此类并抛出结束:
public class TaskExceptionList : Exception
{
public List<Exception> TaskExceptions { get; set; }
public TaskExceptionList()
{
TaskExceptions = new List<Exception>();
}
}
public void DoTasks(MyTask[] taskList)
{
TaskExceptionList log = new TaskExceptionList();
foreach (MyTask task in taskList)
{
try
{
DoTask(task);
}
catch (Exception ex)
{
log.TaskExceptions.Add(ex);
}
}
if (log.TaskExceptions.Count > 0)
{
throw log;
}
}
如果任务失败并且有一个'out List'变量,则或返回true或false。
public bool TryDoTasks(MyTask[] taskList, out List<Exception> exceptions)
{
exceptions = new List<Exception>();
foreach (MyTask task in taskList)
{
try
{
DoTask(task);
}
catch (Exception ex)
{
exceptions.Add(ex);
}
}
if (exceptions.Count > 0)
{
return false;
}
else
{
exceptions = null;
return true;
}
}
答案 2 :(得分:4)
您可以创建一个自定义的Exception,它本身有一组Exceptions。然后,在您的Catch块中,只需将其添加到该集合中。在流程结束时,检查例外计数是否> 0,然后抛出自定义异常。
答案 3 :(得分:2)
您可能希望使用BackgroundWorker为您执行此操作。它会在完成时自动捕获并显示任何异常,然后您可以抛出或记录或执行任何操作。此外,您还可以获得多线程的好处。
BackgroundWorker是委托人asynchronous programming model.
的一个很好的包装器答案 4 :(得分:0)
这里没有超级优雅的解决方案,只有一些想法: