Detect if Web API request is served by cache - asp.net

We have a Web API 2 application that is using Strathweb.CacheOutput.WebApi2 for caching.
Short question: Is there a way to detect in Application_LogRequest (or anywhere, really) whether a request was served by cache?
Long question:
Lately, we've been looking at performance and seeing which API calls we can improve. I pulled a list from our logs (which includes duration) to see what the worst offenders are. By worst offenders, I mean the longest average durations and/or the most number of calls.
But the numbers are misleading because the cached requests are included in the stats. Cached requests are usually served in half a second or less, so they pull average durations down. Also, if a particular call is being made, say 1000 times a minute, but 999 of those are cached, I don't really care.
So I'd like to add a flag in my log that indicates whether the request was served by the cache, so I can exclude those. All our logging is done in the Application_LogRequest event. But even if I can detect it somewhere else, I can store a value in HttpContext.Current.Items that I can retrieve later.

Strathweb CacheOutputAttribute does not add some reliable information in http responses or elsewhere for allowing to know whether the response was served from cache or not.
You may derive from it for doing that, then replace all your usage of CacheOutputAttribute by your own.
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class YourCustomizedCacheOutputAttribute : CacheOutputAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
base.OnActionExecuting(actionContext);
// If response is set, it has been retrieved from cache.
actionContext.Request.Properties["yourCacheHitKey"] =
actionContext.Response != null;
}
}
Of course, use something else than HttpRequestMessage.Properties if where you log you do not have access to it.
You may by example add some custom header in response, if leaking that out to browsers is not an issue for you:
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class YourCustomizedCacheOutputAttribute : CacheOutputAttribute
{
// "X-" prefix is deprecated, but the rational behind this is about wannabe
// standard header. I do not intend this one to become standard for anything.
private const string _cacheHeader = "X-Cache";
protected override void ApplyCacheHeaders(HttpResponseMessage response,
CacheTime cacheTime)
{
base.ApplyCacheHeaders(response, cacheTime);
if (response.Headers.Contains(_cacheHeader))
return;
// At this point, we do not know. miss by default.
response.Headers.Add(_cacheHeader, "miss");
}
public override void OnActionExecuting(HttpActionContext actionContext)
{
base.OnActionExecuting(actionContext);
if (actionContext.Response == null)
return;
// Response has been retrieved from cache.
// Headers.Remove does not fail if not already there.
response.Headers.Remove(_cacheHeader);
actionContext.Response.Headers.Add(_cacheHeader, "hit");
}
}

Related

Is it thread-safe to write this?

Can I write something like the following (in an assembly being used in an ASP.NET web page)?
public static string CurrentAuthenticatedUserFromHttpRequest
{
get
{
if (HttpContext.Current.Items["AuthUser"] == null)
{
return string.Empty;
}
return HttpContext.Current.Items["AuthUser"].ToString(); //set in "TryAuthenticate"
}
}
It is going to be a static read-only property. The value (to HttpContext.Current.Items["AuthUser"]) is set through a httphandler.
Just wondering on how this would perform during multiple requests. Is the data going to be accurate when multiple users try to access the same property (in multiple requests), even when high volumes of requests come in?
Yes, this is threadsafe. The static HttpContext.Current property differs per thread and contains the context for the request that the thread is currently handling.

Client side caching using Last-Modified header and OutputCacheAttribute in asp.net mvc 3

