Url Form Action Without ViewContext

前端 未结 2 1202
时光说笑
时光说笑 2020-12-17 06:08

Is it possible to get a URL from an action without knowing ViewContext (e.g., in a controller)? Something like this:

LinkBuilder.BuildUrlFromExpression(View         


        
相关标签:
2条回答
  • 2020-12-17 06:32

    Here's how I do it in a unit test:

        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 comments, I'll adapt to a controller:

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

    Replace "Foo" and "Bar" with real names. This is off the top of my head, so I can't guarantee that it's the most efficient solution possible, but it should get you on the right track.

    0 讨论(0)
  • 2020-12-17 06:34

    Craig, Thanks for the correct answer. It works great, and it also go me thinking. So in my drive to eliminate those refactor-resistent "magic strings" I have developed a variation on your solution:

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

    I know what some will say...dreaded reflection! In my particular application, I think the benefit of maintainability outweighs performance conerns. I welcome any feedback on this idea and the code.

    0 讨论(0)
提交回复
热议问题