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");
Teraz jest 2012, używam wersji 3.x i nadal mam ten sam problem. –
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? –
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 ... –