2013-10-10 4 views
0

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

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

// I wrap all code send back from service layer to controller in this class. 
    public class ResponseResult 
    { 

     public ResponseResult() 
     { 
      Errors = new Dictionary<string, string>(); 
      Status = new ResponseBase(); 
     } 

     public void AddError(string key, string errorMessage) 
     { 
      if (!Errors.ContainsKey(key)) 
      { 
       Errors.Add(key, errorMessage); 
      } 
     } 

     public bool IsValid() 
     { 
      if (Errors.Count > 0) 
      { 
       return false; 
      } 

      return true; 
     } 


     public Dictionary<string, string> Errors { get; private set; } 


     public ResponseBase Status { get; set; } 
    } 

    public class ResponseResult<T> : ResponseResult 
    { 

     public T Response { get; set; } 
    } 

    public class ResponseBase 
    { 
     public HttpStatusCode Code { get; set; } 
     public string Message { get; set; } 
    } 

Вариант 1 (то, что я использую сейчас)

//controller 
    public HttpResponseMessage GetVenue(int venueId) 
      { 
       if (venueId == 0) 
       { 
        ModelState.AddModelError("badVenueId", "venue id must be greater than 0"); 

       if (ModelState.IsValid) 
       { 
        var venue = venueService.FindVenue(venueId); 
        return Request.CreateResponse<ResponseResult<Venue>>(venue.Status.Code, venue); 
       } 

       // a wrapper that I made to extract the model state and try to make all my request have same layout. 
       var responseResult = new ResponseResultWrapper(); 
       responseResult.Status.Code = HttpStatusCode.BadRequest; 
       responseResult.Status.Message = GenericErrors.InvalidRequest; 
       responseResult.ModelStateToResponseResult(ModelState); 

       return Request.CreateResponse<ResponseResult>(responseResult.Status.Code, responseResult); 
      } 

// service layer   
      public ResponseResult<Venue> FindVenue(int venueId) 
      { 
       ResponseResult<Venue> responseResult = new ResponseResult<Venue>(); 

       try 
       { 
        // I know this check was done in the controller but pretend this is some more advanced business logic validation. 
        if(venueId == 0) 
        { 
         // this is like Model State Error in MVC and mostly likely would with some sort of field. 
         responseResult.Errors.Add("badVenueId", "venue id must be greater than 0"); 
         responseResult.Status.Code = HttpStatusCode.BadRequest; 
        } 

        var venue = context.Venues.Where(x => x.Id == venueId).FirstOrDefault(); 

        if(venue == null) 
        { 
         var foundVenue = thirdPartyService.GetVenue(venueId); 

         if(foundVenue == null) 
         { 
          responseResult.Status.Code = HttpStatusCode.NotFound; 
          responseResult.Status.Message = "Oops could not find Venue"; 

          return responseResult; 
         } 
         else 
         { 
          var city = cityService.FindCity(foundVenue.CityName); 

          if(city == null) 
          { 
           city = cityService.CreateCity(foundVenue.CityName); 

           if(city.Response == null) 
           { 
           responseResult.Status.Code = city.Status.Code; 
           responseResult.Status.Message = city.Status.Message; 

           return responseResult; 
           } 

           CreateVenue(VenueId, city.Response, foundVenue.Name); 

           responseResult.Status.Code = HttpStatusCode.Ok; 
           // I don't think I would return a success message here as the venue being displayed back to the user should be good enough. 
           responseResult.Status.Message = ""; 

           reponseResult.Response = foundVenue; 
          } 
         } 

         return responseResult; 
        } 

       } 
       catch (SqlException ex) 
       { 
        ErrorSignal.FromCurrentContext().Raise(ex); 
        responseResult.Status.Code = HttpStatusCode.InternalServerError; 
        responseResult.Status.Message = GenericErrors.InternalError; 

        // maybe roll back statement here depending on the method and what it is doing. 
       } 
       // should I catch this, I know it should be if you handle it but you don't want nasty messages going back to the user. 
       catch (InvalidOperationException ex) 
       { 
        ErrorSignal.FromCurrentContext().Raise(ex); 
        responseResult.Status.Code = HttpStatusCode.InternalServerError; 
        responseResult.Status.Message = GenericErrors.InternalError; 
       } 
       // should I catch this, I know it should be if you handle it but you don't want nasty messages going back to the user. 
       catch (Exception ex) 
       { 
        ErrorSignal.FromCurrentContext().Raise(ex); 
        responseResult.Status.Code = HttpStatusCode.InternalServerError; 
        responseResult.Status.Message = GenericErrors.InternalError; 
       } 

       return responseResult; 
      } 

// another service layer. 

     // it is ResponseResult<City> and not city because I could have a controller method that directly calls this method. 
      // but I also have a case where my other method in another service needs this as well. 
      public ResponseResult<City> CreateCity(string CityName) 
      { 
       ResponseResult<City> responseResult = new ResponseResult<City>(); 
       try 
       { 
        City newCity = new City { Name = "N" }; 
        context.Cities.Add(newCity); 
        context.SaveChanges(); 

        responseResult.Status.Code = HttpStatusCode.Ok; 
        responseResult.Status.Message = "City was succesfully added"; 
       }   
       // same catch statmens like above 
       catch (SqlException ex) 
       { 
        ErrorSignal.FromCurrentContext().Raise(ex); 
        responseResult.Status.Code = HttpStatusCode.InternalServerError; 
        responseResult.Status.Message = GenericErrors.InternalError; 

        // maybe roll back statement here depending on the method and what it is doing. 
       } 
       return responseResult; 
      } 

Как вы можете видеть эти методы все обернутые в кодах состояния, поскольку они могут быть непосредственно называют контроллером будучи публично. FindCity() и CreateVenue() могут также иметь эту упаковку.

Вариант 2

public HttpResponseMessage GetVenue(int venueId) 
     { 
      try 
      { 
       if (venueId == 0) 
       { 
        ModelState.AddModelError("badVenueId", "venue id must be greater than 0"); 

       if (ModelState.IsValid) 
       { 
        var venue = venueService.FindVenue(venueId); 
        return Request.CreateResponse<ResponseResult<Venue>>(HttpSatusCode.Ok, venue); 
       } 

       // a wrapper that I made to extract the model state and try to make all my request have same layout. 
       var responseResult = new ResponseResultWrapper(); 
       responseResult.Status.Code = HttpStatusCode.BadRequest; 
       responseResult.Status.Message = GenericErrors.InvalidRequest; 
       responseResult.ModelStateToResponseResult(ModelState); 

       return Request.CreateResponse<ResponseResult>(responseResult.Status.Code, responseResult); 
      } 
      catchcatch (SqlException ex) 
      { 
       // can't remember how write this and too tried to look up. 
       return Request.CreateResponse(HttpStatusCode.InternalServerError;, "something here"); 
      } 
     } 

public Venue FindVenue(int venueId) 
     { 
      try 
      { 
       // how to pass back business logic error now without my wrapper? 
       if(venueId == 0) 
       { 
        // what here? 
       } 

       var venue = context.Venues.Where(x => x.Id == venueId).FirstOrDefault(); 

       if(venue == null) 
       { 
        var foundVenue = thirdPartyService.GetVenue(venueId); 

        if(foundVenue == null) 
        { 
         // what here? 
        } 
        else 
        { 
         var city = cityService.FindCity(foundVenue.CityName); 

         if(city == null) 
         { 
          city = cityService.CreateCity(foundVenue.CityName); 

          if(city == null) 
          { 
          // what here? 
          } 

          CreateVenue(VenueId, city.Response, foundVenue.Name); 


         } 
        } 

        return venue; 
       } 

      } 
      catch (SqlException ex) 
      { 
       // should there be a try catch here now? 
       // I am guessing I am going to need to have this here if I need to do a rollback and can't do it in the controller 

       // throw exception here. Maybe this won't exist if no rollback is needed. 
      } 
      return null; 
     } 

     public City CreateCity(string CityName) 
     { 
      // if it crashes something I guess will catch it. Don't think I need to rollback here as only one statement being sent to database. 
      City newCity = new City { Name = "N" }; 
      context.Cities.Add(newCity); 
      context.SaveChanges(); 

      return newCity;    
     } 

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

Также с ловлей все в контроллере и отправки обратно ванильные объекты (без моей обертки) Я не уверен, как сделать зернистость HttpStatus коды (например, как NotFound, создавать и такой)

+0

Я делаю это: Избегайте исключения из простых вещей, таких как проверка, если объект == null. Выбросьте исключение, где происходит исключение. Обращайтесь с Исключением, где вы не хотите, чтобы это остановило приложение. – Tony

+2

Не перехватывайте исключения, с которыми вы не можете справиться. Самое худшее, что вы могли бы сделать, это передать его пользователю вашего веб-страницы, наименее вероятно, чтобы этот сервер dbase был исправлен. –

+0

http://stackoverflow.com/questions/2737328/why-should-i-not-wrap-every-block-in-try-catch/2737337#2737337 –

ответ

1

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

Если возникло непредвиденное исключение, если это то, с чем вы можете иметь дело (например, тайм-аут, который вы можете повторить), попытайтесь справиться с ним, иначе выйдите - просто подумайте, что делает какое-либо приложение MS - например. офис - вы получаете извинение, что что-то пошло не так, и приложение заканчивается. Лучше заканчивать изящно, чем потенциально повреждать данные и оставлять вещи в реальном беспорядке.

+0

Ну, это мое мышление. Если db идет вниз, я хочу поймать это и вернуться к дружественному пользователю сообщению. Моя проблема заключается в том, что у меня есть метод службы, вызывающий другой метод службы. Оба теперь имеют команды Catch для db. Оба будут обертывать возвращаемый объект кодом состояния и дружественным сообщением. Это правильная вещь? В этом случае второй бесполезно обертывается вокруг, где, возможно, метание будет лучше, но если кто-то вызовет этот метод напрямую, я скорее сделаю эту упаковку. Мое расположение уловов плохое или все в порядке? – chobo2

0

This статья с Java - конкретные концепции и примеры, но широкие принципы здесь - путь.

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

Независимо от того, что вы делаете, обязательно сохраните всю информацию об исключениях из источника.

Надеюсь, что это поможет.

0

Бросьте исключение, где ваш код определяет, что что-то пошло не так.

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

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

0

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

Вы сказали, что в вашем вопросе:

Теперь для меня пытаются вернуть сообщение об ошибке обратно к контроллеру и стараться не реально поймать что-нибудь в контроллере.

Если метод обслуживания должен идеально возвращать объект объекта, как вернуть это потенциальное сообщение об ошибке обратно на контроллер? параметр out? изменить тип возвращаемого значения на то, что на нем есть свойство сообщения об ошибке?

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

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

Например, не было бы удобно различать ошибки авторизации и не обнаруживать ошибки, чтобы вы могли вернуть наиболее подходящий код статуса http для пользователя?

Не забывайте, что класс Exception имеет свойство Message вы можете просто вернуться к пользователю, если вы хотите использовать его таким образом

+0

Да У меня есть свой собственный класс, который обертывает объект моего объекта, который содержит код состояния и дружественное сообщение. Если сказать, что Место встречи было найдено, тогда у меня будет успешное сообщение msg и «200», и оно будет возвращено вместе с объектом. Если это плохо, а db умирает, тогда код статуса «500» и сообщение об ошибке отправляются обратно с нулевым объектом «Место».Затем я могу проверить код состояния для 500 или Null на местах и ​​выполнить соответствующие действия. Я добавил очень высокий код lv в своем OP. Если я не буду обертывать объект чем-то, то контроллер должен будет генерировать сообщение об успешности. – chobo2

-2

Используйте try catch на всех уровнях и пузырь его. При необходимости зарегистрируйте ошибку в файле или базе данных. Я использую текстовый файл - вкладку с разделителем. Захват на каждом уровне 1. Название модуля (Использование C# поставляются методы, чтобы получить это) 2. Имя методы 3. выполняемого кода (Пользователь создан - «Подключение к базе данных») 4. Номер ошибки 5. Ошибки Описание 6. Выполняемый код (Пользователь создан - «Доступ к базе данных») 7. Номер ошибки для конечного пользователя 8. Описание ошибки для конечного пользователя Кроме того, я также передаю уникальный идентификатор типа - Идентификатор сеанса в случае Web, Идентификатор пользователя, имя пользователя (если доступно)

У меня всегда есть блок catch Exception. Здесь я устанавливаю номер ошибки как -0 и сообщение из объекта исключения в качестве описания ошибки. Если это связано с SQL Server - я захватываю SQL Exception. Это порождает номер ошибки - я использую это.

Я хочу расширить это еще немного.

+0

Я не понимаю, что ты говоришь, что поймаешь на всех уровнях. Если вы поймаете то, как это пузырится или вы бросаете его снова? – chobo2

0

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

В этой ситуации я настоятельно рекомендую вам поймать все исключения. «Необработанные» исключения - очень плохая форма. На веб-сайтах они приводят к отображению, которое варьируется от бессмысленного до опасного, раскрывая внутреннюю информацию, которую вы не хотите видеть публикой.

Если это программа хорошего размера, я предлагаю создать собственный класс MyException, который происходит из System.Exception. Цель этого - предоставить вам возможность добавлять дополнительную информацию, специфичную для вашего приложения.Вот некоторые типичные вещи, которые я хотел бы добавить к моим классам MyException:

  1. Идентификационный номер, который поможет мне найти местоположение в коде, где возникла проблема.
  2. Метод «LogMessage», который регистрирует исключение, иногда в журнале событий Windows. Независимо от того, регистрируетесь ли вы и какой журнал, который вы пишете, зависит от того, что вы хотите записать, и о серьезности ситуации.
  3. Индикатор, показывающий исключение, был зарегистрирован, поэтому вышеупомянутый метод не будет регистрироваться дважды, даже если он вызывается более одного раза.
  4. Что-нибудь еще, что может быть полезно с учетом обстоятельств.
  5. Я также хотел бы поместить текст сообщений во внешний файл ресурсов, например XML-документ, и указать их на номер ошибки, который вы назначили. Это позволяет вам изменить текст ошибки, чтобы улучшить ясность без необходимости повторного развертывания приложения.

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

На верхнем уровне (уровень приложения) НИКОГДА не допускайте необработанного исключения и никогда не бросайте свое собственное исключение. Лучший способ - вернуть код ошибки и сообщение в ваш контракт с данными. Таким образом, клиентское приложение получит только то, что вы хотите, чтобы они видели. Единственными исключениями, о которых им нужно будет беспокоиться, являются те, которые находятся за пределами вашей области действия, то есть ошибки конфигурации или сбои связи. Другими словами, если они могут ссылаться на вашу услугу, и сеть остается подключенной, вы должны дать им ответ, который они могут интерпретировать.

Надеюсь, это поможет.

PS Я не включил пример исключения, так как я уверен, что поиск немного найдет. Сообщение, если вы хотите, чтобы я выставил простой образец.

+0

Да, я согласен, что все они должны быть обработаны, и это то, что я делаю. Я просто не получаю, когда у меня есть метод, который в настоящее время вызывает мой метод, если он должен просто выбросить execption и позволить другому методу обрабатывать его, или если я должен поймать его и обработать его там, поскольку этот метод, бросающий его в будущем, может легко можно использовать сразу один день. – chobo2

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