Модераторы: Partizan, gambit
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Обработка исключений в многоуровневых приложениях 
:(
    Опции темы
0leg
Дата 23.11.2006, 10:02 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 27
Регистрация: 23.11.2006

Репутация: 1
Всего: 1



Ситуация: 

Проект состоит из основного приложения, которое работает с базой данных через интерфейс IDatabase.
Есть несколько классов, реализующих IDatabase для различных типов СУБД. Эти классы предполагается писать на стороне, без доступа к исходникам основного приложения (в том смысле, что менять исходники под новые классы никому нельзя).

Необходимо создать общий механизм обработки исключений, чтобы выполнялись условия:
1. Исключения должны записываться в единый лог-файл приложения.
2. Должна быть возможность идентифицировать каждое исключение, чтобы обрабатывать каждое исключение по-своему. Создавать для каждой ошибки свой класс не могу - число ошибок может быть до сотни.

Я реализовал это так:

  Создал новый класс исключений, от которого будут наследоваться все остальные. В этом классе реализовал новое совйство ErrorCode (для хранения уникального кода ошибки) и переписал конструкторы, чтобы они принимали на вход код ошибки и лог-файл для записи сообщения:
Код

public class CommonException: ApplicationException
{
   public int ErrorCode{get{ ... };}
   public CommonException(ErrorCodes ErrCode, string AdditionalInfo, LogFile Log): base()
....
}


Новый конструктор делает следующее:
1. Запоминает код ошибки (теперь он будет доступен через свойство ErrorCode).
1. В зависимости от кода ошибки формирует нужное сообщение.
2. Делает запись в лог-файл.

Теперь создаю несколько наследников CommonException (без дополнительного кода). Один из наследников - для ошибок при работе с базами:
Код

public class DBException: CommonException
{
}


Предполагается, что исключения DBException будут генерироваться классами, реализующими интерфейс IDatabase. Но тут возникает 

ПРОБЛЕМА: Базовое приложение не знает заранее коды ошибок, которые будут генерироваться внешними классами (класс может быть еще не написан) и не сможет правильно сформировать по ним сообщение. 

Помогите решить проблему по-изящнее.
Возможно ли обойтись без кодирования ошибок? 

Мне кодирование нужно по двум причинам:
1. По коду я формирую сообщение. Может правильнее формировать сообщение на месте вызова? Но тогда ухудшается читабельность кода.
2. По коду я определяю дальнейшие действия (не все ошибки фатальные - некоторые нужно просто записать в лог, определенным образом обработать и продолжить выполнение приложения).
PM MAIL   Вверх
mr.DUDA
Дата 23.11.2006, 10:22 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


3D-маньяк
****


Профиль
Группа: Экс. модератор
Сообщений: 8244
Регистрация: 27.7.2003
Где: город-герой Минск

Репутация: 110
Всего: 232



Цитата(0leg @  23.11.2006,  09:02 Найти цитируемый пост)
1. По коду я формирую сообщение. Может правильнее формировать сообщение на месте вызова? Но тогда ухудшается читабельность кода.

Обычно, для записи в лог используется Message из исключения; выкинуть исключение можно в любом месте кода и с любым, произвольным сообщением (см. конструктор со строковым аргументом). Для обработки конкретного исключения используется catch на тип исключения или его родительский тип. ИМХО, числовой код не нужен, разве что исключений сотни и обрабатываются они по отдельности (лень писать кучу классов и catch-ей на каждый).

Цитата(0leg @  23.11.2006,  09:02 Найти цитируемый пост)
2. По коду я определяю дальнейшие действия (не все ошибки фатальные - некоторые нужно просто записать в лог, определенным образом обработать и продолжить выполнение приложения).

Хотелось бы увидеть пример такого исключения и кода, который его обрабатывает.


--------------------
user posted image
PM MAIL WWW   Вверх
ivashkanet
Дата 23.11.2006, 10:25 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Кодю потиху
****


Профиль
Группа: Участник Клуба
Сообщений: 3684
Регистрация: 23.2.2006
Где: Гомель, Беларусь

Репутация: 47
Всего: 149



Варианты:
1) Вся информация нужная для записи в лог и определения критична ли ошибка содержится в самой ошибке. Поля: LogMessage и ExceptionType, например. Какая разница что писать код ошибки или полную информацию о ней  smile 
2) Можно передавать ошибку в специальный класс-обработчик, который придется изменять по мере добавления новых. Но с другой стороны ты меняешь только его, а не основное приложение.

P.S. Мне больше нравится второй вариант.
PM MAIL WWW ICQ   Вверх
0leg
Дата 23.11.2006, 11:34 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 27
Регистрация: 23.11.2006

Репутация: 1
Всего: 1



