在我处理的应用程序中,任何业务逻辑错误都会导致抛出异常,并且调用代码会处理异常.这种模式在整个应用程序中使用并且运行良好.
我有一种情况,我将尝试从业务层内部执行许多业务任务.对此的要求是一个任务的失败不应导致进程终止.其他任务仍然可以执行.换句话说,这不是原子操作.我遇到的问题是,在操作结束时,我希望通过抛出异常来通知调用代码确实发生了异常.请考虑以下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 对于抛出的集合异常将是一个很好的解决方案.
.NET 的任务并行库扩展(将成为.NET 4.0的一部分)遵循其他答案中建议的模式:收集已抛入AggregateException类的所有异常.
通过始终抛出相同的类型(子工作中是否存在一个异常,或许多异常),处理异常的调用代码更容易编写.
在.NET 4.0 CTP中,AggregateException
有一个公共构造函数(需要IEnumerable
); 它可能是您的应用程序的一个很好的选择.
如果您的目标是.NET 3.5,请考虑System.Threading.AggregateException
在您自己的代码中克隆您需要的类的部分,例如,一些构造函数和InnerExceptions属性.(您可以将克隆System.Threading
放置在程序集内的命名空间中,如果公开公开它可能会导致混淆,但稍后会更容易升级到4.0.)当.NET 4.0发布时,您应该能够"升级"到框架类型通过从项目中删除包含克隆的源文件,更改项目以定位新框架版本和重建.当然,如果你这样做,你需要仔细跟踪这个类的更改,因为Microsoft发布了新的CTP,这样你的代码就不会变得不兼容.(例如,这似乎是一个有用的通用类,他们可以从中移动它System.Threading
到System
).在最坏的情况下,你可以重命名的类型和移动它返回到自己的名称空间(这是很容易与大多数重构工具).
我头脑中的两种方法是创建一个自定义异常并将异常添加到此类并抛出结束:
public class TaskExceptionList : Exception { public ListTaskExceptions { get; set; } public TaskExceptionList() { TaskExceptions = new List (); } } 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 Listexceptions) { exceptions = new List (); 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; } }