Let me give you an example. I have the following web method inside my aspx.cs file which I use for AJAX calls:
[WebMethod]
public static ResponseMessage GetNextQuestion(string quizGuid)
{
using (DbEntities db = new DbEntities())
{
Quiz theQuiz = Quiz.Get(db, DataValidationHelper.GetGuid(quizGuid));
try
{
Question nextQuestion = QuizHelper.GetNextQuestion(db, theQuiz);
return new ResponseMessage() { Status = "Success", NextQuestion = new NextQuestionResponse(nextQuestion, theQuiz) };
}
catch (QuizNotFoundException)
{
return new ResponseMessage() { Status = "QuizNotFound" };
}
catch (QuizInvalidException)
{
return new ResponseMessage() { Status = "QuizInvalid" };
}
catch (QuizOverException)
{
return new ResponseMessage() { Status = "QuizOver" };
}
catch (QuestionTimedOutException)
{
return new ResponseMessage() { Status = "QuestionTimedOut" };
}
catch (Exception ex)
{
return new ResponseMessage() { Status = "Error", ErrorMessage = ex.Message };
}
}
}
The QuizHelper.GetNextQuestion
method generates a new question from the database and in some specific cases throws the following exceptions:
QuizNotFoundException
: When a quiz with the givenquizGuid
is not found in the database.QuizInvalidException
: Thrown for security purposes, for example when someone tries to hack HTTP requests.QuizOverException
: Every quiz has 10 questions and when the user tries to get 11th question using theQuizHelper.GetNextQuestion
method, this exception is thrown.QuestionTimedOutException
: You have to answer a question within a given time. If you don't, this exception is thrown.Exception
: All other exceptions are grouped under this for the sole purpose of informing the user that an error has occured, for UX purposes.
Then inside the Javascript file, the ResponseMessage.Status
is checked and corresponding action is taken.
I know that using exceptions as they are used in this code to control the flow is bad but making it this way is more intuitive and is much simpler. Not to mention the fact that the code is easier to understand for an outsider.
I am not sure how this code could be rewritten in the "right way" without exceptions but at the same time conserving its simplicity.
Am I missing something, any ideas?
UPDATE: Some answers propose using an Enum to return the status of the operation but I have lots of operations and they all may result in different scenarios (i.e. I cannot use the same Enum for all operations). In this case creating one Enum per operation does not feel to be the right way to go. Any improvements over this model?
There is no any need here to manage flow via exception (apart may be
Exception
andTimeOutException
). Remember that exception as itself a heavy enough artifact, and not suitable for fluid control flows and also as name suggests: it's for exceptional situations.There are places where you simply can not avoid exception baced flow, like for example: when you are working with devices. It's a very common command behavior based on exceptions recieved from device or IO.
But, as I said, it doesn't seem to be your case, so just handle it with simple
if/else
, whenever it's possible.