Цитата(mr.DUDA @  23.11.2006,  10:22 Найти цитируемый пост)
Обычно, для записи в лог используется Message из исключения; выкинуть исключение можно в любом месте кода и с любым, произвольным сообщением (см. конструктор со строковым аргументом).

про конструктор - в курсе  smile


Цитата(mr.DUDA @  23.11.2006,  10:22 Найти цитируемый пост)
 Для обработки конкретного исключения используется catch на тип исключения или его родительский тип. ИМХО, числовой код не нужен, разве что исключений сотни и обрабатываются они по отдельности (лень писать кучу классов и catch-ей на каждый).

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

Цитата(mr.DUDA @  23.11.2006,  10:22 Найти цитируемый пост)
Хотелось бы увидеть пример такого исключения и кода, который его обрабатывает.

Код

// Попытка соединения с БД
try
{
   DB.Connect();
}
catch(DBException e)
{
   // Если логин/пароль не прошел
   if (e.ErrorCode == ErrorCodes.APP_LOGIN_FAILED)
      ...... попытка повторного соединения ........
   else
   {
      ..... обработка других кодов ошибки .........
      throw;
   }
}




Цитата(ivashkanet @  23.11.2006,  10:25 Найти цитируемый пост)
2) Можно передавать ошибку в специальный класс-обработчик, который придется изменять по мере добавления новых. Но с другой стороны ты меняешь только его, а не основное приложение.

А если новые ошибки добавляю не я? А несколько незнакомых со мной и друг с другом человеков?
Это некрасиво. Я хочу как с интерфейсами - описал интерфейс и всё! реализуйте его как хотите, меня не трогайте.
Я за полное разделение труда. 

Можно пойти на изврат - написать класс с run-time добавлением новых сообщенией. Но это уже слишком...

----

Насчет генерации сообщений по месту ошибки: возможно так и сделаю.

Вопрос с идентификацией ошибок пока не закрыт......!!! smile 
PM MAIL   Вверх
mr.DUDA
Дата 23.11.2006, 11:47 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


3D-маньяк
****


Профиль
Группа: Экс. модератор
Сообщений: 8244
Регистрация: 27.7.2003
Где: город-герой Минск

Репутация: 110
Всего: 232



Цитата(0leg @  23.11.2006,  10:34 Найти цитируемый пост)
А если новые ошибки добавляю не я? А несколько незнакомых со мной и друг с другом человеков?Это некрасиво. Я хочу как с интерфейсами - описал интерфейс и всё! реализуйте его как хотите, меня не трогайте.Я за полное разделение труда.

Но ведь в случае с кодами ошибок, если добавится например ошибка ERROR_DBCONNECTION_CLOSED, придётся лезть во все места где есть DBException и вставлять ещё один if ! Какое же тут разделение труда, если все человеки будут рефакторить код на каждый чих.

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


--------------------
user posted image
PM MAIL WWW   Вверх
ivashkanet
Дата 23.11.2006, 12:08 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Кодю потиху
****


Профиль
Группа: Участник Клуба
Сообщений: 3684
Регистрация: 23.2.2006
Где: Гомель, Беларусь

Репутация: 47
Всего: 149



Цитата(0leg @  23.11.2006,  10:34 Найти цитируемый пост)

А если новые ошибки добавляю не я? А несколько незнакомых со мной и друг с другом человеков?
Это некрасиво. Я хочу как с интерфейсами - описал интерфейс и всё! реализуйте его как хотите, меня не трогайте.
Я за полное разделение труда. 

Хочешь интерфейс? Пожалуйста:
Код

interface IExceptionProcessor
{
    void Process(Exception ex);
}

Но лучше создать иерархию классов:

Код

public abstract class AbstractErrorProcessor
{
    public abstract void ProcessException(Exception ex);
}

public class BasicErrorProcessor : AbstractErrorProcessor
{
    public override void ProcessException(Exception ex)
    {
        // ex.Message будет вместо кода
        switch (ex.Message)
        {
            case "2141":
                // обработка
                break;
            case "2647":
                // обработка
                break;
            case "2551":
                // обработка
                break;
            case "2147":
                // обработка
                break;
            default:
                // Лучше всего выкинуть исключение, но исключение в 
                // обработчике исключений это ЛОЛ
                // Поэтому записываем это в лог
                break;
        }
    }
}

public class ExtendedErrorProcessor : BasicErrorProcessor
{
    public override void ProcessException(Exception ex)
    {
        // ex.Message будет вместо кода
        switch (ex.Message)
        {
            case "new exp1":
                // обработка
                break;
            case "new exp2":
                // обработка
                break;

            default:
                // передаем обработку в базовый класс
                base.ProcessException(ex);
                break;
        }

    }
}


P.S. Хотя я считаю, что первоначальную обработку исключения должен проводить класс в котором оно было сгенерировано. Незачем посторонним (и не очень) класса знать подноготную другого. Вот если он не в состоянии это сделать, тогда да...

