views:

3897

answers:

5

I have read lots of information about page caching and partial page caching in a MVC application. However, I would like to know how you would cache data.

In my scenario I will be using LINQ to Entities (entity framework). On the first call to GetNames (or whatever the method is) I want to grab the data from the database. I want to save the results in cache and on the second call to use the cached version if it exists.

Can anyone show an example of how this would work, where this should be implemented (model?) and if it would work.

I have seen this done in traditional ASP.NET apps , typically for very static data.

+9  A: 

Reference the System.Web dll in your model and use System.Web.Caching.Cache

public string[] GetNames()
{
  if(Cache["names"] == null)
  {
    Cache["names"] = DB.GetNames();
  }
  return (string[])Cache["names"];
}

A bit simplified but I guess that would work. This is not MVC specific and I have always used this method for caching data.

TT
I don't recommend this solution: in the return, you might get a null object again, because it's re-reading in the cache and it might have been dropped from the cache already. I'd rather do:public string[] GetNames(){ string[] noms = Cache["names"]; if(noms == null) { noms = DB.GetNames(); Cache["names"] = noms; } return (noms);}
Oli
I agree with Oli.. getting the results from the actual call to the DB is better than getting them from the cache
CodeClimber
Does this work with the `DB.GetNames().AsQueryable` method of delaying the query?
rockinthesixstring
Not unless you change return value from string[] to IEnumerable<string>
TT
+32  A: 

Here's nice Cache service I use:

public class InMemoryCache: ICacheService
{
public T Get<T>(string cacheID, Func<T> getItemCallback) where T : class
{
    T item = HttpRuntime.Cache.Get(cacheID) as T;
    if (item == null)
    {
        item = getItemCallback();
        HttpContext.Current.Cache.Insert(cacheID,item);
    }
    return item;
}
}

ICacheService has just one Get() method defined.

You use it like this:
cacheProvider.Get("cache id", (delegate method if cache is empty));
So, Cache provider will check if there's anything by "cache id" in cache, and if there's not, it will call delegate method to fetch data and store it in cache.

Realworld example:

var products=cacheService.Get("catalog.products", ()=>productRepository.GetAll())
Hrvoje
Is there a way to pass parameters to the callback?
Todd Smith
What CacheService are you referring to? Can you please provide a more complete example.
Coolcoder
Coolcoder: there's not much more too it, that's almost all the code.
Anthony
Here I use system.web.caching, but caching object can be injected in constructor with ioc/di, you then you could have disk caching, probably velocity/memcache, etc.
Hrvoje
I've adapted this so that the caching mechanism is used per user session by using HttpContext.Current.Session instead. I've also put a Cache property on my BaseController class so its easy access and updated the constructor allow for DI for unit testing. Hope this helps.
WestDiscGolf
@Todd Smith: Change the signature of the Get method to receive a Func<T, ParamType> instead.
murki
You can also make this class and method static for reusability among other controllers.
Alex
This class shouldn't depend on HttpContext. I simplified it just for example purpose here. Cache object must be inserted through constructor - it can be replaced then with other caching mechanisms . All this is achieved with IoC/DI, along with static (singleton) life cycle.
Hrvoje
A: 

You can also try and use the caching built into ASP MVC:

Add the following attribute to the controller method you'd like to cache:

[OutputCache(Duration=10)]

In this case the ActionResult of this will be cached for 10 seconds.

More on this here

qui
OutputCache is for the rendering of Action , the question was in relation to caching data not the page.
Coolcoder
+4  A: 

I'm referring to TT's post and suggest the following approach:

Reference the System.Web dll in your model and use System.Web.Caching.Cache

public string[] GetNames()
{ 
    var noms = Cache["names"];
    if(noms == null) 
    {    
        noms = DB.GetNames();
        Cache["names"] = noms; 
    }

    return ((string[])noms);
}

You should not return a value read from the cache, since you'll never know if at that specific moment it is still in the cache. Even if you inserted it in the statement before, it might already be gone or has never been addred to the cache - you just don't know.

Oli
But doesn't the line `Cache["names"] = noms;` put in the cache?
Baddie
A: 

I have tried this code block in my model after adding System.Web.Caching

var noms = Cache["names"];
    if(noms == null) 
    {    
        noms = DB.GetNames();
        Cache["names"] = noms; 
    }

And on compilation getting error 'System.Web.Caching.Cache' is a 'type' but is used like a 'variable'