Я надеюсь 'обобщать' некоторый код в.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 на Модели Представления имеет следующую подпись
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 от Представления.
Я думаю, вы хотите, чтобы 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)
};
}
Способ работы этого помощника заключается в том, что он пытается определить URL на основе лямбда-выражения, которое всегда должно быть вызовом метода в контроллере. Таким образом, он знает имя действия и переданные параметры. Если лямбда-выражение больше не является MethodCallExpression
, то определить URL невозможно.