Simple way to implement caching in ASP.NET Web API
This article is not about caching the output of APIControllers.
Often in ASP.NET Web API you will have a need to cache something temporarily in memory probably to improve performance.
There are several nice libraries that allow you to do just that.
One very popular is CacheCow and here you will find a very nice article how to use this library.
But what if you want to do caching without third party library in a very simple way?
If you want to learn more about ASP.NET Web API I recommend a great book that I love written by real experts:
Designing Evolvable Web APIs with ASP.NET
Caching without third party library in a very simple way
I had a need for this. I implemented some basic authentication for the ASP.NET Web API with Tokens and wanted to cache Tokens temporarily in memory to avoid going into the database for every HTTP request.
The solution is very simple.
You just need to use Microsoft class for this called MemoryCache which resides in System.Runtime.Caching dll.
I created a simple helper class for caching with few lines of code:
public class MemoryCacher
{ public object GetValue(string key) { MemoryCache memoryCache = MemoryCache.Default; return memoryCache.Get(key); } public bool Add(string key, object value, DateTimeOffset absExpiration) { MemoryCache memoryCache = MemoryCache.Default; return memoryCache.Add(key, value, absExpiration); } public void Delete(string key) { MemoryCache memoryCache = MemoryCache.Default; if (memoryCache.Contains(key)) { memoryCache.Remove(key); } } }
When you want to cache something:
memCacher.Add(token, userId, DateTimeOffset.UtcNow.AddHours(1));
And to get something from the cache:
var result = memCacher.GetValue(token);
NOTE: You should be aware that MemoryCache will be disposed every time IIS do app pool recycle.
If your Web API :
- Does not receive any request for more that 20 minutes
- Or hit default IIS pool interval 1740 minutes
- Or you copy a new version of your ASP.NET Web API project into an IIS folder (this will auto-trigger app pool recycle)
So you should have a workaround for this. If you don’t get value from the cache you should grab it for example from a database and then store it again in the cache:
var result = memCacher.GetValue(token); if (result == null) { // for example get token from database and put grabbed token // again in memCacher Cache }
And if you liked this post, go ahead and subscribe here to my RSS Feed to make sure you don’t miss other content like this.
Bernd Viehmann
January 4, 2015 at 6:48 pm (10 years ago)Thank you very much. I found this article and will give your solution a try 🙂
Radenko Zec
January 4, 2015 at 9:52 pm (10 years ago)Thanks Bernd. I use it in production project so it should be fine.
Don’t forget to subscribe to my blog and make sure you don’t miss some great content I’m planning for this year.
pooranspress
March 12, 2015 at 3:52 pm (10 years ago)Why does the cache invalidate if it did not receive any request for more that 20 minutes. how can we extend it?
Radenko Zec
March 12, 2015 at 3:59 pm (10 years ago)This is IIS default setting for Application pool.
Check this link for resolution of your problem http://stackoverflow.com/questions/18937545/caching-application-data-in-memory-mvc-web-api
pooranspress
March 12, 2015 at 4:27 pm (10 years ago)Got it .. thanks 🙂
Vino Spartan
March 21, 2015 at 10:38 am (10 years ago)Hi Man,
I’ve implemented the same and it is doing its job great…but the prob is every time i’m gettin http header as 200 instead of 304 when response from cache..could u pl hlp me…..
Radenko Zec
March 21, 2015 at 12:08 pm (10 years ago)Hi Vino. That is easy. Just implement something similar to this :
var result = memCacher.GetValue(token);
if(result==null)
{
// we grab fresh value from database
return request.CreateResponse(HttpStatusCode.OK, result);
}
// we return result from cache and return status code 304 notModified
return request.CreateResponse(HttpStatusCode. NotModified, result);
Vino Spartan
March 24, 2015 at 5:11 pm (10 years ago)Hi,
Thanks for your response. As i said above, i’ve implemented your way for cachin in my API. I do confirm values retrieved from cache when same reqs handled(by debugging)
But i’ve come with few issues as listed,
1)It is not specified as Cached when i try in Fiddler/PostMan.(Image uploaded FYR)
2)As you advised above, i’ve tried to modify my return but i can’t. i snaped my method and uploaded FYR.
Could you please help me how can i implement and resolve above two issues.
(Probable it might be simple but i jst kicked myself in API…..:) )
Thanks,
Vino
siah
January 17, 2016 at 10:06 am (9 years ago)Hi, nice and clean and efficient way of caching.
Can you also please explain the memory constraints of caching in iis app pool. How to limit the caching size etc. This will help in deciding the right caching strategy. Particularly in hosting in azure.
Many thanks in advance
Sumanta
January 17, 2016 at 10:19 am (9 years ago)Hi, nice and clean and efficient way of caching.
Can you also please explain the memory constraints of caching in iis app pool. How to limit the caching size etc. This will help in deciding the right caching strategy. Particularly in hosting in azure.
Many thanks in advance
Nerdy Beast
February 1, 2016 at 4:26 pm (9 years ago)We use the built in web cache class in many of our web api projects. It’s about as simple as it gets, check it out here: https://msdn.microsoft.com/en-us/library/system.web.helpers.webcache(v=vs.111).aspx
Cristian Rengifo
January 23, 2017 at 11:11 pm (8 years ago)Thank you very much for your contribution, it has helped me a lot, I have managed to Optimize a process. Greetings from Colombia.
Radenko Zec
January 24, 2017 at 7:11 am (8 years ago)Thanks Christian.
Armin
October 27, 2018 at 9:57 pm (6 years ago)Thanks for the tip Radenko, useful indeed. I first wanted to integrate Redis but it would be an overkill for my project at least for now. Then I started using the Strathweb library which offers caching output responses via attributes. Works fine but it doesn’t satisfy my requirements. For example, certain endpoints in my API don’t have a query string param and vary the response based on the current user. Since there’s no way to vary cache entries in these situations I needed another mechanism to have full control over my InMemory cache. So I developed a little solution using your example. Works fine but there’s something I thought I might share with you guys. Assume you have an endpoint that returns a list of projects, and this endpoint filters the result based on the current user’s organization ID. The endpoint doesn’t have a query string param, and extracts this ID from the currently logged-in user. Let’s say you sign in with a user and request this resource, you get your data and you’re done. The response is cached for say 10min. Now suppose you sign in with another user and request the same resource. If your business logic filters the response data based on the current user, and not through query string params, the second user will receive invalid data, because it will be the cached data from the first user. Even though the cache keys will be different, the second user receives invalid data because the cached content is served from disk cache. This could be a catastrophic security bug. To make sure you always serve the right data, you need to set a few HTTP headers. CacheControl and MaxAge of course, but most importantly the NoStore header. Otherwise, the browser will serve data from the disk cache. Cheers!
hanim
January 31, 2019 at 6:42 am (6 years ago)Hi, I have two api running in the same application pool. One Api cant see the items added by another. I was under impression that since both uses same app pool, they should be sharing same memory cache. Please let know if im missing anything.
Matt Drouillard
October 4, 2019 at 8:34 pm (5 years ago)Great solution thanks, I love the auto timeout capability. Beats storing crap in some sort of static variable or implementing some roundabout timer to expire old objects.
Matt Drouillard
October 4, 2019 at 8:53 pm (5 years ago)Likely not application pool wide, but application instance wide. Since you have two wep apps running under the same app pool, each would have it’s own in memory instance of the MemoryCache.
Try persisting your data via a database, or elsewhere if you need to access it between the two applications.
Happy Singh
January 17, 2020 at 9:23 am (5 years ago)When caching any data. It should return same object until it clear or even if there are change in actual data in database.
For example.. there are 1 form which load multiple master data’s in drop down list. So we cache it using above techniques but what if data changed in database via admin. It will still return cache data or automatically pull updated data from database?
radenko
January 17, 2020 at 9:32 am (5 years ago)Hi there. On every change of data in database you should clear cache manually. What it means is in your method where you update that row in database after update you need to clear cache. You cannot allow anybody change these records directly in database from DbAdmin panels etc.
Happy Singh
January 17, 2020 at 10:24 am (5 years ago)Thanks For your reply.
If we want to cache web api response using above techniques is possible and how can we call the same response if no data change. Please guide me it will help me to do.
Alok
April 29, 2020 at 9:25 pm (5 years ago)I tried using this solution in ASP.NET standard Web API project. However, the cache does not persist between two individual requests. What gives?