Please note this is an excerpt from Chapter 9 of my ASP.NET MVC 5 with Bootstrap and Knockout.js book. In this chapter I provide a brief overview of the 5 different MVC filters and then over the next two chapters provide detail examples of each. The following post is leveraging the example of a Result Filter.
On a variety of actions inside your controllers you call the Automapper NuGet Package to convert from one model to another and you do not want to continue repeating this code each time.
Implement FilterAttribute with IResultFilter
Applying an MVC result filter to the action can execute the Automapper code after the action has been processed by the MVC framework and before the view is rendered.
Before I get to the example code, here is a quick summary of what an MVC Result Filter is:
Result filters provide two different functions that can be optionally implemented. The first is when the result has finished executing; for example, in an MVC controller once the view has been fully rendered and is ready to be returned from the server. The second is when the result is executing. This function would not have access to the final content.
Throughout the book, I was building upon an AuthorsController. The Index view would display a list of authors including pagination and sorting. The paging and sorting was accomplished by a class called QueryOptions and a list of the authors. These two properties are wrapped in another class called ResultList. This is the class that the Index view was bound to. These classes are not important for this example, but the background is required as the Result filter will be referencing them.
Installing the Automapper NuGet Package
Moving on with the Automapper Result filter. The first thing required is to install the Automapper NugGet Package. From the NuGet Package Manager Console, enter the following command to install the latest version: Install-Package AutoMapper. Next the filter itself needs to be created, I called it GenerateResultListFilterAttribute.
This class extends the FilterAttribute and implements the IResultFilter interface. In the constructor two Type parameters are accepted. The first is the source for the automapping. The second is the destination. Using these two types (and some reflection), the Automapper mapping is defined and executed. Below is the full source code for this class. Please note it references namespaces and classes from the sample project for my book.
[code]
using BootstrapIntroduction.Models;
using BootstrapIntroduction.ViewModels;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace BootstrapIntroduction.Filters
{
[AttributeUsage(AttributeTargets.Method)]
public class GenerateResultListFilterAttribute : FilterAttribute, IResultFilter
{
private readonly Type _sourceType;
private readonly Type _destinationType;
public GenerateResultListFilterAttribute(Type sourceType, Type destinationType)
{
_sourceType = sourceType;
_destinationType = destinationType;
}
public void OnResultExecuting(ResultExecutingContext filterContext)
{
var model = filterContext.Controller.ViewData.Model;
var resultListGenericType = typeof(ResultList<>).MakeGenericType(new Type[] { _destinationType });
var srcGenericType = typeof(List<>).MakeGenericType(new Type[] { _sourceType });
var destGenericType = typeof(List<>).MakeGenericType(new Type[] { _destinationType });
AutoMapper.Mapper.CreateMap(_sourceType, _destinationType);
var viewModel = AutoMapper.Mapper.Map(model, srcGenericType, destGenericType);
var queryOptions = filterContext.Controller.ViewData.ContainsKey(“QueryOptions”) ?
filterContext.Controller.ViewData[“QueryOptions”] :
new QueryOptions();
var resultList = Activator.CreateInstance(resultListGenericType, viewModel, queryOptions);
filterContext.Controller.ViewData.Model = resultList;
}
public void OnResultExecuted(ResultExecutedContext filterContext)
{
}
}
}
[/code]
Leveraging the OnResultExecuting
In the OnResultExecuting function, the source model is extracted into a variable. This would be set from the controller when calling: return View(model). Then, using some reflection, a new ResultList object is created that specifies the destination type of the list (of authors). At the end of this function, the new ResultList object is set that the view is data bound to. This replaces the previous model that was passed to the view in the controller.
To use this attribute, it needs to be applied to an action like the following example. This is an extract of the Index action from the AuthorsController that was built during the various examples throughout my book.
[code]
// GET: Authors
[GenerateResultListFilterAttribute(typeof(Author), typeof(AuthorViewModel))]
public ActionResult Index([Form] QueryOptions queryOptions)
{
var authors = authorService.Get(queryOptions);
ViewData[“QueryOptions”] = queryOptions;
return View(authors);
}
[/code]
As you can see by the attribute definition, the Result filter will map from the Author model to the AuthorViewModel. From the controller, a list of Author models are supplied to the view. When the result filter runs, it will replace the list of Author models with a ResultList that includes a list of AuthorViewModels. The full source code is available in my ASP.NET MVC 5 with Bootstrap and Knockout.js book.