强类型的url操作
我读过类似于多个帖子和博客
ASP.NET MVC中基于委托的强类型URL生成
但他们没有一个真正做我想做的事情。 目前我有一个混合的方法,如:
// shortened for Brevity
public static Exts
{  
  public string Action(this UrlHelper url, 
    Expression<Func<T, ActionResult>> expression)
    where T : ControllerBase
  {
    return Exts.Action(url, expression, null);
  }
  public string Action(this UrlHelper url, 
    Expression<Func<T, ActionResult>> expression,
    object routeValues)
    where T : ControllerBase
  {
    string controller;
    string action;
    // extension method 
    expression.GetControllerAndAction(out controller, out action);
    var result = url.Action(action, controller, routeValues);
    return result;
  }
}
如果您的控制器方法没有任何参数,则效果很好:
public class MyController : Controller 
{
  public ActionResult MyMethod()
  {
    return null;
  }
  public ActionResult MyMethod2(int id)
  {
    return null;
  }
}
然后我可以:
Url.Action<MyController>(c => c.MyMethod())
但是如果我的方法需要一个参数,那么我必须传递一个值(我永远不会使用它):
Url.Action<MyController>(c => c.MyMethod2(-1), new { id = 99 })
  所以问题是有没有办法改变扩展方法,仍然要求第一个参数是一个方法定义在类型T ,检查以确保返回参数是一个ActionResult而不实际指定参数,如下所示: 
Url.Action<MyController>(c => c.MyMethod2, new { id = 99 })
  所以这会传递一个指向该方法的指针(如反射MethodInfo )而不是Func<> ,所以它不会关心参数。  如果可能的话,那签名会是什么样子? 
你不能这样做:
c => c.MyMethod2
因为这是一个方法组。 方法组中的任何方法都可以返回void或其他任何东西,所以编译器不会允许它:
Error CS0428  Cannot convert method group '...' to non-delegate type '...'
  组中可能有一个方法返回一个ActionMethod ,或者没有。  你需要决定。 
  但是您无需提供方法组。  你可以使用你现有的签名,减去object routeValues ,并像这样调用它: 
Url.Action<MyController>(c => c.MyMethod(99))
  然后在你的方法中,你可以使用MethodInfo methodCallExpression.Method来获取方法参数名,并使用methodCallExpression.Arguments来获取参数。 
  那么你的下一个问题就是在运行时创建匿名对象。  幸运的是,你不需要,因为Url.Action()也有一个接受RouteValueDictionary的重载。 
  将参数和参数一起压缩到一个字典中, RouteValueDictionary创建一个RouteValueDictionary ,并将其传递给Url.Action() : 
var methodCallExpression = expression.Body as MethodCallExpression;
if (methodCallExpression == null)
{                
    throw new ArgumentException("Not a MethodCallExpression", "expression");
}
var methodParameters = methodCallExpression.Method.GetParameters();
var routeValueArguments = methodCallExpression.Arguments.Select(EvaluateExpression);
var rawRouteValueDictionary = methodParameters.Select(m => m.Name)
                            .Zip(routeValueArguments, (parameter, argument) => new
                            {
                                parameter,
                                argument
                            })
                            .ToDictionary(kvp => kvp.parameter, kvp => kvp.argument);
var routeValueDictionary = new RouteValueDictionary(rawRouteValueDictionary);
// action and controller obtained through your logic 
return url.Action(action, controller, routeValueDictionary);
  EvaluateExpression方法非常天真地编译和调用每个非常量表达式,所以在实践中可能会证明是非常慢的: 
private static object EvaluateExpression(Expression expression)
{
    var constExpr = expression as ConstantExpression;
    if (constExpr != null)
    {
        return constExpr.Value;
    }
    var lambda = Expression.Lambda(expression);
    var compiled = lambda.Compile();
    return compiled.DynamicInvoke();
}
  然而,在Microsoft ASP.NET MVC期货包有方便ExpressionHelper.GetRouteValuesFromExpression(expr) ,也可以用来处理路由和地区。  您的整个方法可以替换为: 
var routeValues = Microsoft.Web.Mvc.Internal.ExpressionHelper.GetRouteValuesFromExpression<T>(expression);
return url.Action(routeValues["Action"], routeValues["Controller"], routeValues);
它在内部使用缓存的表达式编译器,因此适用于所有用例,您不必重新发明轮子。
链接地址: http://www.djcxy.com/p/31275.html