2013-05-20 2 views
4

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

Моего код в настоящее время выглядит следующим образом:

public IList<User> GetUser() 
{ 
    try 
    { 
     return _repository.GetUsers(); 
    } 
    catch (WebException ex) 
    { 
     ErrorMessages.Add("..."); 
     _logger.ErrorException("...", ex); 
    } 
    catch (SoapException ex) 
    { 
     ErrorMessages.Add("..."); 
     _logger.ErrorException("...", ex); 
    } 
    ... etc 
} 

Я мог бы заменить эти строки в моем поймать блок с вызовом другого метода, который принимает значение сообщения об ошибке и значение регистратора сообщений. Однако я полагаю, что я мог бы также сделать это, используя параметр Action <>, но я очень неопытен при использовании Func <> и Action <> и не вижу, какую выгоду я бы использовал, используя один из методов.

Вопрос в том, как лучше всего реорганизовать этот код, и почему один из способов приносит пользу другому (как в моем примере выше).

Спасибо за любую помощь.

+0

Почему? http://blog.gauffin.org/2010/11/do-not-catch-that-exception/ – jgauffin

+0

«Попробуйте/поймайте все в порядке, ближайшем к пользователю». В конечном счете, этот результат возвращается к представлению – Serberuss

ответ

3

Предполагая типы исключений всегда одинаковы, но сообщения различны, вы можете сделать это:

static public T Try<T>(string webMessage, string soapMessage, Func<T> func) 
{ 
    try 
    { 
     return func(); 
    } 
    catch (WebException ex) 
    { 
     ErrorMessages.Add(webMessage); 
     _logger.ErrorException(webMessage, ex); 
    } 
    catch (SoapException ex) 
    { 
     ErrorMessages.Add(soapMessage); 
     _logger.ErrorException(soapMessage, ex); 
    } 
} 

Это Try-метод будет использовать делегат типа Func<T> для вызова функции и возвращает его значение. Функция будет находиться внутри одного блока try-catch. Сообщения предоставляются через параметры. Теперь, где-то в вашем коде, вы могли бы назвать это как:

var users = Try("My web message.", "My soap message.",() => _repository.GetUsers()); 

Или в вашем случае даже меньше (если не с помощью параметров):

var users = Try("My web message.", "My soap message.", _repository.GetUsers); 

Конечно, вы можете изменить и аранжировать параметры Try по своему усмотрению.

В случае, если вы смешиваете метод с типами возвратов и без них, лучше не использовать Func, но Action. Это будет в состоянии соответствовать всем ситуациям:

static public void Try(string webMessage, string soapMessage, Action action) 
{ 
    try 
    { 
     action(); 
    } 
    catch (WebException ex) 
    { 
     ErrorMessages.Add(webMessage); 
     _logger.ErrorException(webMessage, ex); 
    } 
    catch (SoapException ex) 
    { 
     ErrorMessages.Add(soapMessage); 
     _logger.ErrorException(soapMessage, ex); 
    } 
} 

Но это решение делает код крошечные немного сложнее для чтения/сохранения:

IList<User> users; 
Try("My web message.", "My soap message.",() => users = _repository.GetUsers()); 
+0

. Обратите внимание, что тип возврата 'T' будет немного неудобным для обработки недействительных методов. –

+0

Абсолютно верно. Я уточню свой ответ. –

+0

еще один пример того, что вы не должны делать просто потому, что можете это сделать. Хороший ответ на вопрос, хотя. – gbjbaanb

3

Вы можете использовать лямбды, чтобы помочь с этим.

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

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

Например, ваш общий обработчик может выглядеть следующим образом:

public void AttemptAction(Action action) 
{ 
    try 
    { 
     action(); 
    } 
    catch (WebException ex) 
    { 
     ErrorMessages.Add("..."); 
     _logger.ErrorException("...", ex); 
     // Rethrow? 
    } 
    catch (SoapException ex) 
    { 
     ErrorMessages.Add("..."); 
     _logger.ErrorException("...", ex); 
     // Rethrow? 
    } 
} 

И тогда вы могли бы использовать его как это:

public IList<User> GetUser() 
{ 
    IList<User> result = null; 

    AttemptAction(() => result = _repository.GetUsers()); 

    return result; 
} 
+0

Спасибо за ваш ответ. Тем не менее, я думаю, что это должно быть наоборот, потому что сообщения об ошибках не одинаковы для всех вызовов. Существует вызов GetUsers, но есть и другие, такие как GetCategories. Я должен был упомянуть об этом в моем оригинальном посте, извините – Serberuss

+0

@Serberuss. Все в порядке - это своего рода общий подход. Вы можете передавать сообщения об ошибках, как в ответе MartinMulder, если вам нужно. –

0

Вы можете использовать Аспект-ориентированное программирование http://en.wikipedia.org/wiki/Aspect-oriented_programming. Идея поместить весь повторяющийся код в специальные классы, которые называются аспект.

Ваш код будет выглядеть в PostSharp

[ExceptionLogger] 
public IList<User> GetUser() 
{ 
    return _repository.GetUsers(); 
} 
public class ExceptionLogger: OnMethodBoundaryAspect 
{ 
    //getting _logger and ErrorMessages 
    public override void OnException(MethodExecutionArgs args) 
    { 
     ErrorMessages.Add("..."); 
     _logger.ErrorException("...", ex); 
    } 
} 

Для C#, вы ccan использовать PostSharp, Castle.Windsor или рамки Unity.

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