2010-11-09 11 views
19

Estoy usando MvcSiteMapProvider 2.2.1 (http://mvcsitemap.codeplex.com) y tengo un problema al crear elementos secundarios en un nodo dinámico (utilizando un DynamicNodeProvider) cuando esos niños tienen un parámetro dinámico (id).Creación de nodos secundarios para un DynamicNode en MvcSiteMapProvider que tienen parámetros dinámicos

estoy perdiendo el pan rallado para el siguiente recorrido:

Tiendas/5/Productos/Editar/23

donde el patrón de URL es:

Tiendas/{storeID}/{ controller}/{action}/{id}

Funciona bien cuando el ID se deja fuera (es decir, la acción "Nuevo"). Pero cuando se especifica el ID, no coincide con la ruta y mis rutas de exploración (usando el ayudante SiteMapPath) están en blanco.

Mi mapa del sitio: (abreviated)

<?xml version="1.0" encoding="utf-8" ?> 
<mvcSiteMap xmlns="http://mvcsitemap.codeplex.com/schemas/MvcSiteMap-File-2.0"> 
    <mvcSiteMapNode title="Home" controller="Dashboard" action="Index" changeFrequency="Always" updatePriority="Normal"> 
     <mvcSiteMapNode title="My Account" controller="Account" action="Index" key="Account" /> 
     <mvcSiteMapNode title="My Stores" area="Stores" controller="Home" action="Index" visibilityProvider="ControlPanel.Areas.Stores.StoreAreaVisibilityProvider, ControlPanel" > 
      <mvcSiteMapNode title="Store" action="Index" dynamicNodeProvider="ControlPanel.Areas.Stores.StoreAreaNodeProvider, ControlPanel" /> 
     </mvcSiteMapNode> 
    </mvcSiteMapNode> 
</mvcSiteMap> 

Área de Registro:

public override void RegisterArea(AreaRegistrationContext context) 
{ 
     context.MapRoute(
      "Store_Index", 
      "Stores", 
      new { action = "Index", controller = "Home" }, 
      new string[] { "ControlPanel.Areas.Stores.Controllers" } 
      ); 

     context.MapRoute(
      "Store_default", 
      "Stores/{storeID}/{controller}/{action}/{id}", 
      new { action = "Index", controller = "Manage", id = UrlParameter.Optional }, 
      new { storeID = @"\d+" }, 
      new string[] { "ControlPanel.Areas.Stores.Controllers" } 
     ); 
    } 

primer intento:

La primera cosa que probé era crear el niño nodos justo en el mapa del sitio xml como niños del nodo dinámico Esto no funcionó en absoluto, y estos terminaron siendo hijos de "Hogar". Me puse un atributo ParentKey allí, excepto éstos se repetirán por tienda y por tanto habrá múltiples parentkeys

<?xml version="1.0" encoding="utf-8" ?> 
<mvcSiteMap xmlns="http://mvcsitemap.codeplex.com/schemas/MvcSiteMap-File-2.0"> 
    <mvcSiteMapNode title="Home" controller="Dashboard" action="Index" changeFrequency="Always" updatePriority="Normal"> 
    <mvcSiteMapNode title="My Account" controller="Account" action="Index" key="Account" /> 
    <mvcSiteMapNode title="My Stores" area="Stores" controller="Home" action="Index" visibilityProvider="ControlPanel.Areas.Stores.StoreAreaVisibilityProvider, ControlPanel" > 
     <mvcSiteMapNode title="Store" action="Index" dynamicNodeProvider="ControlPanel.Areas.Stores.StoreAreaNodeProvider, ControlPanel"> 
     <mvcSiteMapNode title="Products" area="Stores" controller="Products" action="Index"> 
      <mvcSiteMapNode title="Edit" area="Stores" controller="Products" action="Edit"/> 
      <mvcSiteMapNode title="New" area="Stores" controller="Products" action="Edit"/> 
     </mvcSiteMapNode> 
     </mvcSiteMapNode> 
    </mvcSiteMapNode> 
    </mvcSiteMapNode> 
</mvcSiteMap> 

segundo intento:

parecía que la siguiente opción era simplemente agregar el niño nodos directamente en mi DynamicNodeProvider. Esto funcionó mucho mejor a excepción de los nodos que tenían parámetros dinámicos

(el siguiente es modificado para facilitar la explicación ...)

public class StoreAreaNodeProvider : IDynamicNodeProvider 
{ 
    public IEnumerable<DynamicNode> GetDynamicNodeCollection() 
    { 
     var nodes = new List<DynamicNode>(); 

     foreach (var store in repo.GetStores()) 
     { 
      DynamicNode node = new DynamicNode(); 
      node.Title = store.Name; 
      node.Area = "Stores"; 
      node.Controller = "Manage"; 
      node.Action = "Index"; 
      node.RouteValues.Add("storeID", store.StoreID); 
      node.Key = "Store_" + store.StoreID.ToString(); 

      nodes.Add(node); 

      //Child of node 
      DynamicNode productsNode = new DynamicNode(); 
      productsNode.Title = "Products"; 
      productsNode.Area = "Stores"; 
      productsNode.Controller = "Products"; 
      productsNode.Action = "Index"; 
      productsNode.RouteValues.Add("storeID", store.StoreID); 
      productsNode.ParentKey = String.Format("Store_{0}", store.StoreID.ToString()); 
      productsNode.Key = String.Format("Store_{0}_Products", store.StoreID.ToString()); 

      nodes.Add(productsNode); 

      //child of productsNode 
      DynamicNode editNode = new DynamicNode(); 
      editNode.Title = "Edit"; 
      editNode.Area = "Stores"; 
      editNode.Action = "Edit"; 
      editNode.Controller = "Products"; 
      editNode.RouteValues.Add("storeID", store.StoreID); 
      //I can't add the RouteValue "ID" here because it is dynamic 
      //I would have do loop through every product for this store with 
      //another dynamic node provider, but that seems terribly inefficient and stupid 
      editNode.ParentKey = String.Format("Store_{0}_Products", store.StoreID.ToString()); 
      editNode.Attributes.Add("visibility", "SiteMapPathHelper,!*"); 

      nodes.Add(editNode); 
     } 

     return nodes; 
    } 
} 

En resumen

hace el trabajo: Tiendas/5/Productos/Nuevos
no funciona: Tiendas/5/Productos/Editar/23
Por patrón de URL: Tiendas/{storeID}/{controlador}/{acción}/{id}

Lo que me gustaría ser capaz de hacer:

editNode.Attributes.Add("isDynamic", "true"); 
editNode.Attributes.Add("dynamicParameters", "id"); 

¿Cómo puedo imitar dynamicParameters del viejo MvcSiteMapProvider atribuyen en un nodo que es hijo de un dynamicNode? Básicamente, necesito que ignore el valor de la ruta "id" cuando coinciden las rutas.

Afortunadamente lo expliqué correctamente y no te abrumé con información. ¡Gracias!


ACTUALIZACIÓN:

Aquí está la solución que trabajó para mí en base a la respuesta de Jakub.

En MvcSiteMapProvider 2.x, puede realizar su propia implementación de ISiteMapNodeUrlResolver en lugar de tener que modificar la fuente. Así que, básicamente, vuelve a añadir en la capacidad de tener el atributo dynamicParameters

Clase:

namespace ControlPanel 
{ 
    public class CustomSiteMapNodeUrlResolver : ISiteMapNodeUrlResolver 
    { 
     public virtual string ResolveUrl(MvcSiteMapNode mvcSiteMapNode, string area, string controller, string action, IDictionary<string, object> routeValues) 
     { 
      RequestContext ctx; 
      if (HttpContext.Current.Handler is MvcHandler) 
       ctx = ((MvcHandler)HttpContext.Current.Handler).RequestContext; 
      else 
       ctx = new RequestContext(new HttpContextWrapper(HttpContext.Current), new RouteData()); 

      //Begin Added Code 
      if (mvcSiteMapNode["dynamicParameters"] != null) 
      { 
       foreach (var item in mvcSiteMapNode["dynamicParameters"].Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries)) 
       { 
        var dp = item.Trim(); 
        routeValues[da] = ctx.RouteData.Values[dp]; 
       } 
      } 
      //End Added Code 

      return new UrlHelper(ctx).Action(action, controller, new RouteValueDictionary(routeValues)); 
     } 
    } 
} 

