AutoMapperPagedList

9 Jun

If you’re just looking for an example of how to use AutoMapperPagedList, drop down to the bottom and look at the last code sample.

A couple months ago (while at MIX 11, actually) I was playing with some of the new MVC3 Tooling, including EF4.1. To play with it, I was trying to build a simple blog, and for no real reason at all decided to use both AutoMapper and Scott Guthrie’s PagedList. For those unfamiliar with either, AutoMapper is a great way to transform your data access objects into view models, and PagedList makes it really simple to hook up an IQueryable to server-side paging.

First, an explanation of AutoMapper. In creating AutoMapper, Jimmy Bogard provided a super-useful way to automatically map one object to another type. The mapping is primarily based on conventions, so if you name the properties on the classes properly, you only have to do minimal configuration. For more details, see the examples on AutoMapper’s CodePlex site. For my simple blog, I wanted to use AutoMapper to flatten my Post model into a PostViewModel. Because PostViewModel was set up to easily work with AutoMapper, the only configuration I had to do was make sure that this line was called in my Global.asax:

AutoMapper.Mapper.CreateMap<Post, PostViewModel>();

Then when I want to convert a Post to a PostViewModel, I can just call this:

AutoMapper.Mapper.Map<Post, PostViewModel>(post);

Next, an explanation of PagedList. The Gu created a helpful class and extension methods for working with server-side paging. The extension method extends an IQueryable<T> and converts it to a PagedList<T>. PagedList<T> inherits from List<T>, so it has all the normal features of a List<T>, but it adds the interface IPagedList:

public interface IPagedList {
	int TotalCount { get; set; }
	int PageIndex { get; set; }
	int PageSize { get; set; }
	bool IsPreviousPage { get; }
	bool IsNextPage { get; }
}

The properties added by IPagedList can be used in your view to easily control Previous/Next buttons. Additionally, the constructor of PagedList calls Skip() and Take() on the IQueryable before calling ToList() and adding the resultant items to itself, meaning that the execution of the IQueryable happens inside of PagedList. This is important because AutoMapper won’t work as part of deferred execution, which is what is going on with the IQueryable. This means that the trying to use AutoMapper to map an IQueryable<Post> to an IQueryable<PostViewModel> just can’t happen.

So since I can’t use AutoMapper to send objects to PagedList, I had to use AutoMapper inside PagedList, after the IQueryable was executed and returned as a List. However, I didn’t want to muck around too much with the regular PagedList class, so I created a new class that inherits from it instead. This new class is called (quite creatively) MappedPagedList. It works the exact same way as PagedList, but takes one additional generic type and one additional parameter. It requires two generic types because AutoMapper needs a source type and destination type. The extra parameter, however, is the key. As parameter types go, it’s a doozy:

public static MappedPagedList<TSource, TOutput> ToPagedList<TSource, TOutput>(this IQueryable<TSource> source, int index, Func<IEnumerable<TSource>, IEnumerable<TOutput>> mapper, int pageSize = 10) {
	return new MappedPagedList<TSource, TOutput>(source, index, pageSize, mapper);
}

Let me break it down a little.
Func<IEnumerable<TSource>, IEnumerable<TOutput>>
looks intimidating, but it’s really just a delegate for a call to AutoMapper.Map<TSource, TOutput>(). In simpler terms, the parameter takes a method that has one parameter of type IEnumerable<TSource> and returns IEnumerable<TOutput>. The best way to understand it is probably to see an example of the method being called:

IQueryable<Post> posts = context.Posts.OrderByDescending(p => p.Timestamp).AsQueryable();
<PostViewModel> pagedList = posts.ToPagedList<Post, PostViewModel>(2, Mapper.Map<IEnumerable<Post>, IEnumerable<PostViewModel>>, 10);
return View(pagedList);

Because MappedPagedList<T,O> inherits from PagedList<T> (and PagedList<T> is the only part that we actually need in the view), the View only needs to be passed an instance of Paged<T>. The magic happens inside the constructor of MappedPagedList, where it uses the passed-in Func to map the List<T> to a List<O>.

