Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/asp.net-mvc/16.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 强类型url操作_C#_Asp.net Mvc_Expression_Func - Fatal编程技术网

C# 强类型url操作

C# 强类型url操作,c#,asp.net-mvc,expression,func,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

我读过多篇类似的帖子和博客

但是他们没有一个人真正做到我想做的事。目前我有一种混合方法,如:

// 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;
  }
}
然后我可以:

Url.Action<MyController>(c => c.MyMethod())
因此,这将传递一个指向方法的指针(如反射
MethodInfo
),而不是
Func
,因此它不关心参数。如果可能的话,该签名会是什么样子?

您不能这样做:

c => c.MyMethod2
因为这是一个方法组。方法组中的任何方法都可以返回void或其他任何内容,因此编译器不允许它:

组中可能有方法返回
ActionMethod
,或者没有。你需要做出决定

但无论如何,您不必提供方法组。您只需使用现有签名,减去
对象routeValues
,然后像这样调用它:

Url.Action<MyController>(c => c.MyMethod(99))
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();
}

但是,在中有方便的
ExpressionHelper.GetRouteValuesFromExpression(expr)‌​,它还处理布线和区域。然后,您的整个方法可以替换为:

var routeValues = Microsoft.Web.Mvc.Internal.ExpressionHelper.GetRouteValuesFromExpression<T>(expression);
return url.Action(routeValues["Action"], routeValues["Controller"], routeValues);
var routeValues=Microsoft.Web.Mvc.Internal.ExpressionHelper.GetRouteValuesFromExpression(表达式);
返回url.Action(routeValue[“Action”]、routeValue[“Controller”]、routeValue);

它在内部使用缓存表达式编译器,因此它适用于所有用例,您不必重新发明轮子

作为其他项目的替代方案,我最近开始使用

唯一的缺点是,它们可以混合使用,并在编译后产生错误的结果:

Url.Action(nameof(SomeOtherController.MyMethod), nameof(MyController), new { id = 99 })

控制器不匹配,但我认为这没什么大不了的。在编译过程中,当控制器名称或方法名称更改且未在代码中的其他位置更新时,它仍会引发错误。

Ivaylo Kenov创建了一个Nuget包()

只需在
AddMvc
方法之后调用
addtypedroting
扩展方法

services.AddMvc().addTypeDrooting();
或者,如果您正在开发web API:

services.AddControllers().addTypeDrooting();
然后,您可以根据需要使用表达式获取URL

Url.Action(c=>c.Get(fooId));

使用
c.MyMethod2
指向一个方法组,其中任何一个都可以返回其他内容。。。但我相当肯定我见过实现这一点的库。也许您可以执行一些反射魔法,并在
GetControllerAndAction
中检查与提供的参数匹配的组的方法是否确实返回
ActionResult
。这并不能完全为您提供所需的编译时安全性,但无论如何,您不应该在控制器中使用非actionmethods作为公共方法。
c=>c.MyMethod2
不能从
Method Group
强制转换为非委托类型
ActionResult
。您当然是对的,这只适用于当前控制器,不在视野中。为什么不使用
c=>c.MyMethod2(99)
来代替(使用
MethodCallExpression.Arguments
来获取参数)?让我想想。。。我故意删除了那个代码,因为它看起来模棱两可:
c=>c.MyMethod(99),new{id=98}
。。。但也许我不需要在这一点上的路线值。。。我在想一个我需要路由值的原因…如果它们不是
ConstantExpression
,我是否必须编译并执行该表达式才能获得
RouteValueDictionary
的值?我不能使用Futures包,但我可以
compile()
表达式并使用值作为对象。@Erik我已经这样做了,请参见编辑和警告。它对我来说适用于常量、方法调用和成员访问,但您必须亲自测试它。我想如果您想让它处理区域,还必须在控制器上查找区域属性,并将其添加到
routeValueDictionary
。我要做的是检查控制器的命名空间。如果我疯了,我可以根据AreaRegistration验证它:)
Url.Action<MyController>(c => c.MyMethod(99))
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);
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();
}
var routeValues = Microsoft.Web.Mvc.Internal.ExpressionHelper.GetRouteValuesFromExpression<T>(expression);
return url.Action(routeValues["Action"], routeValues["Controller"], routeValues);
Url.Action(nameof(MyController.MyMethod), nameof(MyController), new { id = 99 })
Url.Action(nameof(SomeOtherController.MyMethod), nameof(MyController), new { id = 99 })