Welcome to Part 3 of this review of the Pluralsight course Build Your Own Application Framework with ASP.NET MVC 5 by Matt Honeycutt.
Matt is a software architect specializing in ASP.NET MVC web applications. He has over a decade of experience creating web applications. As an avid practitioner of Test-Driven Development, he has created both the SpecsFor and SpecsFor.Mvc frameworks.
Matt has served as the lead developer on numerous multi-million dollar software projects and enjoys finding elegant solutions to difficult problems.
On Github, he has published the Heroic Framework and the Fail Tracker web app. These projects feature much of the code and techniques that are covered in this course.
Also in this series:
Part 1 -What is an Application Framework?
Part 2 – The Power of an Inversion of Control Container
Part 3 – Optimize Your Controller Layer
Part 4 – Optimize Your View Layer
Part 5 – Optimize Your JavaScript
Optimize Your Controller Layer
Introduction
There are several reasons for optimizing our controller layer:
- Eliminate Errors – by reducing the amount of code we write
- Reduce duplication – by eliminating repetitive patterns with conventions
- Provide consistency – for our users and our developers
- Improve productivity – by allowing us to focus on the business problems
This module describes and demonstrates six useful techniques:
- Simplifying tedious mapping code
- Convention-based mapping configuration
- Eliminating magic strings from our controllers
- Standardizing feedback
- Creating a controller super-type
- Eliminating common code with action filters
Mapping With AutoMapper
This lesson installs AutoMapper and simplifies the configuration by using conventions.
For AutoMapper articles from it’s creator, such as how to Integrate AutoMapper with ASP.NET Core DI, see Los Techies.
It’s easy to install AutoMapper with Nuget:
PM> Install-Package AutoMapper
With this installed, we see how to refactor our YourIssuesWidget action method to eliminate the tedious property assignment.
This involves projecting the query result to our ViewModel.
Initially we see another yellow screen of death: Missing map from Issue to IssueSummaryViewModel.
We fix this by creating this map in a new class AutoMapperConfig. This uses the IRunAtInit interface that we created in the previous module of this course.
Once this is added, we can dramatically reduce the amount of code in our IssueController.
Simplifying Our Mapping
We can improve things further with a little refactoring.
AutoMapper can also pool from subproperties on the source object based on naming conventions.
We see the amount of code in our AutoMapperConfig class drop down and down more.
Building Conventions for Mapping
As our app grows, the amount of configuration will also grow. So we don’t want to have all of this configuration in one class.
AutoMapper provides a profile class that we can derive from. However, Matt prefer a different approach.
Instead of telling AutoMapper to create each map every time, we can set it up to automatically create maps for us.
Matt’s preferred approach is put to the mapping code inside the ViewModel.
We see each ViewModel being updated to inherit from IMapFrom
Matt has published much of this code on Github using the name Heroic Framework.
In this lesson, Matt explains the generic interface IMapFrom, and the IHaveCustomMappings interface.
The code written in our AutoMapperConfig is also similar to the HeroicAutoMapperConfigurator.
Eliminating Magic Strings
C# is a statically typed language, but out of the box MVC encourages us to use magic strings which are not validated by the compiler, so any typos raise the risk of poor code not being caught before the code is released to production!
Using a tool such as ReSharper can help alert the problem to us, but the code still compiles fine.
Fortunately Microsoft have also developed Microsoft.AspNet.Mvc.Futures which includes several features including strongly typed views.
Unfortunately development on this project stopped in late 2013. I guess that this was related to the shift of focus that ASP.NET Core brought.
Nevertheless, this is still a useful tool, and Matt says he’s used it in over a dozen projects without running into any problems.
With MVC Futures installed we can replace our magic strings with strong typing:
//return RedirectToAction(“Index”, “Home”);
return this.RedirectToAction<HomeController>(c=>c.Index());
Feedback Overview
There’s a bunch of inconsistent ways to display feedback to the user. We can standardize this using the decorator pattern.
If you’ve never come across the decorator pattern before there’s a module in the Design Pattern Library course, and Jeremy Clark also covers it in his Design Patterns On Ramp course. However, an in depth understanding of this pattern isn’t necessary to follow along here.
Matt explains that we want to create a single class that can be applied to any ActionResult.
Standardizing Feedback with Bootstrap
If you’re not already using Bootstrap, check out Shawn Wildermuth’s Bootstrap 3 course to learn it.
In this lesson we learn how to use Bootstrap’s Alerts feature to display success and failure messages to the user.
AlertExtensions.cs
public static ActionResult WithSuccess(this ActionResult result, string message) {
return new AlertDecoratorResult(..)
}
public static ActionResult WithInfo(this ActionResult result, string message) {..}
public static ActionResult WithWarning(this ActionResult result, string message) {..}
public static ActionResult WithError(this ActionResult result, string message) {..}
_Layout.cshtml
In order to keep our view clean, we encapsulate the logic for displaying an alert in a partial view:
@Html.Partial(” Alerts”)
In our Partial view we check to see if there are any alerts in TempData:
@if (TempData.GetAlerts().Any())
{
.
}
In the IssueController, we update it with WithSuccess and WithError extension methods.
Finally, Matt shows us an improvement we can make with a little CSS. We’ll build on this in the final module on JavaScript optimization.
Building a Controller Layer Supertype
There are some things that we want to share across all of our controllers, for example:
Helpers that construct custom Action Results that encapsulate common operations
The Layer Supertype pattern was first described in the book Patterns of Enterprise Architecture, but you don’t need to buy this book to understand this pattern. It is basically just a very fancy term for inheritance, or another term for what is essentially the template method pattern.
“It’s not uncommon for all the objects in a layer to have methods you don’t want to have duplicated throughout the system. You can move all of this behavior into a common Layer Supertype.” – Martin Fowler
The Layer Supertype is extremely easy to implement. We create a base class controller:
public abstract class FailTrackerController : Controller
{
//wrap the extension method that’s defined in the ControllerExtensions class
protected ActionResult RedirectToAction<IController>(
Expression<Action><TController>> action)
where TController : Controller
{
return ControllerExtensions.RedirectToAction(this, action);
}
}
Then update all of our existing controllers to reference our new controller
public class AccountController : FailTrackerController
{…}
Custom Action Filters Revisited
Each time we render a view that uses a dropdown list, even if it is after a postback,
we have to remember to repopulate the properties that contain the SelectListITems for that dropdown
public class IssueTypeSelectListPopulatorAttribute : ActionFilterAttribute
{
}
public interface IHaveIssueTypeSelectList
{
SelectListItem[] AvailableIssueTypes { get; set; }
}
public class UserSelectListPopulatorAttribute : ActionFilterAttribute
{
}
public interface IHaveUserSelectList
{
SelectListItem[] AvailableUsers { get; set; }
}
Global Filters are effectively singletons. They are not recreated for each request
and therefore won’t be able to take advantage of our Container and Transaction Per Request
features that we implemented
For Enums, things are better in MVC 5.1