0

I try to put a dynamic menu (load from xml) in my Layout but I've got a StackOverflowException in PartialController.cs/MainMenu()

I don't understand why my code throw a StackOverflowException because I don't have a loop (or I don't see it !).

Layout.cshtml :

....
<div id="menu">
    @if (Request.IsAuthenticated)
    {
        Html.RenderAction("MainMenu", "Partial");
    }
</div>
....

MainMenu.cshtml :

@model Geosys.BoT.Portal.POC.Business.Menu

@foreach (var item in Model.Nodes)
{
    <ul>
        <li>
            @item.Name
            <ul>
                @foreach (var subItem in item.Links)
                {
                    <li>
                        @Html.ActionLink(subItem.Name, subItem.Action, subItem.Controller)
                    </li>
                }
            </ul>
        </li>
    </ul>
}

PartialController.cs :

[ChildActionOnly]
public ActionResult MainMenu()
{
    var menu = new Menu { Nodes = new List<NodeMenu>() };

    var xmlData = System.Web.HttpContext.Current.Server.MapPath("~/Content/navigation.xml");
    if (xmlData == null)
    {
        throw new ArgumentNullException("xmlData");
    }

    var xmldoc = new XmlDataDocument();

    var fs = new FileStream(xmlData, FileMode.Open, FileAccess.Read);
    xmldoc.Load(fs);

    var xmlnode = xmldoc.GetElementsByTagName("node");

    for (var i = 0; i <= xmlnode.Count - 1; i++)
    {
        var xmlAttributeCollection = xmlnode[i].Attributes;

        if (xmlAttributeCollection != null)
        {
            var nodeMenu = new NodeMenu { Name = xmlAttributeCollection["title"].Value, Links = new List<LinkMenu>() };

            if (xmlnode[i].ChildNodes.Count != 0)
            {
                for (var j = 0; j < xmlnode[i].ChildNodes.Count; j++)
                {
                    var linkMenu = new LinkMenu();

                    var xmlNode = xmlnode[i].ChildNodes.Item(j);
                    if (xmlNode != null)
                    {
                        if (xmlNode.Attributes != null)
                        {
                            linkMenu.Name = xmlNode.Attributes["title"].Value;
                            linkMenu.Action = xmlNode.Attributes["action"].Value;
                            linkMenu.Controller = xmlNode.Attributes["controller"].Value;
                            linkMenu.Key = xmlNode.Attributes["key"].Value;

                            nodeMenu.Links.Add(linkMenu);
                        }
                    }
                }
            }

            menu.Nodes.Add(nodeMenu);
        }
    }
    return View(menu);
}

navigation.xml:

<nodes>
  <node title="User Management">
    <link title="Create User" action="CreateUser" controller="UserManagement" key="UM_CREATEUSER" />
    <link title="Users List" action="UsersList" controller="UserManagement" key="UM_USERSLIST" />
    <link title="Import Users" action="ImportUsers" controller="UserManagement" key="UM_IMPORTUSERS" />
  </node>
</nodes>

EDIT : This is the detail of the exception (there's no StackTrace):

System.StackOverflowException was unhandled An unhandled exception of type 'System.StackOverflowException' occurred in mscorlib.dll

{Cannot evaluate expression because the current thread is in a stack overflow state.}

In the Call Stack, I see the line "Html.RenderAction("MainMenu", "Partial");" constantly called but I don't know why.

3
  • 2
    What do you mean, I see 3 loops in this code. 1 foreach loop and 2 for loops. If you can add the stack trace from the overflow exception, that may help narrow down where the stack is overflowing. Commented Dec 4, 2014 at 15:49
  • You may try LINQ expression to get list of selected nodes instead of loops ( if you need help on that, let me know). Side note: Finally saw stackoverflow exception in stackoverflow.com :) . Commented Dec 4, 2014 at 15:54
  • When I say "I don't see a loop", I mean I don't see a recursive loop. I know I've got 3 loops but the stackoverflow exception isn't in these loops. The MainMenu() method is called several times. I'll put the stacktrace in the main. Commented Dec 5, 2014 at 8:24

1 Answer 1

3

There is an infinite loop here. The problem is that you do this in your Layout:

@Html.RenderAction("MainMenu", "Partial");

And then in your Action you do this:

return View(menu);

When you call return View() your view is rendered, including your layout. So your layout then renders your @Html.RenderAction(...) again... which calls your View()... which renders your Layout... etc.. etc.. etc..

You solve this by returning a PartialView() which does not render your Layout, or by rendering a view with Layout set to Null. This is the primary difference between a View() and a PartialView(), Layout rendering.

Sign up to request clarification or add additional context in comments.

1 Comment

I've found this solution in other places on SO, but you're the only one that explained why. Thanks!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.