Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Where should caching occur in an ASP.NET MVC application?

I'm needing to cache some data using System.Web.Caching.Cache. Not sure if it matters, but the data does not come from a database, but a plethora of custom objects.

The ASP.NET MVC is fairly new to me and I'm wondering where it makes sense for this caching to occur?

Model or Controller?

At some level this makes sense to cache at the Model level but I don't necessarily know the implications of doing this (if any). If caching were to be done at the Controller level, will that affect all requests, or just for the current HttpContext?

So... where should application data caching be done, and what's a good way of actually doing it?

Update

Thanks for the great answers! I'm still trying to gather where it makes most sense to cache given different scenarios. If one is caching the entire page, then keeping it in the view makes sense but where to draw the line when it's not the entire page?

like image 735
Scott Saad Avatar asked Oct 24 '08 20:10

Scott Saad


4 Answers

I think it ultimately depends on what you are caching. If you want to cache the result of rendered pages, that is tightly coupled to the Http nature of the request, and would suggest a ActionFilter level caching mechanism.

If, on the other hand, you want to cache the data that drives the pages themselves, then you should consider model level caching. In this case, the controller doesn't care when the data was generated, it just performs the logic operations on the data and prepares it for viewing. Another argument for model level caching is if you have other dependencies on the model data that are not attached to your Http context.

For example, I have a web-app were most of my Model is abstracted into a completely different project. This is because there will be a second web-app that uses this same backing, AND there's a chance we might have a non-web based app using the same data as well. Much of my data comes from web-services, which can be performance killers, so I have model level caching that the controllers and views know absolutely nothing about.

like image 187
Matt Avatar answered Oct 09 '22 00:10

Matt


I don't know the anwser to your question, but Jeff Atwood talks about how the SO team did caching using the MVC framework for stackoverflow.com on a recent hanselminutes show that might help you out:

http://www.hanselminutes.com/default.aspx?showID=152

like image 43
Ely Avatar answered Oct 09 '22 00:10

Ely


Quick Answer

I would start with CONTROLLER caching, use the OutputCache attribute, and later add Model caching if required. It's quicker to implement and has instant results.

Detail Answer (cause i like the sound of my voice)

Here's an example.

[OutputCache(Duration=60, VaryByParam="None")]
public ActionResult CacheDemo() {
  return View();
}

This means that if a user hits the site (for the cache requirements defined in the attribute), there's less work to get done. If there's only Model caching, then even though the logic (and most likely the DB hit) are cached, the web server still has to render the page. Why do that when the render result will always be the same?

So start with OutputCaching, then move onto Model caching as you performance test your site.

Output caching is also a lot simpler to start out with. You don't have to worry about web farm distributed caching probs (if you are part of a farm) and the caching provider for the model.

Advanced Caching Techniques

You can also apply donut caching -> cache only part of the UI page :) Check it out!

like image 3
Pure.Krome Avatar answered Oct 09 '22 00:10

Pure.Krome


I would choose caching at the model level. (In general, the advice seems to be to minimize business logic at the controller level and move as much as possible into model classes.)

How about doing it like this:

I have some entries in the model represented by the class Entry and a source of entries (from a database, or 'a plethora of custom objects'). In the model I make an interface for retrieving entries:

public interface IEntryHandler
{
    IEnumerable<Entry> GetEntries();
}

In the model I have an actual implementation of IEntryHandler where the entries are read from cache and written to cache.

public class EntryHandler : IEntryHandler
{
    public IEnumerable<Entry> GetEntries()
    {
        // Check if the objects are in the cache:
        List<Entry> entries = [Get entries from cache]
        if (entries == null)
        {
            // There were no entries in the cache, so we read them from the source:
            entries = [Get entries from database or 'plethora of custom objects']
            [Save the retrieved entries to cache for later use]
        }
        return entries;
    }
}

The controller would then call the IEntryHandler:

public class HomeController : Controller
{
    private IEntryHandler _entryHandler;

    // The default constructor, using cache and database/custom objects
    public HomeController()
        : this(new EntryHandler())
    {
    }

    // This constructor allows us to unit test the controller 
    // by writing a test class that implements IEntryHandler
    // but does not affect cache or entries in the database/custom objects
    public HomeController(IEntryHandler entryHandler)
    {
        _entryHandler = entryHandler;
    }

    // This controller action returns a list of entries to the view:
    public ActionResult Index()
    {
        return View(_entryHandler.GetEntries());
    }
}

This way it is possible to unit test the controller without touching real cache/database/custom objects.

like image 2
Ole Lynge Avatar answered Oct 09 '22 00:10

Ole Lynge