Web.config:

<siteMap defaultProvider="MvcSiteMapProvider" enabled="true"> 
    <providers> 
    <clear/> 
    <add name="MvcSiteMapProvider" 
     type="MvcSiteMapProvider.DefaultSiteMapProvider, MvcSiteMapProvider" 
     siteMapFile="~/Mvc.Sitemap" 
     securityTrimmingEnabled="true" 
     attributesToIgnore="visibility,dynamicParameters" 
     scanAssembliesForSiteMapNodes="true" 
     siteMapNodeUrlResolver="ControlPanel.CustomSiteMapNodeUrlResolver, ControlPanel" 
     siteMapNodeVisibilityProvider="MvcSiteMapProvider.FilteredSiteMapNodeVisibilityProvider, MvcSiteMapProvider" /> 
    </providers> 
</siteMap> 

dinámico de nodo del proveedor:

DynamicNode node = new DynamicNode(); 
node.Attributes.Add("dynamicParameters", "id"); 
+0

Ahora es 2012, estoy usando la versión 3.x y sigo teniendo el mismo problema. –

+0

Y el problema es que el método ResolveUrl personalizado se ejecuta tan pronto como se inicia la aplicación (sin navegar a esa página parametrizada), por lo que la línea que está obteniendo el parámetro de ruta actual ctx.RouteData.Values ​​[dp] es nula, por lo que no es Es posible reemplazar el parámetro de ruta del mapa del sitio con el parámetro de ruta actual. ¿Algunas ideas? –

