Подтвердить что ты не робот

Выбрасывание нескольких исключений в .Net/С#

В приложении, над которым я работаю, любая ошибка бизнес-логики вызывает исключение, и вызывающий код обрабатывает исключение. Этот шаблон используется во всем приложении и работает хорошо.

У меня есть ситуация, когда я буду пытаться выполнить ряд бизнес-задач изнутри бизнес-уровня. Требование для этого состоит в том, что отказ одной задачи не должен приводить к завершению процесса. Другие задачи все равно должны быть выполнены. Другими словами, это не атомная операция. Проблема заключается в том, что в конце операции я хочу уведомить вызывающий код о том, что исключение или исключения произошли путем исключения исключения. Рассмотрим следующий фрагмент кода psuedo:

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. Я не могу использовать какие-либо бета-библиотеки, или исключение AggregateException в .Net 4.0, как упоминалось Bradley Grainger (ниже), было бы хорошим решением для исключений коллекции для броска.

4b9b3361

Ответ 1

Расширения параллельной библиотеки задач для .NET(который станет частью .NET 4.0) следуют шаблону, предложенному в других ответах: сбор всех исключений, которые были выбраны в класс AggregateException.

Если всегда бросать один и тот же тип (есть ли одно исключение из дочерней работы или много), вызывающий код, обрабатывающий исключение, легче писать.

В CTP.NET 4.0 AggregateException имеет открытый конструктор (который принимает IEnumerable<Exception>); это может быть хорошим выбором для вашего приложения.

Если вы настроили таргетинг на .NET 3.5, рассмотрите клонирование частей класса System.Threading.AggregateException, которые вам нужны в вашем собственном коде, например, некоторые из конструкторов и свойство InnerExceptions. (Вы можете поместить свой клон в пространство имен System.Threading внутри своей сборки, что может вызвать путаницу, если вы опубликуете его публично, но позже добавит обновление до версии 4.0.) Когда .NET 4.0 выпущен, вы должны иметь возможность "обновить" до типа Framework, удалив исходный файл, содержащий ваш клон из вашего проекта, изменив проект, чтобы настроить новую версию фреймворка, и перестроить. Конечно, если вы это сделаете, вам нужно тщательно отслеживать изменения этого класса, поскольку Microsoft выпускает новые CTP, чтобы ваш код не стал несовместимым. (Например, это похоже на полезный класс общего назначения, и они могут перемещать его от System.Threading до System.) В худшем случае вы можете просто переименовать этот тип и перенести его обратно в свое собственное пространство имен (это очень просто с большинством инструментов рефакторинга).

Ответ 2

Два способа верхней части моей головы будут либо создавать настраиваемое исключение, либо добавлять исключения в этот класс, а затем бросать этот конец:

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;
        }
    }

или вернуть true или false, если задачи не удались и имеют переменную "out List".

    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;
        }
    }

Ответ 3

Вы можете создать пользовательское исключение, в котором есть коллекция Исключений. Затем, в вашем блоке Catch, просто добавьте его в эту коллекцию. В конце вашего процесса проверьте, есть ли счетчик исключений > 0, а затем выбросите пользовательское исключение.

Ответ 4

Вы можете использовать BackgroundWorker, чтобы сделать это для вас. Он автоматически захватывает и предоставляет любые исключения, когда вы завершаете их, и затем вы можете бросить их или записать или сделать что угодно. Кроме того, вы получаете преимущество многопоточности.

BackgroundWorker - хорошая оболочка вокруг делегата асинхронная модель программирования.

Ответ 5

Нет супер-элегантного решения здесь, кроме нескольких идей:

  • Передайте функцию обработчика ошибок в качестве аргумента DoTasks, чтобы пользователь мог решить, продолжать ли
  • Использовать трассировку для регистрации ошибок по мере их возникновения.
  • Объединить сообщения из других исключений в сообщении пакета исключений