Reputation: 2553
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:
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 //>
}
Upvotes: 1
Views: 267
Reputation: 1038830
You could pass the ModelState
instance to your queries
layer and it will take care to add the error:
public ApplicationViewModel GetApplicationViewModel(Guid guid, ModelStateDictionary modelState)
{
var applicationViewModel = new applicationViewModel();
if (!_applicationServices.Exists(guid))
{
modelState.AddModelError("", "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;
}
and in your view:
@model MyApp.Web.Areas.Application.Models.ApplicationViewModel
@Html.ValidationSummary()
@if (ViewData.ModelState.IsValid)
{
<!-- Display "normal" content here //>
}
Upvotes: 2