2010-11-09 10 views
19

Używam MvcSiteMapProvider 2.2.1 (http://mvcsitemap.codeplex.com) i mam problem z tworzeniem elementów potomnych w węźle dynamicznym (za pomocą metody dynamicNodeProvider), gdy te dzieci mają parametr dynamiczny (id).Tworzenie węzłów podrzędnych dla węzła DynamicNode w MvcSiteMapProvider z parametrami dynamicznymi

Tracę bułkę tartą na następującej trasie:

Stores/5/Produkty/Edycja/23

gdzie wzorzec URL jest:

Sklepy/{storeID}/{ kontroler}/{action}/{id}

Działa poprawnie, gdy identyfikator jest pominięty (tj. akcja "Nowa"). Ale gdy identyfikator jest określony, nie pasuje on do trasy, a moje bułki tartej (za pomocą pomocnika SiteMapPath) są puste.

Mój Blog: (skrócona)

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

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

pierwsza próba:

Pierwszą rzeczą próbowałem było stworzenie dziecku węzłów w pliku xml jako dzieci węzła dynamicznego. To w ogóle nie zadziałało, a w końcu były to dzieci z "Domu". Chciałbym umieścić atrybut ParentKey tam, z wyjątkiem tych, zostaną powtórzone na sklepie, a tym samym nie będzie wiele 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> 

Druga próba:

Wydawało kolejnym rozwiązaniem było po prostu dodać dziecku węzły bezpośrednio w moim DynamicNodeProvider. To działało znacznie lepiej z wyjątkiem węzłów, które miały parametry dynamiczne

(poniżej jest modyfikowana w celu ułatwienia wyjaśnienia ...)

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

Podsumowując

działa: Sklepy/5/Produkty/Nowość Nie działa: Sklepy/5/Produkty/Edycja/23
Wzór adresu URL: Sklepy/{storeID}/{kontroler}/{działania}/{id}

Co chciałbym być w stanie to zrobić:

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

Jak mogę mimick dynamicParameters starego MvcSiteMapProvider za atrybut węzeł będący dzieckiem węzła dynamicNode? Zasadniczo potrzebuję go, aby zignorować wartość "id" trasy podczas dopasowywania tras.

Mam nadzieję, że wyjaśniłem to poprawnie i nie przytłoczyłem was informacjami. Dzięki!


UPDATE:

Oto rozwiązanie, które pracował dla mnie na podstawie odpowiedzi Jakuba.

W MvcSiteMapProvider 2.x można dokonać własnej implementacji ISiteMapNodeUrlResolver zamiast modyfikować źródło. Więc w zasadzie dodane z powrotem w zdolności mają atrybut dynamicParameters

Klasa:

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> 

Dynamiczny Węzeł Provider:

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

Teraz jest 2012, używam wersji 3.x i nadal mam ten sam problem. –

+0

Problem polega na tym, że Twoja niestandardowa metoda ResolveUrl działa zaraz po uruchomieniu aplikacji (bez przechodzenia do tej sparametryzowanej strony), więc linia, dla której pobierasz bieżący parametr trasy ctx.RouteData.Values ​​[dp] ma wartość null, więc nie jest możliwe zastąpienie parametru trasy mapy witryny aktualnym parametrem trasy. Jakieś pomysły? –

+0

Hi @AlperOzcetin, nie tknąłem tego kodu od tak dawna, nie mam pojęcia. I rzeczywiście, kilka miesięcy po tym poście, projekt ten ewoluował na tyle, że MvcSiteMapProvider nie robił tego dla mnie, a my zakończyliśmy wdrażanie naszego własnego rozwiązania od zera (którego strona wciąż działa dzisiaj). Powodzenia ... –

Odpowiedz

8

Używam wersji 1.x. Miałem podobny problem z parametrami dynamicznymi.

Musiałem zmodyfikować kod źródłowy - dokonałem zmiany w MvcSiteMapNode.cs. Jest to nowa implementacja właściwość 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; 
     } 
    } 

Wskazówki jak rzeczywiste wartości dynamicParameters są dodawane do kolekcji routeValues.

Powyższa zmiana pozwoliła mi zdefiniować dynamiczne paremteres w mapie witryny (np. "Id") i utworzyć bułkę z linkami takimi jak Konto/Użytkownik/Edycja/23.

Przyjrzałem się krótko wersji 2.x i powinieneś być w stanie zastosować podobną łatkę. Mam nadzieję, że pomoże ci ...

+0

Awesome ... Dziękuję bardzo. Wziąłem twoją sugestię i zmodyfikowałem ją tak, aby pasowało do stylu 2.x i opublikowałem ją jako kolejną odpowiedź. –

+0

Również ... co jest tutaj powszechną praktyką? Czy zaznaczam twoją odpowiedź jako zaakceptowaną odpowiedź (co chcę zrobić), czy też zaznaczam swoją odpowiedź, ponieważ zawiera ona kod, który zadziałał dla mnie? –

+0

@Chris - A może edytujesz swoje pytanie i opublikujesz tam ostatni fragment kodu? PS. Cieszę się, że to zadziałało dla ciebie. –