1

I have a little problem, could you explain me what is the best practice to load menu items from DB using MVC5 and Entity Framework6. The menu and localization object must be loaded only once, and then just used from some globally available collection. They are not going to change alot after website launch, so I just goung to implement some Update() method and I'll call it when necessary...

2 Answers2

2

Use child actions.

public class FooController : Controller
{
    ...

    [ChildActionOnly]
    public ActionResult SiteMenu()
    {
        // load menu items however you need to

        return PartialView("_SiteMenu", menuModel);
    }
}

/Views/Shared/_SiteMenu.cshtml

@model Namespace.To.MenuModel

<!-- render your menu here -->

/Views/Shared/_Layout.cshtml

<!-- wherever you want the menu to display -->
@Html.Action("SiteMenu", "Foo")

If you want to cache the result, so the menu doesn't have to be pulled from the DB each request, then you can use the OutputCache attribute on the child action like any other action.

Chris Pratt
  • 207,690
  • 31
  • 326
  • 382
  • In addition to this answer, if you use outputcache and you are going to update your menu content you should reset the cache http://stackoverflow.com/questions/1167890/how-to-programmatically-clear-outputcache-for-controller-action-method – Alex Art. Apr 29 '15 at 18:14
  • Only necessary if you're going to cache some period of time that's near indefinite. Setting a lifetime on the menu of say an hour would be more than sufficient to reduce any undue strain on the database and still get relatively timely updates as they occur. Remember also that cache is memory-based, so even if you cache some incredibly long time, there's no guarantee it's not refreshing more frequently anyways. – Chris Pratt Apr 29 '15 at 18:17
  • Why not to use Global.asax or Application["menuItems"] ? – Alexandre Ostapenko Apr 29 '15 at 18:17
  • Global.asax is the beating heart of your application. It should be *lean* and perform the bare minimum just to bootstrap everything. That's why the `App_Start` files exist: to keep you from having to touch Global.asax. – Chris Pratt Apr 29 '15 at 18:20
  • Use the OutputCache varybycustom - then add an override to the Global.asax file to handle the VaryByCustomString, then cache based on the request locale - that way each local is cached once. You can set the duration to something extremely long. – thorkia Apr 29 '15 at 19:58
  • There is only one problem, i cannot force cache update without tricky codes :) – Alexandre Ostapenko Apr 30 '15 at 06:49
1

As i have already sad, I have thinked about Global.asax So there is currently 2 ways how I can do it with Global.asax:

Update using this method is bad idea, use the second one instead

public static ICollection<MenuItem> MenuItems {
    get
    {
        if (Application["MenuItems"] != null)
            return (ICollection<MenuItems>)Application["MenuItems"];
        else
            return new ICollection<MenuItems>();
    }
    set
    {
        Application["MenuItems"] = value;    
    }
}

private void LoadMenuItems()
{
    MyContext mc = new MyContext();
    this.MenuItems = ms.MenuItems.Include("SubCategories").AsNotTacking().where(x => x.SubCategory == null).ToArray();
} 

protected void Application_Start(object sender, EventArgs e)
{
    this.MenuItems = LoadMenuItems();
}

And another way (The second one):

public static ICollection<MenuItem> MenuItems { get; set; }

private void LoadMenuItems()
{
    MyContext mc = new MyContext();
    this.MenuItems = ms.MenuItems.Include("SubCategories").AsNotTacking().where(x => x.SubCategory == null).ToArray();
} 

protected void Application_Start(object sender, EventArgs e)
{
    this.MenuItems = LoadMenuItems();
}

And same thing for Localization...

Actually i dont know which one is better, need to run some tests.

Almost forgot: All the things, are contained in the "CustomHttpApplication" class, which is derrived from "HttpApplication" class. and Global.asax shoul be derived from "CustomHttpApplication" class. This way the Global.asax file will be cean and readable, but the business logic will be located one level down...

So the complete code could look like so:

CustomHttpApplication.cs

public class CustomHttpApplication : HttpApplication
{
    public static ICollection<MenuItem> MenuItems { get; set; }

    private void LoadMenuItems()
    {
        MyContext mc = new MyContext();
        this.MenuItems = ms.MenuItems.Include("SubCategories").AsNotTacking().where(x => x.SubCategory == null).ToArray();
    } 
}

Global.asax.cs

public class MvcApplication : CustomHttpApplication
{
    protected void Application_Start(object sender, EventArgs e)
    {
        MenuItems = this.LoadMenuItems();
    }
}

And one more edit, if you/me convert the "LoadMenuItems" method to be a "static" one, than you/me will be able to update MenuItems and/or Localization item collections when needed.