Advanced Routing

As mentioned at the end of Chapter 9, Routing is simple to learn yet challenging to master. Here are a few advanced tips Phil recommends to simplify some otherwise tricky routing scenarios.

RouteMagic

In Chapter 9, we mentioned the RouteMagic project, which is an open source project available on CodePlex at http://routemagic.codeplex.com/.

UnFigure
Install-Package RouteMagic.Mvc

This project is also available as a NuGet package appropriately named RouteMagic. RouteMagic is a pet project of Phil Haack, one of the authors of this book, and provides useful extensions to ASP.NET Routing that go above and beyond what's included “in the box.”

One useful extension included in the RouteMagic package is support for redirect routes. As noted usability expert Jakob Nielsen has recommended, “persistent URLs don't change,” and this redirect routes will help you support that.

One of the benefits of routing is that you can change your URL structure all you want during development by manipulating your routes. When you do so, all the URLs in your site are updated automatically to be correct, which is a nice feature. But once you deploy your site to the public, this feature becomes a detriment as others start to link to the URLs you've deployed. You don't want to change a route at this point and break every incoming URL.

Unless…you properly redirect. After installing RouteMagic, you'll be able to write redirect routes which take in an old route and redirect it to a new route, like so:

var newRoute = routes.MapRoute("new", "bar/{controller}/{id}/{action}");routes.Redirect(r => r.MapRoute("oldRoute",   "foo/{controller}/{action}/{id}")).To(newRoute);

For more information on RouteMagic, visit the RouteMagic CodePlex website. We think you'll find it to be an indispensable tool for your routing needs.

Editable Routes

In general, once you deploy your ASP.NET MVC application, you can't change the routes for your application without recompiling the application and redeploying the assembly where your routes are defined.

This is partly by design because as routes are generally considered application code, and should have associated unit tests to verify that the routes are correct. A misconfigured route could seriously tank your application.

Having said that, there are many situations in which the ability to change an application's routes without having to recompile the application comes in very handy, such as in a highly flexible content management system or blog engine.

In this next section, you see how to look at defining routes in a content file as code. You'll then place that file in a Config folder in the application's web root, as shown in Figure 14.3.

Note that you're also using Visual Studio's Properties dialog to mark the file's Build Action as “Content” so that it's not compiled into the application, as illustrated in Figure 14.4.

The authors have intentionally excluded the Route.cs file from build-time compilation because they want it to be compiled dynamically at run time. The code for Route.cs is shown in Listing 14.1. Don't worry about entering this code manually; it's provided as a NuGet package at the end of this section.

Listing 14.1 using System.Web.Mvc; using System.Web.Routing; using EditableRoutesWeb;public class Routes : IRouteRegistrar {    public void RegisterRoutes(RouteCollection routes)     {         routes.IgnoreRoute("{resource}.axd/{*pathInfo}");        routes.MapRoute(             "Default",            "{controller}/{action}/{id}",            new {                            controller = "Home",                            action = "Index",                            id = UrlParameter.Optional }        );     } }

One thing you'll notice is that this class implements an interface named IRouteRegistrar. This is an interface we created and added to our web application (although it could be defined in another assembly).

The code in Global.asax.cs for this application simply calls a new an extension method to register the routes, as shown in Listing 14.2.

Listing 14.2 protected void Application_Start(){    AreaRegistration.RegisterAllAreas();    RouteTable.Routes.RegisterRoutes("∼/Config/Routes.cs");}

This all seems simple enough, but that's because we've hidden all the magic in that extension method. We're using two tricks that will allow us to dynamically generate the routing code in medium trust, without causing an application restart:

1. We use the ASP.NET BuildManager to dynamically create an assembly from the Routes.cs file. From that assembly, we can create an instance of the type Routes and cast it to IRouteHandler.

2. We use an the ASP.NET Cache to get a notification of when the Routes.cs file changes, so we'll know it needs to be rebuilt. The ASP.NET Cache allows us to set a cache dependency on a file and a method to call when the file changes (invalidating the cache).

With those two tricks, we can add a cache dependency pointing to Routes.cs and a callback method that will reload the routes when Routes.cs is changed, as shown in Listing 14.3.

Listing 14.3 using System;using System.Web.Compilation;using System.Web.Routing;namespace EditableRoutesWeb{public static class RouteRegistrationExtensions{    public static void RegisterRoutes(this RouteCollection routes,                                 string virtualPath)    {        ConfigFileChangeNotifier.Listen(             virtualPath, vp => routes.ReloadRoutes(vp));    }    static void ReloadRoutes(             this RouteCollection routes, string virtualPath)    {        var assembly = BuildManager.GetCompiledAssembly(virtualPath);        var registrar =              assembly.CreateInstance("Routes") as IRouteRegistrar;        using(routes.GetWriteLock())        {            routes.Clear();            registrar.RegisterRoutes(routes);        }    }}}

This makes use of a ConfigFileChangeNotifier from ASP.NET team member David Ebbo's work on the ASP.NET Dynamic Data scaffolding system, as shown in Listing 14.4.

download
Listing 14.4 using System;using System.Collections.Generic;using System.Web;using System.Web.Caching;using System.Web.Hosting;namespace EditableRoutesWeb{    public class ConfigFileChangeNotifier    {        private ConfigFileChangeNotifier(Action<string> changeCallback)            : this(HostingEnvironment.VirtualPathProvider,              changeCallback)        {         }        private ConfigFileChangeNotifier(VirtualPathProvider vpp,        Action<string> changeCallback) {            _vpp = vpp;            _changeCallback = changeCallback;        }        VirtualPathProvider _vpp;        Action<string> _changeCallback;        // When the file at the given path changes,         // we'll call the supplied action.        public static void Listen(            string virtualPath, Action<string> action) {            var notifier = new ConfigFileChangeNotifier(action);            notifier.ListenForChanges(virtualPath);        }        void ListenForChanges(string virtualPath) {            //Get a CacheDependency from the BuildProvider, so             // that we know anytime something changes            var virtualPathDependencies = new List<string>();            virtualPathDependencies.Add(virtualPath);            CacheDependency cacheDependency = _vpp.GetCacheDependency(                virtualPath, virtualPathDependencies, DateTime.UtcNow);            HttpRuntime.Cache.Insert(virtualPath /*key*/,                    virtualPath /*value*/,                    cacheDependency,                    Cache.NoAbsoluteExpiration,                    Cache.NoSlidingExpiration,                    CacheItemPriority.NotRemovable,                    new CacheItemRemovedCallback(OnConfigFileChanged));        }        void OnConfigFileChanged(string key, object value,        CacheItemRemovedReason reason) {            // We only care about dependency changes            if (reason != CacheItemRemovedReason.DependencyChanged)                return;            _changeCallback(key);            // Need to listen for the next change            ListenForChanges(key);        }    }}

With this in place, we can now change routes within the Routes.cs file in the Config directory after we've deployed the application without recompiling our application.

note

Technically, a recompilation is happening, but it's happening dynamically at run time when the file changes and there's no need to restart the entire App Domain, which is one benefit of this approach over using the code in App_Code.

 

note
UnFigure

Note that this code is included as part of the RouteMagic NuGet package. Rather than copying this code into your project, you can simply run the command Install-Package RouteMagic and get going right away.

All the source code presented in this section is also available as a package. In an ASP.NET MVC 3 application, run the following command, Install-Package Wrox.ProMvc3.Routing.EditableRoutes, and then replace the call to RegisterRoutes with the following method call in Global.asax:

RouteTable.Routes.RegisterRoutes("∼/Config/Routes.cs");
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.46.141