asp.net-mvc - net - html displayfor text color
Usar un ActionLink fuertemente tipado cuando el método de acción no toma un tipo primitivo (4)
No estoy seguro de qué es exactamente lo que intenta hacer, ya que su URL de ejemplo no coincide con la requerida para la firma de su método. Normalmente, si utiliza un método que requiere un objeto complejo, pasa los valores para construir ese objeto en la cadena de consulta o como parámetros de formulario y ModelBinder construye el objeto a partir de los datos suministrados en los parámetros.
LOL, eso es exactamente lo que trato de hacer :) Esa url funciona bien y se asigna a ese método, la carpeta de modelo puede convertir esa URL en una ruta que se corresponda con esa acción y funcione bien. (Esa ruta mapea el "1" a un RouteValue llamado Id, que el modelo de cuaderno luego asigna al campo Id del objeto de mensaje).
Lo que intento hacer es ir por el otro lado, tomar una llamada a un método y convertirla en una ruta.
¿Alguien sabe cómo podría hacer algo como esto?
Html.ActionLink (c => c.SomeAction (new MessageObject {Id = 1}))
Esto debería generar un enlace con la URL de "/ Controller / SomeAction / 1", apuntando a un ActionMethod en la línea de:
public Controller : Controller
{
public ActionResult SomeMethod(MessageObject message)
{
// do something with the message
return View();
}
}
He escrito algo similar para generar formularios, pero no es necesario que incluya el valor Id al final de la URL. Básicamente, quiero hacer algún tipo de búsqueda inversa en mis rutas, pero no puedo encontrar ningún doco sobre cómo podría hacerlo. Tengo una configuración de ModelBinder que puede construir un MessageObject a partir de los parámetros GET y POST, pero no estoy seguro de cómo puedo revertir el proceso.
Gracias, Matt
No estoy seguro de qué es exactamente lo que intenta hacer, ya que su URL de ejemplo no coincide con la requerida para la firma de su método. Normalmente, si utiliza un método que requiere un objeto complejo, pasa los valores para construir ese objeto en la cadena de consulta o como parámetros de formulario y ModelBinder construye el objeto a partir de los datos suministrados en los parámetros. Si desea pasar solo el id, entonces el método generalmente no toma ningún parámetro, extrae el id de RouteData y busca el objeto en un almacenamiento persistente (o en un caché). Si quieres hacer esto último, tu método debería verse así:
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();
}
Al final terminé envolviendo el siguiente código en un método de extensión HtmlHelper. Esto me permitiría usar algo como Html.ActionLink (c => c.SomeAction (new MessageObject {Id = 1}))
y tiene todas las propiedades de MessageObject creadas como 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);
}
}
});
}
Si no le importa agregar un método junto a cada acción en su controlador para el que desea generar URL, puede continuar de la siguiente manera. Esto tiene algunas desventajas en comparación con su enfoque de expresión lambda, pero también algunos aspectos positivos.
Implementación:-
Agregue esto a su Controlador para CADA método de acción para el que desee una generación de url fuertemente tipada ...
// 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 };
}
Puede usarlos en su código de mapeo de ruta ...
Routes.MapRoute ("SomeMethod",
HomeController.SomeMethodUrl,
HomeController.SomeMethodRouteValues(null));
Y puede usarlos EN TODAS PARTES, necesita generar un enlace a esa acción: - por ej.
<%=Url.RouteUrl(HomeController.SomeMethodValues(new MessageObject())) %>
Si lo haces de esta manera ...
1) Tienes solo un lugar en tu código donde los parámetros de cualquier acción están definidos
2) Solo hay una forma en que esos parámetros se convierten en rutas porque Html.RouteLink y Url.RouteUrl pueden tomar HomeController.SomeMethodRouteValues (...) como parámetro.
3) Es fácil establecer valores predeterminados para cualquier valor de ruta opcional.
4) Es fácil refactorizar su código sin romper las URL. Supongamos que necesita agregar un parámetro a SomeMethod. Todo lo que debes hacer es cambiar SomeMethodUrl y SomeMethodRouteValues () para que coincida con la nueva lista de parámetros y luego ir a arreglar todas las referencias rotas, ya sea en código o en Vistas. Intente hacer eso con new {action = "SomeMethod", ...} diseminado por todo su código.
5) Obtiene soporte Intellisense para que pueda VER qué parámetros son necesarios para construir un enlace o url para cualquier acción. En cuanto a estar "fuertemente tipado", este enfoque parece mejor que usar expresiones lambda donde no hay errores en tiempo de compilación ni en tiempo de diseño comprobando que los parámetros de generación de enlaces sean válidos.
La desventaja es que todavía tiene que mantener estos métodos sincronizados con el método de acción real (pero pueden estar uno al lado del otro en el código, lo que facilita su visualización). Los puristas sin duda objetarán a este enfoque, pero en términos prácticos es encontrar y corregir errores que de otra manera requerirían pruebas para encontrar y está ayudando a reemplazar los métodos de páginas fuertemente tipados que solíamos tener en nuestros proyectos de WebForms.