Caching Best Practices
Here's the offending code from the book, presented on page 504 / 505:
VB.NET If Cache("MyReview" + reviewId.ToString()) Is Nothing Then Using db As New PlanetWroxDataContext() myReview = (From r In db.Reviews _ Where r.Id = reviewId _ Select r).Single() End Using Cache("MyReview" + reviewId.ToString()) = myReview End If myReview = CType(Cache("MyReview" + reviewId.ToString()), Review) C# if (Cache["MyReview" + reviewId.ToString()] == null) { using (PlanetWroxDataContext db = new PlanetWroxDataContext()) { myReview = (from r in db.Reviews where r.Id == reviewId select r).Single(); } Cache["MyReview" + reviewId.ToString()] = myReview; } myReview = (Review) Cache["MyReview" + reviewId.ToString()];
At first, this code may look fine to you. It checks for the existence of an object in the cache. If it's not there, it's created using a LINQ query and inserted in the cache. So, on the last line that retrieves the item from the cache and casts it to a Review, the object must always be there. Or so you think.....
What could happen is that in between the If call at the top of each code block, and the last line of code that retrieves the item, the cached item could actually be removed from the cache. So, while the first If check may tell you the item is there, the cast at the end may actually fail if the item has been removed in the mean time.
The fix is easy though; store the item in a variable and use that instead to do your checking and casting.
VB.NET Dim cacheItem As Object = TryCast(Cache("MyReview" + reviewId.ToString()), Review) If cacheItem Is Nothing Then Using db As New PlanetWroxDataContext() cacheItem = (From r In db.Reviews _ Where r.Id = reviewId _ Select r).Single() Cache("MyReview" + reviewId.ToString()) = cacheItem End Using End If myReview = CType(cacheItem, Review) C# object cacheItem = Cache["MyReview" + reviewId.ToString()] as Review; if (cacheItem == null) { using (PlanetWroxDataContext db = new PlanetWroxDataContext()) { cacheItem = (from r in db.Reviews where r.Id == reviewId select r).Single(); Cache["MyReview" + reviewId.ToString()] = cacheItem; } } myReview = (Review) cacheItem;
This code tries to get the cached item in a local variable called cacheItem. The as keyword in C# and the TryCast method in VB try to cast the requested item to a Review. If it succeeds, a valid instance of a Review is stored in cacheItem. However, if the code doesn't succeed, because the item in the cache doesn't exist or is not a review, then both as in C# and TryCast in VB.NET result in cacheItem getting a value of null / Nothing.
The remainder of the code is similar; LINQ is used to get the requested Review which is then assigned to the cacheItem variable and stored in the cache for later use. At the end, the myReview variable gets a value by casting the cacheItem instance to a Review.
It's an issue I was aware of before writing the book, so it never should have made it in the book in the first place. I'll be sure to correct it in case there's a reprint.
References
Where to Next?
Wonder where to go next? You can read existing comments below or you can post a comment yourself on this article.
Links in this Document
Doc ID | 463 |
Full URL | https://imar.spaanjaars.com/463/caching-best-practices |
Short cut | https://imar.spaanjaars.com/463/ |
Written by | Imar Spaanjaars |
Date Posted | 07/14/2008 15:40 |
Talk Back! Comment on Imar.Spaanjaars.Com
I am interested in what you have to say about this article. Feel free to post any comments, remarks or questions you may have about this article. The Talk Back feature is not meant for technical questions that are not directly related to this article. So, a post like "Hey, can you tell me how I can upload files to a MySQL database in PHP?" is likely to be removed. Also spam and unrealistic job offers will be deleted immediately.
When you post a comment, you have to provide your name and the comment. Your e-mail address is optional and you only need to provide it if you want me to contact you. It will not be displayed along with your comment. I got sick and tired of the comment spam I was receiving, so I have protected this page with a simple calculation exercise. This means that if you want to leave a comment, you'll need to complete the calculation before you hit the Post Comment button.
If you want to object to a comment made by another visitor, be sure to contact me and I'll look into it ASAP. Don't forget to mention the page link, or the Doc ID of the document.