Asp.net mvc 不带ViewContext的Url表单操作

Asp.net mvc 不带ViewContext的Url表单操作,asp.net-mvc,url,routes,Asp.net Mvc,Url,Routes,是否可以在不知道ViewContext的情况下(例如,在控制器中)从操作获取URL?大概是这样的: LinkBuilder.BuildUrlFromExpression(ViewContext context, Expression<Action<T>> action) LinkBuilder.BuildUrlFromExpression(ViewContext上下文,表达式操作) …但使用Controller.RouteData而不是ViewContext。我似乎在

是否可以在不知道ViewContext的情况下(例如,在控制器中)从操作获取URL?大概是这样的:

LinkBuilder.BuildUrlFromExpression(ViewContext context, Expression<Action<T>> action)
LinkBuilder.BuildUrlFromExpression(ViewContext上下文,表达式操作)

…但使用Controller.RouteData而不是ViewContext。我似乎在这上面有金属块。

下面是我在单元测试中如何做到的:

    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;
    }
根据评论,我将适应控制器:

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

用真名替换“Foo”和“Bar”。这是我不知道的,所以我不能保证这是可能的最有效的解决方案,但它会让你走上正确的轨道。

克雷格,谢谢你的正确答案。它工作得很好,也让我思考起来。因此,在我努力消除那些抗重构的“魔串”的过程中,我在您的解决方案上开发了一个变体:

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;
}
公共静态字符串GetUrlFor(此HttpContextBase c,表达式操作)
其中T:控制器
{
返回RouteTable.Routes.GetVirtualPath(
新的RequestContext(c,RouteTable.Routes.GetRouteData(c)),
GetRouteValuesFor(action)).VirtualPath;
}
公共静态RouteValueDictionary GetRouteValuesFor(表达式操作)
其中T:控制器
{
var MethodCallExpression=((MethodCallExpression)action.Body);
var controllerTypeName=methodCallExpresion.Object.Type.Name;
var routeValues=新的RouteValueDictionary(新的
{
controller=controllerTypeName.Remove(controllerTypeName.LastIndexOf(“控制器”)),
action=methodCallExpresion.Method.Name
});
var methodParameters=methodCallExpresion.Method.GetParameters();
对于(var i=0;i

我知道有些人会说什么……可怕的反思!在我的特定应用程序中,我认为可维护性的好处大于性能方面的好处。我欢迎对此想法和代码的任何反馈。

我不需要当前请求的URL,但需要其他操作。我理解。上述解决方案仍然适用。那里没有与当前请求的连接。我不明白。假设我想获取FooController.Bar(“foo”,“Bar”)的Url,它将是“/MyVirtualDir/foo.mvc/Bar/foo/Bar”。我怎样才能用一个(不同的)控制器方法来得到它呢?Craig,你能不能也用一个强类型控制器(即Microsoft.Web.MVC)来更新你的答案,而不必硬编码控制器名称和操作?可能吗?所有控制器都是强类型的。如果您能够获得性能上的成功,您当然可以反映控制器和操作名称,但不要忘记ActionNameAttribute。如果我这样做,我会尝试找到一种方法来使用框架的内部控制器和操作名缓存。我不知道如何正确地存根HttpContext(即使在与Reflector一起玩了一番之后)。我将提供一个接口,使控制器更易于测试。我能够找出如何存根HttpContext,使其在测试中工作。缺少的价格是:SetupResult.For(request.AppRelativeCurrentExecutionFilePath)。Return(“~/”);