2015-09-22 2 views
0

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

public override int SaveChanges() 
{ 
    try 
    { 
     return base.SaveChanges(); 
    } 
    catch (DbEntityValidationException ex) 
    { 
     var entityValidationErrors = ex.EntityValidationErrors 
      .SelectMany(e => e.ValidationErrors) 
      .Select(x => string.Format("{0} - {1}", x.PropertyName, x.ErrorMessage)); 

     var fullErrorMessage = string.Join(Environment.NewLine, entityValidationErrors); 
     var exceptionMessage = string.Concat(ex.Message, " Entity validation errors: ", fullErrorMessage); 

     throw new DbEntityValidationException(exceptionMessage, ex.EntityValidationErrors); 
    } 
    catch (DbUpdateConcurrencyException ex) 
    { 
     Debug.WriteLine(ex.Message); 
     throw; 
    } 
    catch (DbUpdateException ex) 
    {     
     var sqlException = ex.GetBaseException() as SqlException; 

     if (sqlException == null || sqlException.Errors.Count <= 0) 
      throw; 

     var errors = new List<string>(); 

     for (var i = 0; i < sqlException.Errors.Count; i++) 
     { 
      errors.Add(string.Format("{0} - {1}", sqlException.Errors[i].Number, sqlException.Errors[i].Message)); 
     } 

     throw new DbUpdateException(string.Join(Environment.NewLine, errors)); 
    } 
} 
public override async Task<int> SaveChangesAsync() 
{ 
    try 
    { 
     return await base.SaveChangesAsync(); 
    } 
    catch (DbEntityValidationException ex) 
    { 
     var entityValidationErrors = ex.EntityValidationErrors 
      .SelectMany(e => e.ValidationErrors) 
      .Select(x => string.Format("{0} - {1}", x.PropertyName, x.ErrorMessage)); 

     var fullErrorMessage = string.Join(Environment.NewLine, entityValidationErrors); 
     var exceptionMessage = string.Concat(ex.Message, " Entity validation errors: ", fullErrorMessage); 

     throw new DbEntityValidationException(exceptionMessage, ex.EntityValidationErrors); 
    } 
    catch (DbUpdateConcurrencyException ex) 
    { 
     Debug.WriteLine(ex.Message); 
     throw; 
    } 
    catch (DbUpdateException ex) 
    { 
     var sqlException = ex.GetBaseException() as SqlException; 

     if (sqlException == null || sqlException.Errors.Count <= 0) 
      throw; 

     var errors = new List<string>(); 

     for (var i = 0; i < sqlException.Errors.Count; i++) 
     { 
      errors.Add(string.Format("{0} - {1}", sqlException.Errors[i].Number, sqlException.Errors[i].Message)); 
     } 

     throw new DbUpdateException(string.Join(Environment.NewLine, errors)); 
    } 
} 
+1

Ваш 'SaveChangesAsync' возвращает' Задача '. Это звучит как асинхрон. Уловитель попытки не поймает те же исключения, что и синхронный метод. Если ваша задача async выдает исключение, она не поймает эту попытку ... catch. Вы должны добавить непрерывную задачу, которая обрабатывает исключения. –

+0

... или использовать async/await –

+0

Я обновил сообщение, так что SaveChangesAsync является асинхронным и base.SaveChangesAsync() является ожидаемым, но я до сих пор не понимаю, как избавиться от избыточного кода try/catch/catch/catch , – BBauer42

ответ

1

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

public override Task<int> SaveChangesAsync() 
{ 
    return new Task(SaveChanges); 
} 
+0

У него есть асинхронная версия операции, поэтому создается новый поток, чтобы просто сидеть и ничего не делать во время работы * когда у него уже есть инструменты, чтобы не делать этого *, это довольно плохая идея. Вы также возвращаете нестандартную задачу здесь, что является «действительно плохой идеей». – Servy

-1

Я решил решить это с помощью делегата (Func). Вот два метода «duplicate» .SaveChanges.

public override int SaveChanges() 
{ 
    return SaveChangesWrapper<int>(() => base.SaveChanges()); 
} 
public override async Task<int> SaveChangesAsync() 
{ 
    return await SaveChangesWrapper<Task<int>>(async() => await base.SaveChangesAsync()); 
} 

И вот обертка.

private T SaveChangesWrapper<T>(Func<T> function) 
{ 
    try 
    { 
     //do some work up front.... 

     return function(); 
    } 
    catch (DbEntityValidationException ex) 
    { 
     //parse exception and rethrow... 
    } 
    catch (DbUpdateConcurrencyException ex) 
    { 
     //parse and rethrow... 
    } 
    catch (DbUpdateException ex) 
    { 
     //parse and rethrow... 
    } 
    catch (Exception ex) 
    { 
     //parse and rethrow.. 
    } 
} 
+0

Если метод 'async' генерирует исключение в блоке' try', то этот метод не бросает, он успешно возвращает 'Task' (то есть в состоянии ошибки). Это часть того, что вы «платите» за конечный автомат «async», но в результате блоки «catch» никогда не будут удалены с помощью «async» версии этого кода. – Servy

+0

Ну, легкое место для начала - рефакторинг тела блоков 'catch' в отдельные методы, которые создают новые завернутые исключения, так что они могут быть вызваны обоими методами, которые у вас есть. Это избавляет от худшего дублирования кода, даже если не все. Я не вижу способа на первый взгляд сделать лучше, чем это. – Servy

+0

Не уверен, что вы здесь верны. Я просто поместил исключение с нулевым исключением в блок асинхронного try, и он попадает под последний улов, а затем повторно бросается. Я вижу повторное исключение в моем обработчике, и оно распространяется обратно через мой метод api 2 для клиента, как и ожидалось. – BBauer42