If you’ve read this far, you’re probably getting desperate for the part where I say “…and here’s how you use it in your site”, so here you go.

//Controller
public ViewResult Index(int page = 0) {
	int pageSize = 10;
	IQueryable<Post> posts = context.Posts.OrderByDescending(p => p.Timestamp).AsQueryable();
	PagedList<PostViewModel> pagedList = posts.ToPagedList<Post, PostViewModel>(page, Mapper.Map<IEnumerable<Post>, IEnumerable<PostViewModel>>, pageSize);
	return View(pagedList);
}

//View
@if (Model.IsPreviousPage) {
	@Html.ActionLink("Previous", "Index", new { page = Model.PageIndex - 1 })
}
@(Model.PageIndex + 1) of @(Model.TotalCount / Model.PageSize)
@if (Model.IsNextPage) {
	@Html.ActionLink("Next", "Index", new { page = Model.PageIndex + 1 })
}

This will generate a previous button, a page indicator, a next button. The previous and next button are hidden if there isn’t a previous or next page. The controller takes page as an optional parameter, and passes that to the MappedPagedList to use in Skip().

So why would you want to use it? If you need to use AutoMapper to send view models to your view, but want super-easy paging support, this single file will save you a bunch of time and effort. I’ve put it on NuGet at http://nuget.org/List/Packages/AutoMapperPagedList, so feel free to pull it down and give it a try. It’s a single file, and the source is hosted on GitHub at https://github.com/davecowart/AutoMapperPagedList, so feel free to clone it, fork it, or do whatever. I also have an example MVC project on GitHub at https://github.com/davecowart/AutoMapperPagedList.Demo if you need a full-site example.

Named Routes in ASP.NET MVC 3

8 Jun

I’m a recent convert to named routes. Using a named route to create a link or URL gives a you level of explicitness that’s comforting in most situations and a lifesaver in others. In their simplest form, though, named routes lead to DRYer routing and can save you some serious maintenance headaches.

All routes are effectively named routes. Using a named route means to specify the route by name instead of allowing the routing engine to determine which one to use. This means that a route that you intend to reference by name is created the same as any other route:

routes.MapRoute(
	name: "Post",
	url: "Post/{id}",
	defaults: new { controller = "Posts", action = "Details", id = UrlParameter.Optional }
);

If you were to use this as a typical action link, you’d specify the link text, controller, action and any route values (in this case id) as parameters in Html.ActionLink(), like this:

@Html.ActionLink("Show", "Posts", "Details", new { id = 4 })

The downside of this technique is that if you were to change a controller name, change an action name or move an action, your link would break. And not just this link, but any ActionLink that points to that combination of controller and action. The other danger is that you have no guarantees as to which route will be pulled from the routing table. It’s easy to envision a scenario where you have to figure out which of 50 routes in the routing table is being used. With named routes, you avoid both of these problems, and as a side benefit your link code is shorter too. To use a named route link, you only need to specify the link text, the name of the route and any route values as parameters, like this:

@Html.RouteLink("Show", "Post", new { id = 4 })

This link is now protected against future change, takes up a little less space, and leaves no doubts as to which route will be utilized. The one negative is that there is now an extra step to find the action and controller used by the route, but in a sensibly-structured site it shouldn’t take a wild guess to figure it out. In my mind these benefits vastly outweigh the slight obfuscation. With named routes, you’ll always know what you get.

Extending Html.RouteLink()

7 Jun

The other day I was tasked with converting a raw HTML site into MVC3/Razor. Each individual HTML page had its own copy of the navigation menu, with the link to the current page manually set to include class=”current” in order to automatically expand the menu and style the link differently. However, I wanted to place the navigation menu into a partial so that I could reuse it between multiple layouts. That meant  needed to have a way to determine the current page, compare that to the URL in the link, and add the “current” class whenever the two matched.

