views:

442

answers:

3

Hi.

I am trying to create a menu for my website. It needs to meet the following requirements

  • it must be database driven, pulling data from DB to build up the menu structure
  • the data being pulled from the DB needs to be cached - I do not want to hit the DB for each page request

At the moment, I have a simplistic example running, but I don't know how to integrate caching. I think I might have to rework the entire way I do this. Here it is:

I have a ProductMenuAttribute, which pulls the data from the DB, and stores it in the ViewData:

public class ProductMenuAttribute: FilterAttribute, IActionFilter
{
    #region IActionFilter Members

    public void OnActionExecuted(ActionExecutedContext filterContext)
    {
        if (filterContext != null)
        {
            var context = filterContext.Result as ViewResult;
            if (context != null)
            {
                ProductsRepository repository = new ProductsRepository(Properties.Settings.Default.SqlConnectionString);

                context.ViewData.Add("ProductsList", repository.GetAllProductRanges());
            }
        }
    }

    public void OnActionExecuting(ActionExecutingContext filterContext)
    {

    }

    #endregion
}

In my Site.master I pull the data from the ViewData and use it to render my menu. This is a small snippet in my un-ordered menu list, which gets styled using CSS. Here is the code:

            <li>
                <%= Html.ActionLink("Products", "Index", "Products")%>

                <%  IQueryable<ProductRange> productRanges = ViewData["ProductsList"] as IQueryable<ProductRange>; %>

                    <% if (productRanges != null)
                       { %>

                    <ul>
                        <% foreach (ProductRange range in productRanges) 
                           { %>   
                            <li><%= Html.ActionLink(range.Name, "RangeDetails", "Products", new { id = range.ID }, null)%></li>
                        <% } %>
                    </ul>

                    <% } %>
            </li>

I then decorate each controller with the [ProductMenu] attribuate as follows:

[ProductMenu]
public class HomeController : BaseController
{
    public ActionResult Index()
    {
        return View();
    }

    public ActionResult About()
    {
        return View();
    }
}

Now whenever any of the actions on my controller are hit, the OnActionExecuted method in the ProductMenuAttribute class will be called, which will set the ViewData, which will eventually be used on my Site.Master to build up my menu from the DB, which time I call any of the actions.

The problem now is - how do I add caching to this scenario?? I have no clue where to start, and have a feeling the solution I have is not cacheable.

+1  A: 

You can modify your repository to be cache-aware: See this two questions: cached repository and http cache.

rrejc
A: 

I think what I am really looking for is to use the Html.RenderAction() helper extension from the MVC Futures project.

The idea is to use the above to call an action on a controller, which will generate the HTML menu structure by pulling data from the DB. I then also use simple Output Caching to cache the data for a few minutes..

This is the simplest approach I've found so far to achieve what I want.

EDIT: Phil Haack blogged about this recently - Html.RenderAction and Html.Action. A great blog post covering all my exact needs, with explanations of all the issues.

To get the caching to work correctly, I would need to put my Html.RenderAction() call inside a ViewUserControl with the OutputCaching directive set as follows:

<@ OutputCache Duration="100" VaryByParam="None" %>

I then call the above with Html.RenderPartial(), and voila, it all works!

Saajid Ismail
A: 

I have done this by using Enterprise Library Caching block. You check for cached data, if there is no cached data, you create html string from data in your database and put it in cache so later when searching for cached data you can just output plain string :D

Just to mention that code for doing that was in static class. Ill post example but i am rewriting that app in MVC2 from scratch so i dont have code by hand.

zveljkovic