2015-12-29 2 views
1

Мой C# 6, .Net 4.6, Entity Framework 6 Приложение имеет множество методов по этой базовой схеме:Устранить дублирование кода в C# уловом

try 
{ 
    using (Entities dbContext = new Entities()) 
    { 
     // Some database stuff 
    } 
} 
catch (System.Data.Entity.Core.EntityException eEntCore) 
{ 
    // May occur if connection to DB fails 
    throw MyApplicationException("Cannot access data store (Entity Core Exception)", eEntCore); 
} 
catch (System.Data.Entity.Infrastructure.DbUpdateException eDbu) 
{ 
    throw MyApplicationException("Cannot access data store (Database Exception)", eDbu); 
} 
catch (System.Data.SqlClient.SqlException eSql) 
{ 
    // Usually occurs after a Referential Integrity error 
    throw MyApplicationException("Cannot access data store (Data Exception)", eSql); 
} 

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

Моя проблема заключается в том, что если появляется другой тип исключения, мне нужно внести поправки в десятки методов, чтобы добавить новое исключение. Я попытался централизовать обработку ошибок с помощью этого блока catch:

catch (Exception e) 
{ 
    AnalyseException(e); 
} 

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

Интерфейс пользователя обрабатывает исключение MyApplicationException. Я не хочу, чтобы он обрабатывал все типы исключений EF и SQL.

Я попытался переработать дизайн с помощью этой функции C# 6:

catch (Exception ex) when (AnalyseException(ex)) 

, но я не могу получить, чтобы сохранить трассировку стека. Есть ли способ сохранить трассировку стека без большого количества дублированного кода?

+0

Почему вы вообще ловите исключения? Это похоже на фатальный недостаток. –

+0

Я считаю, что это связано с поведением стека и исключениями. Прочтите раздел «Примечания» на этой странице (https://msdn.microsoft.com/en-us/library/system.exception.stacktrace(v=vs.110).aspx). «Если исключение выбрано, а затем повторно показано, в том же методе, трассировка стека содержит только место, где исключение было восстановлено, и не включает в себя местоположение, в котором исключение было изначально выбрано». – wentimo

+0

Хотя я полностью согласен с ответами Криса и Даниэля, существует огромная разница между 'throw ex'' и' throw; 'insider 'catch'. Первый удаляет трассировку стека. Последний перебрасывает пойманное исключение и сохраняет трассировку стека. Кроме того, вы создаете новые экземпляры исключений и включаете исключение catch в качестве внутреннего исключения. Это внутреннее исключение будет включать исходную трассировку стека. –

ответ

3

Я думаю, что у вас есть две проблемы здесь:

  1. Вы воспринимаете исключения просто обернуть их в своем собственном типе исключения. Зачем беспокоиться?
  2. Вы должны изменить «десятки мест», если попытаетесь поймать новое исключение. Это означает, что у вас есть нечеткая абстракция, и вы повторяете свой код доступа к данным.

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

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

0

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

1

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

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

0

Я предполагаю, что это может работать:

try 
{ 
    using (Entities dbContext = new Entities()) 
    { 
     // Some database stuff 
    } 
} 
catch (Exception ex) 
{ 
    if (!(ex is System.Data.Entity.Core.EntityException || ex is System.Data.Entity.Infrastructure.DbUpdateException || ex is System.Data.SqlClient.SqlException)) 
     throw; 

     throw new MyApplicationException("Cannot access data store (" + ex.GetType().ToString() + ")", ex) 
} 

Сохранение трассировки стека невозможно, если вы не используете «бросок» (без упаковки его в виде исключения).throw отмечает место для трассировки стека, поэтому вы либо бросаете исходное исключение, либо ломаете его где-то в другом месте, либо оставляете его таким образом.

Смежные вопросы