I have implemented this https://github.com/maartenba/MvcSiteMapProvider/wiki/Defining-sitemap-nodes-using-IDynamicNodeProvider
Edit: this is my class
public class MyDynamicNodeProvider
: DynamicNodeProviderBase
{
public override IEnumerable<DynamicNode> GetDynamicNodeCollection(ISiteMapNode node)
{
webdata storeDB = new webdata();
var returnValue = new List<DynamicNode>();
foreach (var article in storeDB.SiteContents)
{
DynamicNode enode = new DynamicNode();
enode.Title = article.ArticleTitle;
enode.ParentKey = "ArticleID";
enode.Url = "ArticleDetails/" + article.ArticleID + "/" + article.ArticleAlias;
//Specify Controller and Action name
enode.Controller = "SiteContents";
enode.Action = "ArticleDetails";
enode.RouteValues.Add("id", article.ArticleID);
returnValue.Add(enode);
yield return enode;
}
}
}
Edit:this is my sitemap file
<mvcSiteMapNode title="Home" controller="Home" action="Index">
<mvcSiteMapNode title="About Us" controller="Menu" action="AboutUs">
<mvcSiteMapNode title="Profile" controller="Menu" action="Profile"/>
<mvcSiteMapNode title="History" controller="Menu" action="History"/>
</mvcSiteMapNode>
<mvcSiteMapNode title="Article" controller="SiteContents" action="ArticleDetails" key="ArticleID">
<mvcSiteMapNode title="Details" dynamicNodeProvider="Myproject.Models.MyDynamicNodeProvider, Myproject" />
</mvcSiteMapNode>
Edit: The second controller which I have (SiteContentsController )
public ActionResult ArticleDetails(int? id, string slug)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
SiteContents siteContents = db.SiteContents.Find(id);
if (siteContents == null)
{
return HttpNotFound();
}
if (string.IsNullOrWhiteSpace(slug))
{
var alias = db.SiteContents.First(p => p.ArticleID == id).ArticleAlias;
return RedirectToAction("ArticleDetails", new { id = id, slug = alias });
}
return View(siteContents);
}
The url I would like to have (which work but it doesn't bring the sitemap
is http://localhost:xxxx/ArticleDetails/1/Quality_Policy
and I call the sitemap at my layoutpage
@Html.MvcSiteMap().SiteMapPath()
Edit: My route.config
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(name: "Articles", url: "ArticleDetails/{id}/{slug}", defaults: new { controller = "SiteContents", action = "ArticleDetails", id = UrlParameter.Optional, slug = UrlParameter.Optional });
routes.MapRoute(name: "Default", url: "{controller}/{action}/{id}", defaults: new {controller = "Home", action = "Index", id = UrlParameter.Optional});
}
I also have some static nodes which work ok. The problem is that with in dynamic pages returns nothing and I dont get any error message thank you
The reason why it is not working is that you have not accounted for all of the route values, namely you have a route value named slug
which you need to configure the node to match.
If you want the node to match regardless of the value for slug
(even if it is blank), you should use PreservedRouteParameters
to match it. Otherwise, you should add it to RouteValues
and the node will only match the one value you configure for it (you can add additional nodes to match other values if needed). I am showing the PreservedRouteParameters
approach here.
In addition, you have effectively disabled MVC support by configuring the Url
property on your dynamic node. This property is useful if you need to use a non-MVC page or an external URL, but is not recommended for MVC.
MvcSiteMapProvider
depends directly on the MVC routing configuration. This is where you configure your URLs to look how you want them to look. For your expected URL (http://localhost:xxxx/ArticleDetails/1/Quality_Policy
) to work, you need a corresponding route to match this pattern, as below.
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
// Route to match the URL /ArticleDetails/1/Quality_Policy
routes.MapRoute(
name: "ArticleDetails",
url: "ArticleDetails/{id}/{slug}",
defaults: new { controller = "SiteContents", action = "ArticleDetails", slug = UrlParameter.Optional }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
}
One other issue you have is the node you are attaching the dynamic nodes to. The way you have your node configured currently goes to the ArticleDetails
action. I can't tell what you are trying to do here. Normally, you would show a list (index) of all of the article pages and then when the user clicks an article, you would show it. Here is an example.
// NOTE: Normally, you would put all of your Article stuff
// into an ArticleController
public class SiteContentsController
{
// NOTE: Normally, this would be named ArticleController.Index()
public ActionResult ArticleIndex()
{
// NOTE: You may want to use a view model here
// rather than using the SiteContents directly.
var siteContents = db.SiteContents.ToList();
return View(siteContents);
}
// NOTE: Normally, this would be named ArticleController.Details()
public ActionResult ArticleDetails(int? id, string slug)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
SiteContents siteContents = db.SiteContents.Find(id);
if (siteContents == null)
{
return HttpNotFound();
}
if (string.IsNullOrWhiteSpace(slug))
{
var alias = db.SiteContents.First(p => p.ArticleID == id).ArticleAlias;
return RedirectToAction("ArticleDetails", new { id = id, slug = alias });
}
return View(siteContents);
}
}
And your Mvc.sitemap
file would look more like this (with articles being below the home page). I believe this is your main issue - you must only have one root node in your XML file (usually, it is the home page of the site).
<mvcSiteMapNode title="Home" controller="Home" action="Index">
<mvcSiteMapNode title="About Us" controller="Menu" action="AboutUs">
<mvcSiteMapNode title="Profile" controller="Menu" action="Profile">
<mvcSiteMapNode title="Quality Policy" controller="Menu" action="Policy"/>
</mvcSiteMapNode>
<mvcSiteMapNode title="History" controller="Menu" action="History"/>
<mvcSiteMapNode title="Articles" controller="SiteContents" action="ArticleIndex" key="Articles">
<mvcSiteMapNode title="Details" dynamicNodeProvider="Myproject.Models.MyDynamicNodeProvider, Myproject" />
</mvcSiteMapNode>
</mvcSiteMapNode>
Finally, we have the corrected DynamicNodeProvider
.
public class MyDynamicNodeProvider
: DynamicNodeProviderBase
{
public override IEnumerable<DynamicNode> GetDynamicNodeCollection(ISiteMapNode node)
{
webdata storeDB = new webdata();
foreach (var article in storeDB.SiteContents)
{
DynamicNode enode = new DynamicNode();
enode.Title = article.ArticleTitle;
enode.ParentKey = "Articles";
// Don't use the Url property unless you have a
// non-MVC page/external URL
//enode.Url = "ArticleDetails/" + article.ArticleID + "/" + article.ArticleAlias;
// Specify Controller, Action name, and id.
// These values all must match the request in order
// for the node to be considered the "current" node
enode.Controller = "SiteContents";
enode.Action = "ArticleDetails";
enode.RouteValues.Add("id", article.ArticleID);
// Match the slug (we don't really care what its value is here)
enode.PreservedRouteParameters.Add("slug");
yield return enode;
}
}
}