Strongly typed and well-controlled links within ASP.NET MVC areas
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
public class LinkWithinAreaAttribute : Attribute
{
public string AreaName { get; private set; }
public string OrSwitchTo { get; set; }
public LinkWithinAreaAttribute(string areaName)
{
AreaName = areaName;
}
}
public static class LinkWithinArea
{
public static RouteValueDictionary FixArea<TController>(this RouteValueDictionary routeValues)
{
var attr = (LinkWithinAreaAttribute) typeof(TController)
.GetCustomAttributes(typeof(LinkWithinAreaAttribute), inherit: true)
.SingleOrDefault();
if (attr != null && !IsInAlternativeArea(routeValues, attr))
routeValues["area"] = attr.AreaName;
return routeValues;
}
private static bool IsInAlternativeArea(RouteValueDictionary routeValues, LinkWithinAreaAttribute attr)
{
return attr.OrSwitchTo != null
&& routeValues.ContainsKey("area")
&& attr.OrSwitchTo.ToLowerInvariant() == routeValues["area"].ToString().ToLowerInvariant();
}
}
public static class AreaAwareLinkingExtensions
{
public static string Action<TController>(this UrlHelper helper, Expression<Action<TController>> action)
where TController : Controller
{
var vpd = helper.RouteCollection
.GetVirtualPathForArea(helper.RequestContext, GetFixedRouteValues(action));
return (vpd == null) ? null : vpd.VirtualPath;
}
public static void RenderAction<TController>(this HtmlHelper helper, Expression<Action<TController>> action)
where TController : Controller
{
var routeValues = GetFixedRouteValues(action);
foreach (var keyValuePair in helper.ViewContext.RouteData.Values)
{
if (!routeValues.ContainsKey(keyValuePair.Key))
routeValues.Add(keyValuePair.Key, keyValuePair.Value);
}
helper.RenderRoute(routeValues);
}
public static MvcHtmlString ActionLink<TController>(this HtmlHelper helper, Expression<Action<TController>> action, string linkText, object htmlAttributes = null)
where TController : Controller
{
return System.Web.Mvc.Html.LinkExtensions.RouteLink(helper, linkText, GetFixedRouteValues(action), new RouteValueDictionary(htmlAttributes));
}
private static RouteValueDictionary GetFixedRouteValues<TController>(Expression<Action<TController>> action)
where TController : Controller
{
return Microsoft.Web.Mvc.Internal.ExpressionHelper.GetRouteValuesFromExpression(action).FixArea<TController>();
}
}