Eri*_*ips 8 c# asp.net-mvc expression func
我读了很多类似的帖子和博客
但他们中没有一个人真的做我想做的事.目前我有一个混合方法,如:
// 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;
}
}
Run Code Online (Sandbox Code Playgroud)
如果你的控制器方法没有任何参数,那么效果很好:
public class MyController : Controller
{
public ActionResult MyMethod()
{
return null;
}
public ActionResult MyMethod2(int id)
{
return null;
}
}
Run Code Online (Sandbox Code Playgroud)
然后我可以:
Url.Action<MyController>(c => c.MyMethod())
Run Code Online (Sandbox Code Playgroud)
但是如果我的方法接受一个参数,那么我必须传递一个值(我永远不会使用):
Url.Action<MyController>(c => c.MyMethod2(-1), new { id = 99 })
Run Code Online (Sandbox Code Playgroud)
所以问题是有一种方法可以改变扩展方法,仍然要求第一个参数是一个在类型T上定义的方法,它确保返回参数是一个ActionResult没有实际指定参数的类型,如:
Url.Action<MyController>(c => c.MyMethod2, new { id = 99 })
Run Code Online (Sandbox Code Playgroud)
所以这会传递一个指向方法的指针(比如反射MethodInfo)而不是Func<>它,所以它不会关心参数.如果有可能,那签名会是什么样子?
你不能这样做:
\n\nc => c.MyMethod2\nRun Code Online (Sandbox Code Playgroud)\n\n因为那是一个方法组。方法组中的任何方法都可以返回 void 或其他任何内容,因此编译器不会允许这样做:
\n\n\n\n\nRun Code Online (Sandbox Code Playgroud)\nError CS0428 Cannot convert method group \'...\' to non-delegate type \'...\'\n
组中可能有一个方法返回ActionMethod方法返回,或者不返回。你需要做出决定。
但无论如何您不必提供方法组。您可以只使用现有的签名,减去object routeValues,然后像这样调用它:
Url.Action<MyController>(c => c.MyMethod(99))\nRun Code Online (Sandbox Code Playgroud)\n\n然后在你的方法中,你可以使用MethodInfo methodCallExpression.Method来获取方法参数名称,并且methodCallExpression.Arguments来获取参数。
那么你的下一个问题是在运行时创建匿名对象。幸运的是你不必这样做,因为Url.Action()也有一个接受a的重载RouteValueDictionary.
将参数和参数一起压缩到一个字典中,从中创建一个字典RouteValueDictionary,然后将其传递给Url.Action():
var methodCallExpression = expression.Body as MethodCallExpression;\nif (methodCallExpression == null)\n{ \n throw new ArgumentException("Not a MethodCallExpression", "expression");\n}\n\nvar methodParameters = methodCallExpression.Method.GetParameters();\nvar routeValueArguments = methodCallExpression.Arguments.Select(EvaluateExpression);\n\nvar rawRouteValueDictionary = methodParameters.Select(m => m.Name)\n .Zip(routeValueArguments, (parameter, argument) => new\n {\n parameter,\n argument\n })\n .ToDictionary(kvp => kvp.parameter, kvp => kvp.argument);\n\nvar routeValueDictionary = new RouteValueDictionary(rawRouteValueDictionary);\n\n// action and controller obtained through your logic \n\nreturn url.Action(action, controller, routeValueDictionary);\nRun Code Online (Sandbox Code Playgroud)\n\n这EvaluateExpression方法非常天真地编译并调用每个非常量表达式,因此在实践中可能会非常慢:
private static object EvaluateExpression(Expression expression)\n{\n var constExpr = expression as ConstantExpression;\n if (constExpr != null)\n {\n return constExpr.Value;\n }\n\n var lambda = Expression.Lambda(expression);\n var compiled = lambda.Compile();\n return compiled.DynamicInvoke();\n}\nRun Code Online (Sandbox Code Playgroud)\n\n然而,在Microsoft ASP.NET MVC Futures 包中,有一个方便的ExpressionHelper.GetRouteValuesFromExpression(expr)\xe2\x80\x8c\xe2\x80\x8b,它也可以处理路由和区域。然后您的整个方法可以替换为:
var routeValues = Microsoft.Web.Mvc.Internal.ExpressionHelper.GetRouteValuesFromExpression<T>(expression);\nreturn url.Action(routeValues["Action"], routeValues["Controller"], routeValues);\nRun Code Online (Sandbox Code Playgroud)\n\n它在内部使用缓存的表达式编译器,因此它适用于所有用例,并且您不必重新发明轮子。
\n| 归档时间: |
|
| 查看次数: |
1531 次 |
| 最近记录: |