Доброго времени суток!
В настоящий момент я разрабатываю библиотеку классов предоставляющую API для логирования сообщений в таблицу БД MS SQL Server.
Изучив тему обработки исключений в C# саомостоятельно по книге Герберта Шилдта: "С# 4.0 Полное руководство" и засучив рукава начал применять полученные знания на своей рабочей задачи, но позже наткнулся на раздел MSDN:
http://msdn.microsoft.com/ru-ru/library/ms229014%28v=vs.100%29.aspx и понял, что всё сделаное мной противоречит практически всем основным рекомендация там приведённым. Вот кратко основные из них:
1. Создавайте и генерируйте пользовательские исключения, если у Вас возникает ошибка, которую можно обработать программным путем иным способом, чем существующие исключения. В противном случае генерируйте одно из существующих исключений.
2. Не создавайте и не генерируйте новых исключений, просто чтобы иметь свои собственные исключения.
3. Генерируйте наиболее специфичные (самые отдаленные от базовых) исключения, которые подходят в конкретной ситуации. Например, если метод принимает null-аргумент (Nothing в Visual Basic), он должен генерировать исключение System.ArgumentNullException вместо базового типа System.ArgumentException.
4. Особые исключения, создаваемые кодом нижнего уровня, рекомендуется заключать в оболочку более подходящего исключения, если исключение нижнего уровня не имеет смысла в контексте операции высокого уровня.
5. Не перехватывайте исключение System.Exception или System.SystemException в своем коде, только если не планируется его повторный вызов. Избегайте перехватов исключений System.Exception или System.SystemException, за исключением обработчиков исключений высокого уровня.
Теперь фрагмент из моего проекта:
public Category GetCategory(string CategoryName)
{
try
{
if( !DataBase.ValidateArgument(CategoryName) )
throw new JournalProvaiderException("Ошибка получения категории журнала. " +
"Не верный формат имени категории журнала.", null);
//Если экземпляр запрошенной категории журнала уже создан,
//возвращаем ссылку на него
if (_CatInstances.ContainsKey(CategoryName))
return _CatInstances[CategoryName];
if (!IsConnected)
throw new JournalProvaiderException("Ошибка получения категории журнала. " +
"Не установлено подключение к серверу журналов.", null);
//Проверка существоваония категории журнала в БД сервера журналов
if (!IsJournalExist(CategoryName))
throw new JournalProvaiderException("Катгория журнала с именем: <" + CategoryName +
"> не существует на сервере журналов.", null);
Category _Category = null;
//Выбор блока обработки категории журнала
switch (CategoryName)
{
case ("Информационные сообщения"):
//Входные параметры хранимой процедуры
SQLParametr[] InputParamList = new SQLParametr[1];
InputParamList[0].Name = "@CatName";
InputParamList[0].Value = CategoryName;
//Возвращаемое хранимой процедурой значение
SqlParameter ReturnValue;
//Получить описание категории журнала из БД
using (SqlDataReader Reader = DataBase.ExecuteSQL(_SQLConnection, "sp_GetCategories",
InputParamList, out ReturnValue, false))
{
if (!Reader.HasRows)
{
Reader.Close();
throw new JournalProvaiderException("Не удалось получить описание категории журнала <" + CategoryName +
"> от сервера журналов! (sp_GetCategories return " +
ReturnValue.Value.ToString() + " ).", null);
}
//Получить аттрибуты записи Категори журнала
if (Reader.Read())
_Category = new Category(
Reader.GetSqlString(0).Value,
Reader.GetSqlString(1).Value,
Reader.GetSqlString(2).Value,
Reader.GetSqlBoolean(3).Value,
Reader.GetSqlInt32(4).Value,
Reader.GetSqlInt32(5).Value
);
else
throw new JournalProvaiderException("Сбой при получении аттрибутов записи " +
"категории журнала <" + CategoryName + ">.", null);
Reader.Close();
}
break;
default:
throw new JournalProvaiderException("Запрошенная категория журнала: <" + CategoryName +
"> не поддерживается бибилотекой провайдера.", null);
}
//Добавляем ссылку на категорию журнала в коллекцию
_CatInstances.Add(CategoryName, _Category);
return _Category;
}
catch(SystemException e)
{
throw new JournalProvaiderException("Ошибка получения данных категории журнала.", e);
}
}
Я размышлял так:
1. Так API библиотеки абстрагирует пользователя библиотеки от БД вводя в свою очередь собственную абстракцию "Сервер жураналов" то
информационные сообщения должны описывать ситуацию в контексте сущности "Сервер журналов", являясь оболочкой для исключений СУБД.
Поэтому создал собственный класс исключения:
//Определяет исключительную ситуацию
//в работе провайдера журналов
public class JournalProvaiderException: Exception
{
public JournalProvaiderException() : base() {}
public JournalProvaiderException(string message) : base(message) { }
public JournalProvaiderException(string message, Exception innerException) : base(message, innerException) { }
public JournalProvaiderException(SerializationInfo info, StreamingContext context) : base(info, context) { }
public override string ToString()
{
return "JournalProvaiderException: < " + base.Message + " >\n" +
"Source: < " + base.Source + " >\n" +
"TargetSite: < " + base.TargetSite + " >\n" +
"StackTrace: < " + base.StackTrace + " >\n" + "\n" +
//Внутреннее исключение
"InnerException: \n<\n" + base.InnerException + "\n>";
}
}
2. Все исключения возбуждаемые внути моей библиотеки перехватываются на уровне SystemException и "заворачиваются" в мой класс JournalProvaiderException, чтобы в клиентском коде достаточно было ставить только один "фирменый" перехватчик:
try
{
//Клиентский код
}
catch (JournalProvaiderException e)
{
...
}
Внутри библиотеки я начал ставить несколько блоков перехвата, так:
try
{
}
catch (SqlNullValueException e)
{
}
catch (OutOfMemoryException e)
{
}
catch (InvalidOperationException e)
{
}
...
но бысро понял, что такой код выглядит громоздким, плохочитаемыми, да и знать сколько именно исключений перехватывть сложно.
И тут меня осенило

Я посчитал, что гораздо правильней и читабельнее использовать полиморфизм и перехватывать исключения базового класса, и генерировать своё исключение завернув в него перехваченное. Так и поступил:
try
{
}
catch (SystemExceptione e)
{
throw new JournalProvaiderException("Ошибка получения данных из журнала.", e);
}
Объясните пожалуйста почему поступать так плохо?
Почему Microsoft даёт рекомендации так не делать (см. выше П.5) ?
Что значит рекомендация: " Создавайте и генерируйте пользовательские исключения, если у Вас возникает ошибка, которую можно обработать программным путем иным способом, чем существующие исключения. В противном случае генерируйте одно из существующих исключений." ?
Помогите разобраться

. Заранее благодарен!