Как передать делегат для создания дерева выражений, которое представляет собой MethodCallExpression - PullRequest
1 голос
/ 23 июня 2010

Я пытаюсь «обобщить» некоторый код в приложении .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")

Свойство Action в View Model имеет следующую подпись

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 типа. Это исключение выдается методом расширения Html.ActionLink из представления.

Ответы [ 2 ]

2 голосов
/ 23 июня 2010

Я думаю, вы хотите, чтобы CreateViewModel использовал дерево выражений, а не делегат.ActionLink, похоже, предполагает, что ему передается дерево выражений, которое является лямбда-выражением, тело которого является вызовом метода для параметра.Если вы сделаете то же предположение относительно выражения, переданного в 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)
    };
}
0 голосов
/ 23 июня 2010

Способ работы этого помощника заключается в том, что он пытается определить URL на основе лямбда-выражения, которое всегда должно быть вызовом метода в контроллере. Таким образом, он знает имя действия и переданные параметры. Если лямбда-выражение больше не равно MethodCallExpression, определить URL невозможно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...