В ASP.NET MVC для генерации путей (url) для действий (action) контроллеров (controller) можно использовать методы-расширения объекта UrlHelper, такой как Url.Action(string actionName, string controllerName). Но этот метод использует нетипизированные параметры, просто имена action и controller, где легко можно ошибиться. А главный недостаток – нет поддержки рефакторинга, и если вы захотите переименовать действие контроллера – вам придется нудно и без гарантий от ошибки переименовывать их вручную.
Но если внимательно посмотреть – то можно найти замечательный класс – ExpressionHelper и его метод GetRouteValuesFromExpression, который по лямбде-выражению (expression) получит необходимые параметры роутинга. Потом, уже на основе этих параметров можно получить и путь. И конечно, с лямбда-выраженями полная поддержка рефакторинга (спасибо ReSharper за наше счастливое настоящее).
Все бы хорошо – но этот метод не принимает во внимание атрибут ActionName, с помощью которого можно изменить имя действия. Встроенная функция всегда возвращает имя метода, которое в 99% совпадает с именем действия, но не всегда.
Ниже представлен небольшой класс-хелпер, который решает проблему генерации путей к действиям контроллеров по типизированному выражению с учетом аттрибута ActionName.
1: using System;
2: using System.Linq;
3: using System.Linq.Expressions;
4: using System.Web.Mvc;
5: using System.Web.Routing;
6:
7: using Microsoft.Web.Mvc.Internal;
8:
9: namespace MediaOnline.Web.Helpers
10: {
11: public static class UrlExtensions
12: {
13: public static string Action<T>(this UrlHelper url, Expression<Action<T>> action) where T : Controller
14: {
15: return Action(url, null, action);
16: }
17:
18: public static string Action<T>(this UrlHelper url, string name, Expression<Action<T>> action) where T : Controller
19: {
20: return RouteTable.Routes.GetVirtualPath(url.RequestContext, name,
21: GetRouteValuesFromExpression(action)).VirtualPath;
22: }
23:
24: public static RouteValueDictionary GetRouteValuesFromExpression<TController>(Expression<Action<TController>> action) where TController : Controller
25: {
26: var route = ExpressionHelper.GetRouteValuesFromExpression(action);
27:
28: var body = (MethodCallExpression)action.Body;
29: var customName = (body.Method.GetCustomAttributes(typeof (ActionNameAttribute), false))
30: .Cast<ActionNameAttribute>().FirstOrDefault();
31:
32: if (customName != null)
33: {
34: route["Action"] = customName.Name;
35: }
36: return route;
37: }
38: }
39: }