I wanted to use named routes for these links. Named routes are great for maintainability, because the references to action and controller names are contained in a single place–Global.asax.  However, neither route links nor action links provide the functionality I was looking for, which meant I would need to extend the existing HtmlHelpers to add a new kind of link helper. I wanted to cause as little disruption as possible with the new helper, so I made sure to match the syntax of Html.RouteLink() as closely as possible.

First, I needed to create my helper method.

using System.Collections.Generic;
using System.Web.Routing;

namespace System.Web.Mvc.Html {
	public static class Html {
		public static MvcHtmlString NavigationRouteLink(this HtmlHelper<dynamic> htmlHelper, string linkText, string routeName, RouteValueDictionary routeValues = null, IDictionary<string, object> htmlAttributes = null) {
			return new MvcHtmlString(HtmlHelper.GenerateRouteLink(htmlHelper.ViewContext.RequestContext, RouteTable.Routes, linkText, routeName, routeValues, htmlAttributes));
		}
	}
}

Next, I needed a way to determine whether or not the current page’s URL matched the URL generated by the named route. To do that, I pulled the current request’s absolute path out of the HtmlHelper object, and I pulled the named route’s absolute path out of an instance of UrlHelper.

using System.Collections.Generic;
using System.Web.Routing;

namespace System.Web.Mvc.Html {
	public static class Html {
		public static MvcHtmlString NavigationRouteLink(this HtmlHelper<dynamic> htmlHelper, string linkText, string routeName, RouteValueDictionary routeValues = null, IDictionary<string, object> htmlAttributes = null) {
			if (htmlHelper.ViewContext.RequestContext.HttpContext.Request.Url.AbsolutePath == new UrlHelper(htmlHelper.ViewContext.RequestContext).RouteUrl(routeName))
				htmlAttributes.Add("class", "current");
			return new MvcHtmlString(HtmlHelper.GenerateRouteLink(htmlHelper.ViewContext.RequestContext, RouteTable.Routes, linkText, routeName, routeValues, htmlAttributes));
		}
	}
}

With that out of the way, I needed to make sure that I wouldn’t run into any problems if htmlAttributes were null (which is extremely likely).

using System.Collections.Generic;
using System.Web.Routing;

namespace System.Web.Mvc.Html {
	public static class Html {
		public static MvcHtmlString NavigationRouteLink(this HtmlHelper<dynamic> htmlHelper, string linkText, string routeName, RouteValueDictionary routeValues = null, IDictionary<string, object> htmlAttributes = null) {
			IDictionary<string, object> dict;
			if (htmlAttributes == null)
				dict = new Dictionary<string, object>();
			else
				dict = htmlAttributes;
			if (htmlHelper.ViewContext.RequestContext.HttpContext.Request.Url.AbsolutePath == new UrlHelper(htmlHelper.ViewContext.RequestContext).RouteUrl(routeName))
				dict.Add("class", "current");
			return new MvcHtmlString(HtmlHelper.GenerateRouteLink(htmlHelper.ViewContext.RequestContext, RouteTable.Routes, linkText, routeName, routeValues, dict));
		}
	}
}

As a possible improvement, I could allow the name of the assigned class to be passed in. Alternatively, I could provide another (required) htmlAttributes parameter, which would only be applied if the named route matched the current URL. However, given the small scope of the project and the extra effort involved, I decided I wasn’t going to need it. The final product was an easy-to-use HtmlHelper that allowed me to add navigation links to my heart’s content without having to stop and think about URL-comparison logic.

<div id="nav">
    <ul class="level1">
        <li>
            <a>Who We Are</a>
            <ul class="level2">
                <li>@Html.NavigationRouteLink("About Us", "About")</li>
                <li>@Html.NavigationRouteLink("The Blog", "Blog")</li>
            </ul>
        </li>
    </ul>
</div>

produces:

<div id="nav">
	<ul class="level1">
		<li>
			<a>Who We Are</a>
			<ul class="level2">
				<li><a class="current" href="/AboutUs">About Us</a></li>
				<li><a href="/Blog">The Blog</a></li>
			</ul>
		</li>
	</ul>
</div>