Skip to content

Instantly share code, notes, and snippets.

@chakrit
Created June 11, 2010 18:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save chakrit/434894 to your computer and use it in GitHub Desktop.
Save chakrit/434894 to your computer and use it in GitHub Desktop.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Web.Mvc;
using System.Web.Routing;
namespace MyApp
{
public static class FluentMapRoute
{
public static RouteCollection Using<TController>
(this RouteCollection routes, Action<FluentMapRouteSyntax<TController>> mapper)
where TController : ControllerBase
{
mapper(new FluentMapRouteSyntax<TController>(routes));
return routes;
}
}
public class FluentMapRouteSyntax<TController>
where TController : ControllerBase
{
private static ISet<string> _namespaces;
private RouteCollection _routes;
public FluentMapRouteSyntax(RouteCollection routes)
{
_routes = routes;
_namespaces = _namespaces ?? new HashSet<string>();
}
public FluentMapRouteSyntax<TController> Handle
(string url, Expression<Action<TController>> action)
{
var dict = new RouteValueDictionary();
// get controller name from the type
var controllerType = typeof(TController);
var controller = controllerType.Name;
if (controller.EndsWith("Controller"))
controller = controller.Substring(0, controller.Length - "Controller".Length);
dict["Controller"] = controller;
// ensure the controller namespace is included
_namespaces.Add(controllerType.Namespace);
// get action name from the method name in the expression
var lambdaExpr = (LambdaExpression)action;
var callExpr = (MethodCallExpression)lambdaExpr.Body;
dict["Action"] = callExpr.Method.Name;
// get defaults for each arguments/params from the expression
// using the supplied value as the default value
var callArgs = callExpr.Arguments
.Select(arg => arg.CanReduce ? arg.Reduce() : arg)
.Cast<ConstantExpression>()
.Select(arg => arg.Value);
var parameters = callExpr.Method
.GetParameters()
.Select(param => param.Name);
var args = parameters.Zip(callArgs, Tuple.Create);
foreach (var pair in args) {
dict.Add(pair.Item1, pair.Item2);
}
// construct a new route from the finished dictionary
var route = new Route(url, new MvcRouteHandler()) {
Defaults = dict,
DataTokens = new RouteValueDictionary {
{ "namespaces", _namespaces.ToArray() }
}
};
_routes.Add(route);
// return self to enable chaining
return this;
}
}
}
using System.Web.Routing;
using ByMenu.Controllers;
namespace MyApp
{
public static class Routes
{
public static void Initialize(RouteCollection routes)
{
routes
.Using<HomeController>(x => x
// This will map "" to Controller=HomeController and Action=Index
.Handle("", c => c.Index())
// This will map "test/{name}" to Controller=HomeController and Action=Test
// with the name parameter defaults to "Hello World!"
.Handle("test/{name}", c => c.Test("Hello World!"))
)
// All chainable just because it's fun :)
.Using<FooContorller>(x =>
{
// use braces and separate lines for a little better readability
x.Handle("another", c => c.Action());
x.Handle("url", c => c.Action2(1, 2, 3));
})
// Or just use another method altogether
.Using<BarController>(MapBarUrls);
}
// this method could as well be moved into the controller itself
// if you're like me and prefer a weird sense of locality of code :)
public static void MapBarUrls(FluentMapRouteSyntax<BarController> x)
{
x.Handle("foo", x => x.Foo());
x.Handle("bar", x => x.Bar());
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment