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 givenquizGuidis 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.GetNextQuestionmethod, 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?
QuestionResult, similar to yourResponseMessage. It can have aStatusenumeration representing these possible states; perhaps even a message. Then you'd just haveQuestion nextQuestion = QuizHelper.GetNextQuestion(db, theQuiz); var response = new ResponseMessage() { Status = nextQuestion.Status }; if (nextQuestion.Exists) response.NextQuestion = new NextQuestionResponse(nextQuestion, theQuiz); return response;. Then yourtry/catchcan catch truly exceptional cases.Int32.TryParse, only maybe it'd tell you that it failed because the input was out of range vs bad format). I won't post an answer; the "simplified" FrankPl's answer is more or less what I was aiming for.ResponseMessage? Then why not make theEnumthere instead of the strings or maybe with a 1:1 mapping between the different status enum values and their string representation sent to the client?