2

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

tereško
  • 58,060
  • 25
  • 98
  • 150
touinta
  • 971
  • 3
  • 10
  • 26

1 Answers1

2

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;
        }
    }
}
NightOwl888
  • 55,572
  • 24
  • 139
  • 212
  • hello, thank you for your reply. Now it gives the error: Not all configured nodes could be paired with a parent node. Check your parent keys to ensure that a node with a corresponding key exists in the SiteMap. Orphaned Nodes: ParentKey: 'ArticleUrl' | Controller: 'SiteContents' | Action: 'ArticleDetails' | Area: '' | URL: '/ArticleDetails/1' | Key: '0ea47139-9577-43ae-bdc4-b304293e788d' | Source: 'Myproject.Models.MyDynamicNodeProvider, Myproject' – touinta Feb 26 '16 at 06:47
  • In the example you posted, you have the key of the parent node set to `key="ArticleUrl"`. You have apparently changed it to something else or removed it. You need to have the same value in the `key` attribute of the parent node as the `ParentKey` property of the `DynamicNode`. – NightOwl888 Feb 26 '16 at 09:34
  • I change it to ArticleUrl but again it doesn't show nothing again. No errors. Static nodes works ok. I have one table only. My sitemap file: ` – touinta Feb 26 '16 at 09:55
  • Please edit your question and put 1) your current `Mvc.sitemap` contents, 2) The controller(s) and action(s) you are trying to match, and 3) your routing configuration. You are clearly not configuring one of these correctly, but since you haven't posted them I cannot tell which. It would also help if you post the URL you are attempting to go to when the breadcrumbs disappear. – NightOwl888 Feb 26 '16 at 10:37
  • Also post what you have currently in your dynamic node provider. – NightOwl888 Feb 26 '16 at 10:38
  • I have set the key at the ArticleID but whatever I try lets say ArticleUrl field or something else it doesn't bring nothing as sitemap – touinta Feb 26 '16 at 11:09
  • I also post my Route.config. Thanks a lot – touinta Feb 26 '16 at 11:50
  • I made all the changes you suggest. Still nothing no errors no breadcrumbs. :) My site and the sitemap works ok for all the static pages I have. The problem appears only to dynamic pages which I get them from the database. If I remove the ` ` I get the error: Not all configured nodes could be paired with a parent node. Check your parent keys to ensure that a node with a corresponding key exists in the SiteMap. – touinta Feb 26 '16 at 13:32
  • .....Orphaned Nodes: ParentKey: 'Articles' | Controller: 'SiteContents' | Action: 'ArticleDetails' | Area: '' | URL: '/ArticleDetails/Home/About' | Key: '84128a15-1bd9-4e03-944c-ebc37f425815' | Source: 'MyprojectName.Models.ElkeDynamicNodeProvider, MyprojectName' – touinta Feb 26 '16 at 13:33
  • I dont have the same controller for my Home page and and Articles details action. Thank you again – touinta Feb 26 '16 at 13:35
  • It finally worked. I just have to change the enode.RouteValues.Add("id", article.ArticleUrl); to enode.RouteValues.Add("id", article.ArticleID); thanks a lot again. – touinta Feb 26 '16 at 13:48
  • last but not least...I promise last question. In the breadcrumbs it returns Home> Profile> ArticleTitle > Quality Policy. I dont need the ArticleTitle to appear. It has to be Home> Profile> Quality Policy. – touinta Feb 26 '16 at 14:01
  • If you just have one policy for the whole site, add a nested node below the `Profile` node (note I have changed my answer to reflect this). If you have one policy per article, then you can add an extra dynamic node (specifying the key-parent key mapping correctly) and use the [SiteMapTitleAttribute](https://github.com/maartenba/MvcSiteMapProvider/wiki/Using-Action-Filter-Attributes#sitemaptitle) to update the title of the parent node dynamically when you navigate to the policy page. – NightOwl888 Feb 26 '16 at 14:15
  • I have one Policy article for whole site and I call Policy content from my database. I have to store all articles to database. I will give a try and I let you know how it goes. thank you! – touinta Feb 26 '16 at 18:16
  • hello again. What if I need to change the parent Key. I mean that I have some more parents menu. The one I have is Home>Profile>Quality Policy and the other Home>eServices> eService 1. It always brings the Home>Profile> (and the according article title). thank you. – touinta Feb 29 '16 at 13:42
  • I post a new question here http://stackoverflow.com/questions/35717293/change-parent-key-dynamically-idynamicnodeprovider-mvc. It will be great if you can help. Thank you – touinta Mar 01 '16 at 07:48