views:

37

answers:

2

Hi,

I've been reading 'ASP.NET-MVC Tutorials' to learn how to generate data for the 'masterpage' view in an ASP.NET MVC application. It suggests the pattern of using a 'base-controller' and generating the data in its constructor.

My issue is that I wish to store the application data in the application cache rather than the viewdata dictionary. The application cache doesn't exist in the controller constructors as its set later, how can I store data for the masterpage view in application cache?

A: 

Figured it out. Well.... a version that works. I needed to add the applciation data to the cache when the ControllerActionInvoker's 'InvokeAction' method fires. To do this I had to create a new ActionInvoker as below.

public class ContextActionInvoker : ControllerActionInvoker
{
    public const string testMessageCacheAndViewDataKey = "TESTMESSAGE";
    private const int testListLifetimeInMinutes = 10;

    public ContextActionInvoker(ControllerContext controllerContext) : base() { }

    public override bool InvokeAction(ControllerContext context, string actionName)
    {
        // Cache a test list if not already done so
        var list = context.HttpContext.Cache[testMessageCacheAndViewDataKey];
        if (list == null)
        {
            list = new SelectList(new[] {
                new SelectListItem { Text = "Text 10", Value = "10" },
                new SelectListItem { Text = "Text 15", Value = "15", Selected = true },
                new SelectListItem { Text = "Text 25", Value = "25" },
                new SelectListItem { Text = "Text 50", Value = "50" },
                new SelectListItem { Text = "Text 100", Value = "100" },
                new SelectListItem { Text = "Text 1000", Value = "1000" }
            }, "Value", "Text");
            context.HttpContext.Cache.Insert(testMessageCacheAndViewDataKey, list, null, DateTime.Now.AddMinutes(testListLifetimeInMinutes), TimeSpan.Zero);
        }
        context.Controller.ViewData[testMessageCacheAndViewDataKey] = list;

        return base.InvokeAction(context, actionName);
    }
}

Once this was done I needed to create a custom controllerfactory that would make sure the correct ActionInvoker method was called. I did this by doing...

public class ContextControllerFactory : DefaultControllerFactory
{
    protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
    {
        IController controller = base.GetControllerInstance(requestContext, controllerType);
        Controller contextController = controller as Controller;

        if (contextController != null)
        {
            var context = new ControllerContext(requestContext, contextController);
            contextController.ActionInvoker = new ContextActionInvoker(context);
        }
        return controller;
    }
}

I then had to tell the MVC application which controllerfactory to use. I did this by changing Global.asax.cs a bit...

public class MvcApplication : System.Web.HttpApplication
{
    ...

    protected void Application_Start()
    {
        ...
        ControllerBuilder.Current.SetControllerFactory(typeof(ContextControllerFactory));
        ...
    }

    ...
}

Then on the masterpage I used the dropdownlist HTML helper method, by doing...

<%: Html.DropDownList(MVC_MasterPage_data_set_in_cache.Controllers.ContextActionInvoker.testMessageCacheAndViewDataKey) %>
simonwilbert
A: 

If you override the Initialize method in your base controller, you have access to the Cache. Initialize will execute before any action is executed on any request for an action in the controller.

protected override void Initialize(RequestContext requestContext)
{
    base.Initialize(requestContext);
    var list = requestContext.HttpContext.Cache[testMessageCacheAndViewDataKey];
    if (list == null) { ... }
}
bzlm