2010-11-09 10 views
19

Sto utilizzando MvcSiteMapProvider 2.2.1 (http://mvcsitemap.codeplex.com) e sto avendo un problema con la creazione di bambini in un nodo dinamico (utilizzando un dynamicNodeProvider) quando quei bambini hanno un parametro dinamico (id).Creazione di nodi figlio per un DynamicNode in MvcSiteMapProvider con parametri dinamici

sto perdendo il pangrattato per il seguente itinerario:

Stores/5/Prodotti/Modifica/23

dove il modello url è:

Stores/{storeID}/{ controller}/{action}/{id}

Funziona correttamente quando l'ID viene omesso (vale a dire l'azione "Nuovo"). Ma quando l'ID è specificato, non corrisponde alla rotta e il mio breadcrumb (usando l'helper SiteMapPath) è vuoto.

mio sito: (abbreviati)

<?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> 

Area di registrazione:

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" } 
     ); 
    } 

primo tentativo:

La prima cosa che ho provato è stato quello di creare il bambino nodi nella sitemap xml come bambini del nodo dinamico. Questo non ha funzionato affatto e questi sono diventati figli di "Home". Avrei messo un attributo ParentKey in là, tranne questi saranno ripetuti per punto vendita e, quindi, non ci sarà più 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> 

secondo tentativo:

sembrava l'opzione successiva è stato quello di aggiungere solo il bambino nodi proprio nel mio DynamicNodeProvider. Questo ha funzionato molto meglio, tranne per i nodi che avevano parametri dinamici

(il seguito vengono modificati per facilità di spiegazione ...)

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; 
    } 
} 

In sintesi

funziona: Stores/5/Prodotti/Nuovi
non funziona: Negozi/5/Prodotti/Modifica/23
testo Url: Negozi/{storeID}/{Controller}/{action}/{id}

Quello che mi piacerebbe essere in grado di fare:

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

Come posso mimare dynamicParameters del vecchio MvcSiteMapProvider attributo un nodo figlio di un dynamicNode? Fondamentalmente mi serve per ignorare il valore del percorso "id" quando si abbinano percorsi.

Spero di averlo spiegato correttamente e di non averti sopraffatto dalle informazioni. Grazie!


UPDATE:

ecco la soluzione che ha funzionato per me in base alla risposta di Jakub.

In MvcSiteMapProvider 2.x, è possibile eseguire la propria implementazione di ISiteMapNodeUrlResolver invece di dover modificare l'origine. Così ho praticamente aggiunto indietro nella capacità di avere l'attributo dynamicParameters

Classe:

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> 

dinamico nodo Provider:

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

È il 2012, sto usando la versione 3.x e ho ancora lo stesso problema. –

+0

E il problema è che il metodo ResolveUrl personalizzato viene eseguito non appena l'applicazione si avvia (senza accedere a quella pagina parametrizzata), quindi la riga che si ottiene il parametro di percorso corrente ctx.RouteData.Values ​​[dp] è nullo, quindi non è possibile sostituire il parametro del percorso Sitemap con il parametro del percorso corrente. Qualche idea? –

+0

Ciao @AlperOzcetin, non ho toccato questo codice da così tanto tempo, non ne ho idea. E in effetti pochi mesi dopo questo post, questo progetto si è evoluto abbastanza da dove MvcSiteMapProvider non lo stava facendo per me, e abbiamo finito con il rollare la nostra soluzione da zero (su cui il sito funziona ancora oggi). Buona fortuna ... –

risposta

8

Sto usando la versione 1.x. Ho avuto un problema simile con i parametri dinamici.

Ho dovuto modificare il codice sorgente - apportato una modifica in MvcSiteMapNode.cs. Questa è la nuova implementazione di proprietà 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; 
     } 
    } 

Notate come sono aggiunti i valori effettivi di dynamicParameters alla raccolta routeValues.

Il cambiamento di cui sopra mi ha permesso di definire parametri dinamici in sitemap (come 'id') e creare breadcrumb con link come Account/Utente/Modifica/23.

Ho dato una breve occhiata alla versione 2.x e dovresti essere in grado di applicare una patch simile. Spero che ti possa aiutare ...

+0

Fantastico ... Grazie mille. Ho preso il tuo suggerimento e l'ho modificato per adattarlo allo stile di 2.x, e lo ho postato come un'altra risposta. –

+0

Inoltre ... qual è la pratica comune qui? Segnalo come risposta accettata (cosa voglio fare) o contrassegno la mia risposta poiché contiene il codice che ha funzionato per me? –

+0

@Chris - Che ne dici di modificare la tua domanda e pubblicare qui il tuo ultimo pezzo di codice? PS. Sono contento che abbia funzionato per te. –

Problemi correlati