2008-12-12 2 views
3

Возможно ли получить URL-адрес от действия без знания ViewContext (например, в контроллере)? Что-то вроде этого:Url Form Action Без ViewContext

LinkBuilder.BuildUrlFromExpression(ViewContext context, Expression<Action<T>> action) 

... но с использованием Controller.RouteData вместо ViewContext. Похоже, у меня есть металлический блок.

ответ

5

Вот как я это делаю в модульном тесте:

private string RouteValueDictionaryToUrl(RouteValueDictionary rvd) 
    { 
     var context = MvcMockHelpers.FakeHttpContext("~/"); 
     // _routes is a RouteCollection 
     var vpd = _routes.GetVirtualPath(
      new RequestContext(context, _ 
       routes.GetRouteData(context)), rvd); 
     return vpd.VirtualPath; 
    } 

Per комментарии, я буду адаптироваться к контроллеру:

string path = RouteTable.Routes.GetVirtualPath(
    new RequestContext(HttpContext, 
     RouteTable.Routes.GetRouteData(HttpContext)), 
    new RouteValueDictionary( 
     new { controller = "Foo", 
       action = "Bar" })).VirtualPath; 

Replace "Foo" и "Bar" с реальными именами , Это не в моей голове, поэтому я не могу гарантировать, что это наиболее эффективное решение, но оно должно помочь вам на правильном пути.

+0

Мне не нужен URL-адрес текущего запроса, но для некоторых других действий. – 2008-12-12 16:58:55

+0

Я понимаю. Решение выше все еще применяется. Существует нет соединения * с текущим запросом. – 2008-12-12 18:07:36

4

Крейг, Спасибо за правильный ответ. Он отлично работает, и я тоже думаю. Так что в моем диске, чтобы устранить эти рефакторинг-устойчивай «волшебные струны» Я разработал вариацию на ваше решение:

public static string GetUrlFor<T>(this HttpContextBase c, Expression<Func<T, object>> action) 
    where T : Controller 
{ 
    return RouteTable.Routes.GetVirtualPath(
     new RequestContext(c, RouteTable.Routes.GetRouteData(c)), 
     GetRouteValuesFor(action)).VirtualPath; 
} 

public static RouteValueDictionary GetRouteValuesFor<T>(Expression<Func<T, object>> action) 
    where T : Controller 
{ 
    var methodCallExpresion = ((MethodCallExpression) action.Body); 
    var controllerTypeName = methodCallExpresion.Object.Type.Name; 
    var routeValues = new RouteValueDictionary(new 
    { 
     controller = controllerTypeName.Remove(controllerTypeName.LastIndexOf("Controller")), 
     action = methodCallExpresion.Method.Name 
    }); 
    var methodParameters = methodCallExpresion.Method.GetParameters(); 
    for (var i = 0; i < methodParameters.Length; i++) 
    { 
     var value = Expression.Lambda(methodCallExpresion.Arguments[i]).Compile().DynamicInvoke(); 
     var name = methodParameters[i].Name; 
     routeValues.Add(name, value); 
    } 
    return routeValues; 
} 

Я знаю, что некоторые из них будут говорить ... страшились отражение! В моем конкретном приложении я считаю, что преимущество ремонтопригодности перевешивает производительность. Я приветствую любую обратную связь по этой идее и коду.