Добавлено @ 12:09 
Цитата(mr.DUDA @  23.11.2006,  10:47 Найти цитируемый пост)
обрабатывать по возможности ближе к месту возникновения

Вот и я об этом smile
PM MAIL WWW ICQ   Вверх
mr.DUDA
Дата 23.11.2006, 13:19 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


3D-маньяк
****


Профиль
Группа: Экс. модератор
Сообщений: 8244
Регистрация: 27.7.2003
Где: город-герой Минск

Репутация: 110
Всего: 232



ivashkanet, одна поправка: ex.Message нельзя использовать для проверки, Message не для этого предназначен. Если кто-то не знает о том, что Message предполагается проверять в свитче, то может написать к примеру:

Код
throw new DbException (@"А-а-а ! Всё свалилось, я не знаю почему !!! 
Вот вам моё многострочное
сообщение в Message,
как хотите так его и используйте теперь,
можете хоть в лог записать, хоть забить на него.
На всякий случай сообщаю: код ошибки -2394857");


после чего exception processor-у останется виновато развести руками


--------------------
user posted image
PM MAIL WWW   Вверх
ivashkanet
Дата 23.11.2006, 14:06 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Кодю потиху
****


Профиль
Группа: Участник Клуба
Сообщений: 3684
Регистрация: 23.2.2006
Где: Гомель, Беларусь

Репутация: 47
Всего: 149



mr.DUDA, нет, нет, нет smile 
Я просто выбрал первое попавшееся текстовое (string) поле класса Exception smile
Не писать же ex.ErrorCode если в классе Exception этого свойства нет smile
P.S. Извиняюсь, что ввел в заблуждение.

Добавлено @ 14:07 
 smile у меня один пост до 1700, а у Дуды 6 до 5000  smile 
PM MAIL WWW ICQ   Вверх
0leg
Дата 23.11.2006, 14:34 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 27
Регистрация: 23.11.2006

Репутация: 1
Всего: 1



Спасибо за ответы, помогли упорядочить моск.

Итак, задача вкратце:

Есть основное приложение, оно работает с базой данных посредством одного интерфейса - IDatabase.
Есть куча классов, реализующих интерфейс IDatabase. 
Основное приложение будет выбирать нужный класс для IDatabase в run-time.
При написании классов код основного приложения менять уже будет нельзя.

Вопрос: как обрабатывать исключения и записывать их в единый лог.

Решил делать так:

1. Пишу общего предка для всех своих исключений, который пишет лог-файл:
Код

// Базовый класс для всех исключений
public class CommonException: ApplicationException
{    
    public CommonException():base()
    {
        ToLog();
    }
    
    public CommonException(string message):base(message)
    {
        ToLog();
    }

    public CommonException(string message, Exception innerException) : base(message, innerException) 
    {
        ToLog();
    }
        
    // Общая часть всех конструкторов
    private void ToLog()
    {
        if (LogFile.Opened)
            LogFile.WriteLine(System.DateTime.Now.ToString() + " " + Message);
    }
}


2. На основе CommonException создаю остальные исключения - и для основного приложения, и для подключаемых внешних классов.
Все классы исключений помещаю в общедоступную библиотеку.
3. В месте возникновения ошибки генерирую исключение и сразу передаю в конструктор сообщение об ошибке.
4. Если ошибка не критическая - исключение не генерирую, самостоятельно пишу в лог сообщение, самостоятельно вывожу окно с сообщением на экран(если необходимо).
5. Все ошибки, для которых нужна особая обработка (например, пароль непрошел - нужно ввести пароль еще раз) реализую отдельными классами. Остальные - объединяю по несколько ошибок на класс, просто будут разные сообщения.

Если что - поправьте.
PM MAIL   Вверх
Exception
Дата 25.11.2006, 16:39 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Участник Клуба
Сообщений: 4525
Регистрация: 26.12.2004

Репутация: 29
Всего: 186



ИМХО, не стоит изобретать велосипед. Exception Handling Block, входящий в состав Enterprise Library.

Небольшая статья о нём.
PM   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Прежде чем создать тему, посмотрите сюда:
mr.DUDA
THandle

Используйте теги [code=csharp][/code] для подсветки кода. Используйтe чекбокс "транслит" если у Вас нет русских шрифтов.
Что делать если Вам помогли, но отблагодарить помощника плюсом в репутацию Вы не можете(не хватает сообщений)? Пишите сюда, или отправляйте репорт. Поставим :)
Так же не забывайте отмечать свой вопрос решенным, если он таковым является :)


Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, mr.DUDA, THandle.

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | Общие вопросы по .NET и C# | Следующая тема »


 




[ Время генерации скрипта: 0.0604 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.