In my MVC3 application, I have a “queries” class specific to each controller that performs conversions between domain entities and converts them to view models. I do this to keep my controllers clean and it’s easier to unit test the controllers and queries seperately.
There are cases, though, when the query method needs to pass non-exceptional error messages to the view (e.g. the entity was not found). However, since my controller is only receiving a ViewModel from the query method and not any type of return code, the only two options that I have found to pass this error are as follows:
-
Throw an Exception from the query method and use a Try/Catch block to catch the exception in the controller.
-
Add a property to the View Model called “ErrorMessage” which is populated by the query method that the view uses to perform logic of what needs displayed.
Because these aren’t exceptional cases, and I know I shouldn’t use Try/Catch to control program flow, I have choosen to use the second method. Though this works for now, it feels “dirty” to me for the following reasons:
- The controller has to receive a whole View Model when an error occurs just to get the ErrorMessage property.
- The view has to have hard-coded logic and two sections to display either the error or the normal content
- Though I could put the
if (Model.ErrorMessage != null)logic in my controller to determine which view to pass, it’s still not feeling like a “clean” solution.
Are there any design patterns that I could use that could help me refactor this code and make it cleaner?
Example View Model:
public class ApplicationViewModel
{
public string ErrorMessage { get; set; }
public int Id { get; set; }
public string Name { get; set; }
// Other properties here...
}
Example Controller Method:
public ActionResult Retrieve(Guid guid)
{
return View("Application", _applicationQueries.GetApplicationViewModel(guid));
}
Example ApplicationQueries Method:
public ApplicationViewModel GetApplicationViewModel(Guid guid)
{
var applicationViewModel = new applicationViewModel();
if (!_applicationServices.Exists(guid))
{
applicationViewModel.ErrorMessage = "The requested application does not exist.";
return applicationViewModel;
}
// More code here that checks things which might set the ErrorMessage property...
var application = _applicationServices.GetApplicationByGuid((Guid)guid);
Mapper.Map(grantApplication, grantApplicationViewModel);
return grantApplicationViewModel;
}
Snippit from Application.cshtml View for Error Handling:
@model MyApp.Web.Areas.Application.Models.ApplicationViewModel
if (Model.ErrorMessage != null)
{
<div>@Model.ErrorMessage</div>
}
else
{
<!-- Display "normal" content here //>
}
You could pass the
ModelStateinstance to yourquerieslayer and it will take care to add the error:and in your view: