I’d really like to improve the current readability of my code by reducing the amount of null checks I’m doing. I get the feeling that although checking for certain conditions is a good thing, repeating those checks in various places isn’t such a good idea. As an example, here’s one method of my PostsController:
public ActionResult ShowPost(int PostID, string slug)
{
PostViewModel viewModel = new PostViewModel();
Post model = postRepository.FindPost(PostID, filterByPublished: true);
if (model.PostID == 0)
return Redirect(Url.Home());
else if (model.Slug != slug)
return RedirectPermanent(Url.ShowPost(model.PostID, model.Slug));
postRepository.PostVisited(model);
Mapper.Map(model, viewModel);
return View(viewModel);
}
What I don’t like
Firstly, the check to see if PostID is 0. It can be 0 because of the way I set that method up in the repository:
public Post FindPost(int id, bool filterByPublished = false)
{
var query = db.Posts.Where(post => post.PostID == id);
if (filterByPublished)
query = query.Where(post => post.IsPublished == filterByPublished);
return query.Select(post => post).SingleOrDefault() ?? new Post { PostID = 0 };
}
I don’t like that I’ve pushed that little hack in there just to cater for handling a null model. I also check for a null model in various strongly-typed views which require a PostViewModel.
Possible solution
My first thought would be to create an action filter, override OnResultExecuting and check for null models there. I actually quite like this idea and it does solve the problems of checking for null models in the controller and also the view. However, what it doesn’t do is cater for situations like this:
else if (model.Slug != slug)
That’ll give me a null reference exception as would passing the model to the postRepository to update the view count. I’m guessing the call to Mapper.Map would do the same.
So what can do I about it? Could I simply override OnActionExecuted and check for exceptions there, logging them and then redirecting to a custom error view? Does that sound like a reasonable approach?
Thank you.
OK, let’s try putting this controller on a diet.
We start by fixing your repository and leaving it return null instead of some default post with ID = 0 which was kind of weird as you noticed:
then we could write a custom model binder for the
Postmodel:which could be associated with the
Posttype inApplication_Start:and then a custom action filter that will take care to short-circuit the action in case the post is null or the post slug is different than the one passed as parameter:
If you are using ASP.NET MVC 3 this custom attribute could be registered as a global one in
Global.asax:and finally your controller action will become:
or even bringing this a step further and introducing a custom mapper action filter:
The
AutoMapaction filter is pretty easy to implement. You would subscribe to theOnActionExecutedmethod and check whether the returned result from the controller action was aViewResultBaseand then extract the model from it, pass it to AutoMapper given the 2 types and substitute the model with the view model.