有谁知道如何做以下事情:
Html.ActionLink(c => c.SomeAction(new MessageObject {Id = 1}))
这应输出一个带有“/ Controller / SomeAction / 1”网址的链接,指向一行ActionMethod:
public Controller : Controller
{
public ActionResult SomeMethod(MessageObject message)
{
// do something with the message
return View();
}
}
我已经为生成表单编写了类似的东西,但是不需要在Url的末尾包含Id值。基本上我想在我的路线中进行某种反向查找,但我找不到任何关于如何做到这一点的doco。我有一个ModelBinder设置,能够从GET和POST参数构建一个MessageObject,但我不知道如何扭转这个过程。
谢谢, 马特
答案 0 :(得分:1)
最后,我最终将以下代码包装在HtmlHelper扩展方法中。这将允许我使用类似的东西 Html.ActionLink(c => c.SomeAction(new MessageObject {Id = 1}))
并将MessageObject的所有属性创建为RouteValues。
public static RouteValueDictionary GetRouteValuesFromExpression<TController>(Expression<Action<TController>> action)
where TController : Controller
{
Guard.Against<ArgumentNullException>(action == null, @"Action passed to GetRouteValuesFromExpression cannot be null.");
MethodCallExpression methodCall = action.Body as MethodCallExpression;
Guard.Against<InvalidOperationException>(methodCall == null, @"Action passed to GetRouteValuesFromExpression must be method call");
string controllerName = typeof(TController).Name;
Guard.Against<InvalidOperationException>(!controllerName.EndsWith("Controller"), @"Controller passed to GetRouteValuesFromExpression is incorrect");
RouteValueDictionary rvd = new RouteValueDictionary();
rvd.Add("Controller", controllerName.Substring(0, controllerName.Length - "Controller".Length));
rvd.Add("Action", methodCall.Method.Name);
AddParameterValuesFromExpressionToDictionary(rvd, methodCall);
return rvd;
}
/// <summary>
/// Adds a route value for each parameter in the passed in expression. If the parameter is primitive it just uses its name and value
/// if not, it creates a route value for each property on the object with the property's name and value.
/// </summary>
/// <param name="routeValues"></param>
/// <param name="methodCall"></param>
private static void AddParameterValuesFromExpressionToDictionary(RouteValueDictionary routeValues, MethodCallExpression methodCall)
{
ParameterInfo[] parameters = methodCall.Method.GetParameters();
methodCall.Arguments.Each(argument =>
{
int index = methodCall.Arguments.IndexOf(argument);
ConstantExpression constExpression = argument as ConstantExpression;
if (constExpression != null)
{
object value = constExpression.Value;
routeValues.Add(parameters[index].Name, value);
}
else
{
object actualArgument = argument;
MemberInitExpression expression = argument as MemberInitExpression;
if (expression != null)
{
actualArgument = Expression.Lambda(argument).Compile().DynamicInvoke();
}
// create a route value for each property on the object
foreach (PropertyDescriptor descriptor in TypeDescriptor.GetProperties(actualArgument))
{
object obj2 = descriptor.GetValue(actualArgument);
routeValues.Add(descriptor.Name, obj2);
}
}
});
}
答案 1 :(得分:0)
由于您的示例网址与方法签名所需的网址不匹配,因此我不确定您要执行的操作。通常,如果使用需要复杂对象的方法,则传递值以在查询字符串中构造该对象或作为表单参数,并且ModelBinder根据参数中提供的数据构造对象。如果您只想传递id,那么该方法通常不接受任何参数,您从RouteData中提取id,并在持久存储(或缓存)中查找对象。如果你想做后者,你的方法应该如下:
public ActionResult SomeMethod()
{
int messageObjectID;
if (RouteData.Values.TryGetValue("id",out messageObjectID))
{
... get the object with the correct id and process it...
}
else
{
... error processing because the id was not available...
}
return View();
}
答案 2 :(得分:0)
我不确定你到底是什么 尝试从您的示例网址开始 不符合要求 你方法的签名。通常如果 你使用的方法需要一个 复杂对象,您将值传递给 在查询中构造该对象 字符串或作为表单参数和 ModelBinder从中构造对象 参数中提供的数据。
LOL这正是我想要做的:)这个url工作正常并映射到该方法,模型绑定器能够将该URL转换为映射到该操作的路由并且工作正常。 (该路由将“1”映射到名为Id的RouteValue,然后模型绑定器将其分配给消息对象的Id字段)。
我要做的是走另一条路,接听方法并将其变成路线。
答案 3 :(得分:0)
如果您不介意在控制器中为要生成URL的每个操作添加方法,可以按以下步骤操作。与lambda表达式方法相比,这有一些缺点,但也有一些上升空间。
实现: -
将此添加到您希望强类型网址生成的Controller for EACH操作方法...
// This const is only needed if the route isn't already mapped
// by some more general purpose route (e.g. {controller}/{action}/{message}
public const string SomeMethodUrl = "/Home/SomeMethod/{message}";
// This method generates route values that match the SomeMethod method signature
// You can add default values here too
public static object SomeMethodRouteValues(MessageObject messageObject)
{
return new { controller = "Home", action = "SomeMethod",
message = messageObject };
}
您可以在路线映射代码中使用这些代码......
Routes.MapRoute ("SomeMethod",
HomeController.SomeMethodUrl,
HomeController.SomeMethodRouteValues(null));
您可以使用它们,无论您需要生成指向该操作的链接: - e.g。
<%=Url.RouteUrl(HomeController.SomeMethodValues(new MessageObject())) %>
如果你这样做......
1)您的代码中只有一个位置,其中定义了任何操作的参数
2)只有一种方法可以将这些参数转换为路由,因为Html.RouteLink和Url.RouteUrl都可以将HomeController.SomeMethodRouteValues(...)作为参数。
3)可以轻松为任何可选路线值设置默认值。
4)在不破坏任何网址的情况下,很容易重构代码。假设您需要向SomeMethod添加参数。您所做的就是更改SomeMethodUrl和SomeMethodRouteValues()以匹配新参数列表,然后修复所有损坏的引用,无论是在代码中还是在视图中。尝试将新的{action =“SomeMethod”,...}分散在您的代码中。
5)您获得了Intellisense支持,因此您可以查看构建任何操作的链接或URL所需的参数。就“强类型”而言,这种方法似乎比使用lambda表达式更好,因为没有编译时或设计时错误检查链接生成参数是否有效。
缺点是您仍然需要将这些方法与实际操作方法保持同步(但它们可以在代码中彼此相邻,以便于查看)。纯粹主义者无疑会反对这种方法,但实际上它正在发现和修复本来需要测试才能找到的错误,它有助于取代我们过去在WebForms项目中使用的强类型页面方法。