Pregunta

¿Es posible obtener una URL desde una acción sin saber ViewContext (por ejemplo, en un controlador)? Algo como esto:

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

... pero utilizando en lugar de Controller.RouteData ViewContext. Parece que tengo bloque de metal en esto.

¿Fue útil?

Solución

Así es como lo hago en una prueba de unidad:

    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;
    }

Por los comentarios, voy a adaptar a un controlador:

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

Reemplazar "Foo" y "bar" con nombres reales. Esta es la parte superior de la cabeza, por lo que no puede garantizar que se trata de la solución más eficiente posible, pero debe obtener en el camino correcto.

Otros consejos

Craig, Gracias por la respuesta correcta. Funciona muy bien, y también ir me hizo pensar. Así que en mi campaña para eliminar esos refactor-resistentes "cuerdas mágicas" He desarrollado una variación en su solución:

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;
}

Yo sé lo que dirán algunos ... reflexión temida! En mi aplicación en particular, creo que el beneficio de mantenimiento mayor que conerns rendimiento. Doy la bienvenida a cualquier comentario en esta idea y el código.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top