Edited
I want to cache images on the client and know that there are different ways to do it in mvc 3: (correct me if I'm wrong)
1) You can use OutputCacheAttribute which works with the help of Expires http header. But it will return 304 Not Modified unless the time expire (even if the image was changed).
2) To avoid displaing stale images You can use Last-Modified http header (with OutputCacheAttribute). In this case the browser sends the request to the server with If-Modified-Since http header. On the server You verify whether the object is still valid or not and if it is You just return Last-Modified http header (and the browser takes image from the local cache); if the object was modified You return it with 200 OK status.
So, the browser needs to send the request to the server each time before taking image from it's own cache. Here is the example -
3) There is another way (as I was told the right way in my case, cause the images will change very rarely... anyway, I need to implement exactly this): To add modified date to the image url and set caching with Expires for the eternity (1 year or more). If the image have changed You should send new url with new version.
Here is the code:
public class LastModifiedCacheAttribute : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
if (filterContext.Result is FilePathResult)
{
var result = (FilePathResult)filterContext.Result;
var lastModify = File.GetLastWriteTime(result.FileName);
if (!HasModification(filterContext.RequestContext, lastModify))
filterContext.Result = NotModified(filterContext.RequestContext, lastModify);
SetLastModifiedDate(filterContext.RequestContext, lastModify);
}
base.OnActionExecuted(filterContext);
}
private static void SetLastModifiedDate(RequestContext requestContext, DateTime modificationDate)
{
requestContext.HttpContext.Response.Cache.SetLastModified(modificationDate);
}
private static bool HasModification(RequestContext context, DateTime modificationDate)
{
var headerValue = context.HttpContext.Request.Headers["If-Modified-Since"];
if (headerValue == null)
return true;
var modifiedSince = DateTime.Parse(headerValue).ToLocalTime();
return modifiedSince < modificationDate;
}
private static ActionResult NotModified(RequestContext response, DateTime lastModificationDate)
{
response.HttpContext.Response.Cache.SetLastModified(lastModificationDate);
return new HttpStatusCodeResult(304, "Page has not been modified");
}
}
And I registered the LastModifiedCacheAttribute in Global.asax and applied the following OutputCacheAttribute to my action method.
[HttpGet, OutputCache(Duration = 3600, Location = OutputCacheLocation.Client, VaryByParam = "productId")]
public FilePathResult GetImage(int productId)
{ // some code }
If I use the code above seems like the browser doesn't send requests to the server, instead it just take images from the cache unless the duration is not ended. (When I change the image the browser doesn't display new version)
Questions:
1) How to implement the third approach, so that the browser will take the images from client cache (and will not send the response to the server each time it wants the image) unless the image was modified?
edited: the actual code will be appreciated.
2) In the code above the time of the first image request is written to the Last-Modified (don't know why). How to write the modification date of the file into Last-Modified?
edited: this question relates to second approach. Also, if I cache only on the client and use Last-Modified implementation I get 304 Not Modified status only if I press F5. If I reenter the same url I will get 200 OK. If I cache on a client without using Last-Modified it will always return 200 OK no matter what. How could this be explained?
You could look into using ETags (http://en.wikipedia.org/wiki/HTTP_ETag), that's the first thing I thought of reading your question.
You could also have a look here: Set ETag for FileResult - MVC 3
If I understood correctly, what you are after, is having infinite caching, and rely on invalidating the cache by changing the actual url of the resource.
In that case, I believe the actual implementation is much simpler and doesn't require manual handling of headers.
Ultimately, the point is to be able to have the images loaded by a URL such as the following:
http://someDomain/product/image/1?v={something}
With "1" being the productId, and specifying some sort of version identifier ("v").
The key is to build that url, where the value of v is dependent on the last modification of the image (which you should probably store along with the image, or the product). You can probably hash the modification date, and use that.
If the next time you build the url, the date of the last modification is the same, you will get the same hash value, and therefore render the same url as before, and the browser will load that from cache without requesting anything from the server.
As soon as the image is updated, and that modification date changes, your code will generate a different url, which will force the browser to request it again.
Then you just apply the OutputCache attribute to the Action, configured to cache on the client (no need to specify VaryByParam) and you should be set.
You can use the VaryByCustom option with Output Caching to achieve this without using a custom attribute. Change your method code like this:
[HttpGet, OutputCache(Duration = 3600,
Location = OutputCacheLocation.Client,
VaryByCustom = "imagedate")]
public FilePathResult GetImage(int productId)
{ // some code }
Then add the following code to your Global.asax:
public override string GetVaryByCustomString(System.Web.HttpContext context, string custom)
{
if (custom.ToLower() == "imagedate")
{
return System.IO.File.GetLastWriteTime(Server.MapPath("~/Images/my-image.png")).ToString();
}
else
{
return base.GetVaryByCustomString(context, custom);
}
}
When the timestamp of the image file changes, the return value of the GetVaryByCustomString method will change which will cause ASP.NET to reload the image rather than use the cached value.
See http://msdn.microsoft.com/en-us/library/aa478965.aspx for further details.
Intially used this answer but for some reason when image is modified it doesn't update in client, instead shows the cached version of image.
So the solution is using versioning which is your 3'rd option, just add LastUpdated datetime field from your product image database
Action Method
[HttpGet]
[OutputCache(
Duration = 7200,
VaryByParam = "productId;lastUpdated",
Location = OutputCacheLocation.Client)]
public ActionResult GetImage(string productId, string lastUpdated)
{
var dir = Server.MapPath("~/productimages/");
var path = Path.Combine(dir, productId + ".jpg");
return base.File(path, "image/jpeg");
}
In View
<img src="#Url.Action("GetImage", "Home", new { productId = "test-product-100",
lastUpdated =Model.LastUpdated })" />
Idea taken from this post.
Answer is late but Hope helps someone.

Last-Modified Header in MVC

I have recently come across the Last-Modified Header.
How and where can I include it in MVC?
What are the advantages of including it?
I want an example how last modified header can be included in an mvc project, for static pages and database queries as well?
Is it different from outputcache, if yes how?
Basically, I want the browser to clear the cache and display the latest data or pages automatically, without the need for the user to do a refresh or clearing the cache.
The Last-Modified is mainly used for caching. It's sent back for resources for which you can track the modification time. The resources doesn't have to be files but anything. for instance pages which are generated from dB information where you have a UpdatedAt column.
Its used in combination with the If-Modified-Since header which each browser sends in the Request (if it has received a Last-Modified header previously).
How and where can I include it in MVC?
Response.AddHeader
What are the advantages of including it?
Enable fine-grained caching for pages which are dynamically generated (for instance you can use your DB field UpdatedAt as the last modified header).
Example
To make everything work you have to do something like this:
public class YourController : Controller
{
public ActionResult MyPage(string id)
{
var entity = _db.Get(id);
var headerValue = Request.Headers["If-Modified-Since"];
if (headerValue != null)
{
var modifiedSince = DateTime.Parse(headerValue).ToLocalTime();
if (modifiedSince >= entity.UpdatedAt)
{
return new HttpStatusCodeResult(304, "Page has not been modified");
}
}
// page has been changed.
// generate a view ...
// .. and set last modified in the date format specified in the HTTP rfc.
Response.AddHeader("Last-Modified", entity.UpdatedAt.ToUniversalTime().ToString("R"));
}
}
You might have to specify a format in the DateTime.Parse.
References:
HTTP status codes
HTTP headers
Disclamer: I do not know if ASP.NET/MVC3 supports that you manage Last-Modified by yourself.
Update
You could create an extension method:
public static class CacheExtensions
{
public static bool IsModified(this Controller controller, DateTime updatedAt)
{
var headerValue = controller.Request.Headers['If-Modified-Since'];
if (headerValue != null)
{
var modifiedSince = DateTime.Parse(headerValue).ToLocalTime();
if (modifiedSince >= updatedAt)
{
return false;
}
}
return true;
}
public static ActionResult NotModified(this Controller controller)
{
return new HttpStatusCodeResult(304, "Page has not been modified");
}
}
And then use them like this:
public class YourController : Controller
{
public ActionResult MyPage(string id)
{
var entity = _db.Get(id);
if (!this.IsModified(entity.UpdatedAt))
return this.NotModified();
// page has been changed.
// generate a view ...
// .. and set last modified in the date format specified in the HTTP rfc.
Response.AddHeader("Last-Modified", entity.UpdatedAt.ToUniversalTime().ToString("R"));
}
}
UPDATE: Check my new answer
How and where can I include it in MVC?
The built-in OutputCache filter does the job for you and it uses those headers for caching.
The OuputCache filter uses the Last-Modified header when you set the Location as Client or ServerAndClient.
[OutputCache(Duration = 60, Location = "Client")]
public ViewResult PleaseCacheMe()
{
return View();
}
What are the advantages of including it?
Leveraging client-side caching with conditional cache flush
I want an example how last modified header can be included in an mvc project, for static pages and database queries as well?
This link contains enough information to try out a sample. For static pages like html, images IIS will take care of setting/checking the Last-Modified header and it uses the file's last modified date. For database queries you can go for setting the SqlDependency in the OutputCache.
Is it different for outputcache, if yes how? When do I need to include Last-Modified Header and when to use outputcache?
OutputCache is an action filter used for implementing caching mechanism in ASP.NET MVC. There are different ways you could perform caching using OutputCache: client-side caching, server-side caching. Last-Modified header is one way to accomplish caching in the client-side. OutputCache filter uses it when you set the Location as Client.
If you go for client-side caching (Last-Modified or ETag) the browser cache will automatically get updated in subsequent request and you don't need to do F5.
Last-Modified vs. OutputCache
The OutputCache attribute controls output caching on your IIS WebServer. This is a vendor specific server feature (see Configure IIS 7 Output Caching).
I also suggest to read Cache Exploration in ASP.NET MVC3 if you are interested in the powerful capabilities of this technology.
Last-Modified response header and it's counterpart If-Modified-Since request header are representatives of the validation cache concept (section cache control).
These headers are part of the HTTP protocol and specified in rfc4229
OutputCache and validation are not exclusive, you can combine it.
What caching scenario makes me happy?
As usual: it depends.
Configuring a 5 second OutputCache on a 100 hits/second page would drastically reduce the load. Using OutputCache, 499 out of 500 hits can be served from cache (and do not cost db roundtrip, calculations, rendering).
When I have to serve rarely changes immediately, then the validation scenario could save a lot of bandwith. Specially when you serve large content compared to a lean 304 status message. However, changes are adopted immediately since every request validates for changes in the source.
Last-Modified attribute implementation sample
Based on my experience I would recommend to implement the validation scenario (last modified) as an action filter attribute.
(Btw: Here's an other caching scenario implemented as an attribute)
Static content from file
[LastModifiedCache]
public ActionResult Static()
{
return File("c:\data\static.html", "text/html");
}
Dynamic content sample
[LastModifiedCache]
public ActionResult Dynamic(int dynamicId)
{
// get data from your backend (db, cache ...)
var model = new DynamicModel{
Id = dynamivId,
LastModifiedDate = DateTime.Today
};
return View(model);
}
public interface ILastModifiedDate
{
DateTime LastModifiedDate { get; }
}
public class DynamicModel : ILastModifiedDate
{
public DateTime LastModifiedDate { get; set; }
}
The LastModifiedCache attribute
public class LastModifiedCacheAttribute : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
if (filterContext.Result is FilePathResult)
{
// static content is served from file in my example
// the last file write time is taken as modification date
var result = (FilePathResult) filterContext.Result;
DateTime lastModify = new FileInfo(result.FileName).LastWriteTime;
if (!HasModification(filterContext.RequestContext, lastModify))
filterContext.Result = NotModified(filterContext.RequestContext, lastModify);
SetLastModifiedDate(filterContext.RequestContext, lastModify);
}
if (filterContext.Controller.ViewData.Model is HomeController.ILastModifiedDate)
{
// dynamic content assumes the ILastModifiedDate interface to be implemented in the model
var modifyInterface = (HomeController.ILastModifiedDate)filterContext.Controller.ViewData.Model;
DateTime lastModify = modifyInterface.LastModifiedDate;
if (!HasModification(filterContext.RequestContext, lastModify))
filterContext.Result = NotModified(filterContext.RequestContext, lastModify);
filterContext.RequestContext.HttpContext.Response.Cache.SetLastModified(lastModify);
}
base.OnActionExecuted(filterContext);
}
private static void SetLastModifiedDate(RequestContext requestContext, DateTime modificationDate)
{
requestContext.HttpContext.Response.Cache.SetLastModified(modificationDate);
}
private static bool HasModification(RequestContext context, DateTime modificationDate)
{
var headerValue = context.HttpContext.Request.Headers["If-Modified-Since"];
if (headerValue == null)
return true;
var modifiedSince = DateTime.Parse(headerValue).ToLocalTime();
return modifiedSince < modificationDate;
}
private static ActionResult NotModified(RequestContext response, DateTime lastModificationDate)
{
response.HttpContext.Response.Cache.SetLastModified(lastModificationDate);
return new HttpStatusCodeResult(304, "Page has not been modified");
}
}
How to enable global LastModified suppport
You can add the LastModifiedCache attribute to the RegisterGlobalFilters section of your global.asax.cs to globally enable this type of caching in your mvc project.
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
...
filters.Add(new LastModifiedCacheAttribute());
...
}
Note that outputcache isn't your only option here, and in fact you may well not want to handle last-modified in the way it does. To clarify a few options:
Option 1 - use [OutputCache]
In this case the framework will cache the response body according to whatever duration you specify. It will serve it with Last-Modified set to the current time, and max-age set to the time remaining until the original cache duration expires. If a client sends a request with If-Modified-Since then the framework will correctly return a 304. Once the cached response expires then the Last-Modified date will be updated each time a new response is cached.
Pros: caching happens at the controller level (so can work for partial content or the same cached content on different end URLs). You have better control over cacheability - e.g. HttpCacheability.ServerAndPrivate allows your server to cache the content, but not intermediate proxies.
Cons: you have no control over last-modified. When your cache expires all clients will need to re-download the content even if it hasn't actually changed
Option 2 - specify settings on Response.Cache
asp.net has another layer of caching outside of the outputcacheattribute in the form of System.Web.OutputCacheModule which all requests pass through. This behaves like an HTTP cache immediately in front of your application. So if you set sensible cache headers without applying the OutputCacheAttribute then your response will be cached here instead. For example:
Response.Cache.SetLastModified(lastModifiedDate);
Response.Cache.SetCacheability(HttpCacheability.Public);
Response.Cache.SetExpires(DateTime.Now + timespan);
Based on the above then the outputcachemodule would cache your content and any requests to the same URL would be served from the cache. Requests with If-Modified-Since would get 304s. (You could use ETags equally). When your cache expires the next request will hit your app normally, but if you know the content hasn't changed you can return the same Last-Modified or ETag as you did previously. Once this next response has been cached then subsequent clients will then be able to extend their cache lifetimes without re-downloading the content
Pros: If you have a meaningful way of determining last-modified or ETag then you have full control over it and can reduce the number of duplicate downloads.
Cons: Caching is only at the request/URL level. Only works if you're happy to set cache-control: public
Although this option reduces the likelihood of unnecessary content downloads, it doesn't eliminate it - the first request after the (server) cache expires will be served normally and result in a 200, even if a 304 would have been appropriate. That's probably for the best though, since it enables the cache to grab a fresh copy of the response body which it would have discarded when it expired previously, and hence future requests can be served straight from cache. I believe that HTTP caches can in theory be cleverer than this and use 304s to extend their own cache lifetime, but the asp.net one appears not to support that.
(Edited to replace SetMaxAge with SetExpires in the code above - seems IIS/asp.net won't respect max-age headers unless you also SetSlidingExpiration(true) but that setting appears to prevent the caching we want)
This is my second answer after doing some quite research on caching and OutputCache.
Let me answer your second question first.
What are the advantages of including it?
Browser caches the responses returned from the server. The caching is controlled by mainly three headers: Cache-Control, Last-Modified and Expires (there are others like ETag also comes to play).
The Last-Modified header tells the browser when does the resource has been modified at last. The resource could be either static file or dynamically created view. Whenever the browser makes the request for that resource it checks with the server "Hey, I already have a response for this request and it's Last-Modified date is so and so.. see the user is already tired... if you return a 304 I'm glad to use the response from my cache else please send your new response quick". (Note that the browser passes the Last-Modified value returned previously by the server in a new header called If-Modified-Since)
Ideally the server should read the value from the If-Modified-Since header and has to check with the current modified date and if they are same then it should return 304 (NOT MODIFIED) or it should return the new copy of the resource again passing the current modified date in the Last-Modified header.
The advantage is browser caching. By leveraging the browser caching the server can avoid creating a duplicate response and also it can return a new response if the cached response in the browser looks like old. The ultimate goal is save the time.
How and where can I include it in MVC?
In the case of static resources like images, html files and others you don't need to worry about setting How and Where because IIS takes care of that job. IIS uses the file's last modified date as the Last-Modified header value.
In the case of dynamic pages like a html content returned through an MVC action, how you can determine the Last-Modified header value? The dynamic driven pages are mostly data driven and it's our responsibility to decide whether the response returned previously is stale or not.
Let's say you have a blog and you have a page whether you display the details of an article (not any other details) then the page's version is decided by the last modified date or created date (if the article is not modified yet) of the article. So you have to do the same work answered by #jgauffin in the corresponding action that delivers the view.
You have asked in the comment Should I include it per action in the controller?
If you could able to abstract away the logic of reading the last modified date from database from the actions then you could accomplish the job through an action filter avoiding duplicating the code throughout actions. The question is how you are going to abstract the details away from the actions? Like passing the table/column names to the attribute? You have to figure it out!
As an example..
[LastModifiedCacheFilter(Table = "tblArticles", Column = "last_modified")]
public ViewResult Post(int postId)
{
var post = ... get the post from database using the postId
return View(post);
}
The pseudo code (means I haven't tested this :) of the LastModifiedCacheFilterAttribute implementation shown below uses Table/Column to read the last modified date but it could be some other ways as well. The idea is in the OnActionExecuting method we are doing the check and returning a 304 (if the cache is still fresh) and in the OnResultExecuted method we are reading/setting the latest modified date.
public class LastModifiedCacheFilterAttribute : ActionFilterAttribute
{
// Could be some other things instead of Table/Column
public string Table { get; set; }
public string Column { get; set; }
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
// var lastModified = read the value from the passed Column/Table and set it here
var ifModifiedSinceHeader = filterContext.RequestContext.HttpContext.Request.Headers["If-Modified-Since"];
if (!String.IsNullOrEmpty(ifModifiedSinceHeader))
{
var modifiedSince = DateTime.Parse(ifModifiedSinceHeader).ToLocalTime();
if (modifiedSince >= lastModified)
{
filterContext.Result = new EmptyResult();
filterContext.RequestContext.HttpContext.Response.Cache.SetLastModified(lastModified.ToUniversalTime());
filterContext.RequestContext.HttpContext.Response.StatusCode = 304;
}
}
base.OnActionExecuting(filterContext);
}
public override void OnResultExecuted(ResultExecutedContext filterContext)
{
// var lastModified = read the value from the passed Column/Table and set it herefilterContext.RequestContext.HttpContext.Response.Cache.SetLastModified(lastModified.ToUniversalTime());
base.OnResultExecuted(filterContext);
}
}
Why can't OutputCache attribute?
As per my analysis, OutputCache attribute not uses Last-Modified caching mechanism. The other thing is it uses the old page caching mechanism making it difficult to customize/extend.
Do you really need to implement the last-modified mechanism in all your actions?
Really not required. You could implement the last-modified mechanism to the actions that takes more time to create such a response and it takes more time to travel the response down the wire and reach the browser. In other cases I feel it just an overhead implementing throughout all the actions and also you have to measure out the benefits before doing so. The other main point is, in many cases the version of the page is not just decided by a single table column it could be by many other things and in those cases it may be more complicated to implement this!
A point about ETag
Though the question is about Last-Modified header I should tell something about ETag before clicking the Post Your Answer button. Compared to Last-Modified (which relies on datetime) header ETag header (relies on a hash value) is more accurate in determining whether the cached response in the browser is fresh or not but it could be little complicated to implement. IIS also includes ETag header along with the Last-Modified header for static resources. Before implementing any of this mechanism google out and see whether there is any library out there that helps you out!

Streaming Databased Images Using HttpHandler

For a long time now I have noticed something annoying when working on Web Application projects involving databased images on my local machine. By local I mean that it's a typical environment with VS 2008 and SQL Server 2005 on my workstation. Whenever I use an HttpHandler to display the images on my local, only some of the images render on each page load.
However, when I push the application to a hosted environment, the problem usually disappears. However, I just pushed a new project out to a hosted environment and experienced the same problem as on my local - this time the site and the DB were on the same server in the hosting environment. Has anyone got a take on what's happening here?
Here's the handler:
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class FeaturedHandler : IHttpHandler
{
Business biz = new Business();
public void ProcessRequest(HttpContext context)
{
if (context.Request.QueryString["ListingID"] != null)
{
int listingID = Convert.ToInt32(context.Request.QueryString["ListingID"]);
DataSet ds = biz.GetFeaturedImageByID(listingID);
DataRow row = ds.Tables[0].Rows[0];
byte[] featureImage = (byte[])row["Photo"];
context.Response.ContentType = "image/jpeg";
context.Response.OutputStream.Write(featureImage, 0, featureImage.Length);
}
else
throw new ArgumentException("No ListingID parameter specified");
}
public bool IsReusable
{
get
{
return false;
}
}
}
I have tried using a DB on a separate server but encountered the same problem. Should I be using a DataReader instead?
UPDATE
I should have used a DataReader initially since I am reading binary data.
I finally got all images to render by changing the value of the IsReusable property to true:
public bool IsReusable
{
get
{
return true;
}
}
Apparently, this keeps the handler in memory and able to handle multiple requests. When set to false, it had to create a new instance of the handler for each incoming request.
By this:
Whenever I use an HttpHandler to
display the images on my local, only a
portion of the images render on each
page load.
Do you mean that the same image appear on places where different images should appear or that some images appear and some doesn't show at all?
In your case the difference by switching isReusable to true is that new Business(); will be called once for multiple images. If isReusable is false the new Business(); will be called once per image. This means that if you have several images per page new Business(); will be called several times for this particular page.
Also I strongly suggest changing this:
if (context.Request.QueryString["ListingID"] != null)
{
int listingID = Convert.ToInt32(context.Request.QueryString["ListingID"]);
with:
string listingIdParam = context.Request.QueryString["ListingID"];
if (listingIdParam != null)
{
int listingID = Convert.ToInt32(listingIdParam);
This will save you null reference exceptions that usually surface only under heavy load. Also the above will prevent serving the wrong image to a request especially when isReusable is true.
I cannot determine what the problem was exactly but I can definitely say that setting the isReusable flag was just a workaround and doesn't fix your problem. Also when a problem like this is reproducible only in certain environment that means that either it's a thread problem or there is some difference in request handling (different web server - IIS6, IIS7, development server).
Maybe posting the Business class and it's constructor can shed some light.
Also I suggest implementing some kind of error logging to trap exceptions in the handler and review them.
If you are serving images directly, do not forget to set the correct caching headers, i.e. etags and expires. If you don't you are really going to hit your database hard and use up your bandwidth.
You will need to handle the following http headers:
ETag
Expires
Last-Modified
If-Match
If-None-Match
If-Modified-Since
If-Unmodified-Since
Unless-Modified-Since
For an example http handler that does this check out:
http://code.google.com/p/talifun-web/wiki/StaticFileHandler

ASP.NET/Static class Race Condition?

I have an ASP.NET application with a lot of dynamic content. The content is the same for all users belonging to a particular client. To reduce the number of database hits required per request, I decided to cache client-level data. I created a static class ("ClientCache") to hold the data.
The most-often used method of the class is by far "GetClientData", which brings back a ClientData object containing all stored data for a particular client. ClientData is loaded lazily, though: if the requested client data is already cached, the caller gets the cached data; otherwise, the data is fetched, added to the cache and then returned to the caller.
Eventually I started getting intermittent crashes in the the GetClientData method on the line where the ClientData object is added to the cache. Here's the method body:
public static ClientData GetClientData(Guid fk_client)
{
if (_clients == null)
_clients = new Dictionary<Guid, ClientData>();
ClientData client;
if (_clients.ContainsKey(fk_client))
{
client = _clients[fk_client];
}
else
{
client = new ClientData(fk_client);
_clients.Add(fk_client, client);
}
return client;
}
The exception text is always something like "An object with the same key already exists."
Of course, I tried to write the code so that it just wasn't possible to add a client to the cache if it already existed.
At this point, I'm suspecting that I've got a race condition and the method is being executed twice concurrently, which could explain how the code would crash. What I'm confused about, though, is how the method could be executed twice concurrently at all. As far as I know, any ASP.NET application only ever fields one request at a time (that's why we can use HttpContext.Current).
So, is this bug likely a race condition that will require putting locks in critical sections? Or am I missing a more obvious bug?
If an ASP.NET application only handles one request at a time all ASP.NET sites would be in serious trouble. ASP.NET can process dozens at a time (typically 25 per CPU core).
You should use ASP.NET Cache instead of using your own dictionary to store your object. Operations on the cache are thread-safe.
Note you need to be sure that read operation on the object you store in the cache are threadsafe, unfortunately most .NET class simply state the instance members aren't thread-safe without trying to point any that may be.
Edit:
A comment to this answer states:-
Only atomic operations on the cache are thread safe. If you do something like check
if a key exists and then add it, that is NOT thread safe and can cause the item to
overwritten.
Its worth pointing out that if we feel we need to make such an operation atomic then the cache is probably not the right place for the resource.
I have quite a bit of code that does exactly as the comment describes. However the resource being stored will be the same in both places. Hence if an existing item on rare occasions gets overwritten the only the cost is that one thread unnecessarily generated a resource. The cost of this rare event is much less than the cost of trying to make the operation atomic every time an attempt to access it is made.
This is very easy to fix:
private _clientsLock = new Object();
public static ClientData GetClientData(Guid fk_client)
{
if (_clients == null)
lock (_clientsLock)
// Check again because another thread could have created a new
// dictionary in-between the lock and this check
if (_clients == null)
_clients = new Dictionary<Guid, ClientData>();
if (_clients.ContainsKey(fk_client))
// Don't need a lock here UNLESS there are also deletes. If there are
// deletes, then a lock like the one below (in the else) is necessary
return _clients[fk_client];
else
{
ClientData client = new ClientData(fk_client);
lock (_clientsLock)
// Again, check again because another thread could have added this
// this ClientData between the last ContainsKey check and this add
if (!clients.ContainsKey(fk_client))
_clients.Add(fk_client, client);
return client;
}
}
Keep in mind that whenever you mess with static classes, you have the potential for thread synchronization problems. If there's a static class-level list of some kind (in this case, _clients, the Dictionary object), there's DEFINITELY going to be thread synchronization issues to deal with.
Your code really does assume only one thread is in the function at a time.
This just simply won't be true in ASP.NET
If you insist on doing it this way, use a static semaphore to lock the area around this class.
you need thread safe & minimize lock.
see Double-checked locking (http://en.wikipedia.org/wiki/Double-checked_locking)
write simply with TryGetValue.
public static object lockClientsSingleton = new object();
public static ClientData GetClientData(Guid fk_client)
{
if (_clients == null) {
lock( lockClientsSingleton ) {
if( _clients==null ) {
_clients = new Dictionary``();
}
}
}
ClientData client;
if( !_clients.TryGetValue( fk_client, out client ) )
{
lock(_clients)
{
if( !_clients.TryGetValue( fk_client, out client ) )
{
client = new ClientData(fk_client)
_clients.Add( fk_client, client );
}
}
}
return client;
}

Resources