如何传递委托来创建一个MethodCallExpression的表达式树

时间:2010-06-23 10:45:26

标签: c# asp.net-mvc generics .net-3.5 lambda

我正在寻找'概括'.NET 3.5 MVC应用程序中的一些代码并且遇到了问题。

背景

我有一个带有一些操作的 SomeController 类:

public ActionResult Renew(string qualification, int tierId) { ... }
public ActionResult Reinstate(string qualification, int tierId) { ... }
public ActionResult Withdraw(string qualification, int tierId) { ... }

在视图中,我使用强类型扩展辅助方法来创建 ActionLink (此通用方法来自Microsoft.Web.Mvc.dll)。样品使用将是

Html.ActionLink<SomeController>(c=>c.Renew(Model.Qualification, Model.TierId),"Renew")

只有我在视图上有一个属性才能定义方法调用的表达式,所以我可以这样做:

Html.ActionLink(Model.Action,"Renew")

视图模型上的操作属性具有以下签名

public Expression<Action<SomeController>> Action { get; set; }

当我在控制器动作中创建视图时,我可以使用以下代码:

model.Action = c => c.Renew(dto.Qualification.Name, t.Id)

方法

到目前为止一直很好,所有强类型和工作都很好,但这是我的问题开始的地方。我希望能够使用传递给组装视图模型的方法的参数替换对c => c.Renew(dto.Qualification.Name, t.Id)的显式调用。

我希望做到以下几点:

private SomeViewModel CreateViewModel(SomeDto dto, Tier t, Func<string, int, ActionResult>> actionToCall) {
  return new SomeViewModel {
    ...
    Action = a => actionToCall(dto.Qualification.Name, t.Id), 
  }
}

然后使用此方法构造模型并将适当的控制器操作作为参数传递

var model = CreateViewModel(dto,tier,this.Reinstate)

问题

这个解决方案可以编译,但是当我开始渲染ActionLink时,我得到了一个预期,因为作为一个动作传入的表达式不是MethodCallExpression类型。来自View的Html.ActionLink扩展方法抛出此异常。

2 个答案:

答案 0 :(得分:2)

我认为您希望CreateViewModel接受表达式树而不是委托。 ActionLink似乎假设它传递了一个表达式树,它是一个lambda,它的主体是对参数的方法调用。如果对传递给CreateViewModel的表达式做出相同的假设,则可以通过执行以下操作来拉出MethodInfo对象并手动构造表达式树:

private SomeViewModel CreateViewModel(SomeDto dto, Tier t, 
    Expression<Func<string, int, ActionResult>> actionToCall)
{
    var methodCallExpression = (MethodCallExpression)actionToCall.Body;
    var param = Expression.Parameter(typeof(SomeController), null);
    return new SomeViewModel()
    {
        Action =
            Expression.Lambda<Action<SomeController>>(
                Expression.Call(
                    param,
                    methodCallExpression.Method,
                    Expression.Constant(dto.Qualification.Name),
                    Expression.Constant(t.Id)),
                param)
    };
}

答案 1 :(得分:0)

这个帮助程序的工作方式是它尝试根据lambda表达式确定URL,该表达式应始终是控制器上的方法调用。这样它就知道传递的动作名称和参数。如果lambda表达式不再是MethodCallExpression,则无法确定URL。