+0

Hola @AlperOzcetin, no he tocado este código en tanto tiempo, no tengo ni idea. Y en realidad, unos meses después de esta publicación, este proyecto evolucionó lo suficiente como para que MvcSiteMapProvider no lo hiciera por mí, y terminamos lanzando nuestra propia solución desde cero (que el sitio aún ejecuta hoy). Buena suerte ... –

Respuesta

8

Estoy usando la versión 1.x. Tuve un problema similar con los parámetros dinámicos.

Tuve que modificar el código fuente: hice un cambio en MvcSiteMapNode.cs. Esta es la nueva aplicación de la propiedad Url

public override string Url 
    { 
     get 
     { 
      if (!string.IsNullOrEmpty(this.url)) 
       return this.url; 

      RequestContext ctx; 
      if (HttpContext.Current.Handler is MvcHandler) 
       ctx = ((MvcHandler)HttpContext.Current.Handler).RequestContext; 
      else 
       ctx = new RequestContext(new HttpContextWrapper(HttpContext.Current), new RouteData()); 

      var routeValues = new RouteValueDictionary(RouteValues); 

      foreach (var key in DynamicParameters) 
       routeValues.Add(key, ctx.RouteData.Values[key]); 

      return new UrlHelper(ctx).Action(Action, Controller, routeValues); 
     } 
     set 
     { 
      this.url = value; 
     } 
    } 

Observe cómo se añaden valores reales de dynamicParameters a la colección routeValues.

El cambio anterior me permitió definir paremteres dinámicos en el mapa del sitio (como 'id') y crear rutas de navegación con enlaces como Cuenta/Usuario/Editar/23.

Eché un vistazo a la versión 2.x y debería poder aplicar un parche similar. Espero que te ayude ...

+0

Impresionante ... Muchas gracias. Tomé su sugerencia y la modifiqué para que se ajustara al estilo de 2.x, y la publiqué como otra respuesta. –

+0

Además ... ¿cuál es la práctica común aquí? ¿Marcó su respuesta como la respuesta aceptada (lo que quiero hacer), o marcó mi respuesta ya que contiene el código que funcionó para mí? –

+0

@Chris - ¿Qué tal si editas tu pregunta y publicas tu último fragmento de código allí? PD. Me alegra que haya funcionado para ti. –

Cuestiones relacionadas