Search code examples
xmlasp.net-mvcdatamodel

Data model help for asp.net MVC


I am new to MVC and decided to use it for my newest project. I have been tasked to redesign the intranet homepage for my company, which is effectively a bunch of links and sub-links in columns.

Example:

  • Head
    • Link 1
    • Link 2
  • Link 3
  • Link 4
    • Link 5

The catch is they want those links to come from a database which will be managed (add, edit, order, delete) from the /admin page. Since it is the homepage, I would like to make as few db calls as possible but doesn't have to be this way.

I created a db table and found in MVC it was difficult (I did kind of get it though) to iterate through and render it on the page without lots of code in the View page, I figured this is not the purpose/best practice of MVC and I must be doing something wrong with my Model.

Here is my table structure with example records:

ID|  Text     | Href       | OrderId | NewWindow | ParentId
1 |  Head     | NULL       |    1    |    0      |   NULL
2 | Link 1    | link1.htm  |    1    |    1      |    1
3 | Link 2    | link2.htm  |    2    |    1      |    1
4 | Link 3    | link3.htm  |    2    |    1      |   NULL
5 | Link 4    | link4.htm  |    3    |    1      |   NULL
6 | Link 5    | link5.htm  |    1    |    1      |    5

Pretty much I was doing this really messy foreach loop which returned the whole table and I was determining "head (parent) links" and handling the HTML accordingly. It really reminded me of the classic asp days.

I am hoping that there is something to do with the model and work with smaller data sets.

Do you have any tips/suggestions/comments on how I should handle this?


Solution

  • As you say, it's better to avoid such complex logic in your view. As much as possible, you should pass your view simple data to display. You will always likely have some if and foreach statements, but anything more complex than that you want to avoid pretty much.

    It is better to have the data access and the assembly of your objects in an action method in your controller, never in the view, and typically then even these operations might be farmed out to some other service (e.g. a call to a repository, a call to a mapper.) In general you are also better off passing ViewModel objects to your view, rather than the objects straight from your domain. (Although in this case, as the objects are always intended for display purposes anyway, you can probably not worry about that too much.)

    You might put all this stuff in an action method on your HomeController if these links are specific to the home page, or if it's a site-wide menu, you could have a separate NavController to specifically deal with it.

    e.g.

    public class NavController : Controller
    {
        // ...
    
        [ChildActionOnly]
        public PartialViewResult SiteMenu(string currentPage)
        {
            // Get your flat list of menu items from the DB
            var menuItems = _siteMapRepo.GetMenuItems();
    
            // Map them into something more tree-like...
            var treeMenu = MapFlatItemsToTreeMenu(menuItems);
    
            return View("_Menu", treeMenu);
        }
    
        // ...
    }
    

    Then you could have a partial view that renders these, e.g. _Menu.cshtml.

    Then somewhere in a parent view, most likely your site layout, you could have a RenderAction call to pull the menu in:

    @{Html.RenderAction("SiteMap", "Nav");}
    

    The code in _Menu.cshtml might get interesting, as you're dealing with a tree structure. You might have recursive calls in your partial view, bottoming out at leaf nodes, preferably using RenderPartial rather than RenderAction at this point as RenderAction results in a new request. Something like...

    @model MenuItem
    
    @if (Model.HasChildren)
    {
        <ul>
        @foreach (var child in Model.Children)
        {
            @{Html.RenderPartial("_Menu", child);}
        }
        </ul>
    }
    else
    {
        <li>
           // do something with Model.Url etc.
        </li>
    }
    

    DISCLAIMER: I haven't actually tried this, just written it out, but it should give the gist I hope.

    You could also of course see if someone else has already rolled something like this already that you can use, for example MvcSiteMapProvider or MvcTreeView.