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!
Related
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");
}
}
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.
I'm trying to create a Caching Class to cache some objects from my pages. The purpose is to use the Caching system of the ASP.NET framework but to abstract it to separate class.
It seems that the caching doesn't persist.
Any ideas what I'm doing wrong here? Is it possible at all to cache object out side the Page it self?
EDIT: added the code:
Insert to cache
Cache c = new Cache();
c.Insert(userid.ToString(), DateTime.Now.AddSeconds(length), null, DateTime.Now.AddSeconds(length), Cache.NoSlidingExpiration,CacheItemPriority.High,null);
Get from the cache
DateTime expDeath = (DateTime)c.Get(userid.ToString())
I get null on the c.Get, even after I did have the key.
The code is in a different class than the page itself (the page uses it)
Thanks.
There are numerous ways you can store objects in ASP.NET
Page-level items -> Properties/Fields on the page which can live for the lifetime of the page lifecycle in the request.
ViewState -> Store items in serialised Base64 format which is persisted through requests using PostBack. Controls (including the page itself - it is a control) can preserve their previous state by loading it from ViewState. This gives the idea of ASP.NET pages as stateful.
HttpContext.Items -> A dictionary of items to store for the lifetime of the request.
Session -> Provides caching over multiple requests through session. The session cache mechanism actually supports multiple different modes.
InProc - Items are stored by the current process, which means should the process terminate/recycle, the session data is lost.
SqlServer - Items are serialised and stored in a SQL server database. Items must be serialisable.
StateServer - Items are serialised and stored in a separate process, the StateServer process. As with SqlServer, items must be serialisable.
Runtime - Items stored in the runtime cache will remain for the lifetime of the current application. Should the applciation get recycled/stop, the items will be lost.
What type of data are you trying to store, and how do you believe it must be persisted?
Right at the beginning of last year I wrote a blog post on a caching framework I had been writing, which allows me to do stuff like:
// Get the user.
public IUser GetUser(string username)
{
// Check the cache to find the appropriate user, if the user hasn't been loaded
// then call GetUserInternal to load the user and store in the cache for future requests.
return Cache<IUser>.Fetch(username, GetUserInternal);
}
// Get the actual implementation of the user.
private IUser GetUserInternal(string username)
{
return new User(username);
}
That was nearly a year ago, and it has been evolved a bit since then, you can read my blog post about it, let me know if thats of any use.
Your cache reference needs to be accessible to all items in your code - the same reference.
If you are newing up the Cache class every time, you are doing it wrong.
I have done almost the same things, but with a different code (and it work for me) :
(CacheKeys is an enum)
using System;
using System.Configuration;
using System.Web;
using System.IO;
public static void SetCacheValue<T>(CacheKeys key, T value)
{
RemoveCacheItem(key);
HttpRuntime.Cache.Insert(key.ToString(), value, null,
DateTime.UtcNow.AddYears(1),
System.Web.Caching.Cache.NoSlidingExpiration);
}
public static void SetCacheValue<T>(CacheKeys key, T value, DateTime expiration)
{
HttpRuntime.Cache.Insert(key.ToString(), value, null,
expiration,
System.Web.Caching.Cache.NoSlidingExpiration);
}
public static void SetCacheValue<T>(CacheKeys key, T value, TimeSpan slidingExpiration)
{
HttpRuntime.Cache.Insert(key.ToString(), value, null,
System.Web.Caching.Cache.NoAbsoluteExpiration,
slidingExpiration);
}
public static T GetCacheValue<T>(CacheKeys key)
{
try
{
T value = (T)HttpRuntime.Cache.Get(key.ToString());
if (value == null)
return default(T);
else
return value;
}
catch (NullReferenceException)
{
return default(T);
}
}
I have settings in AppSettings (web.config) and I need to pass them to an external javascript file.
In ASP.NET I would think of an ASHX handler to write the javascript file to the response replacing placeholders with the settings values.
Is there a better way to do it in ASP.NET MVC? Thank you.
You could send them via a JsonResult?
In your JS, you'd have a request which sends a GET/POST request to a particular action (let's call it GetAppSetting(), and the corresponding value is returned in the response.
For security reasons, I would restrict what can be requested though...
public JsonResult GetAppSetting(string id)
{
//You could check what's been requested here if you want to make sure you're only returning information that you may not wish to send.
string appSetting = AppSettings[id];
if(string.IsNullOrEmpty(appSetting) == false)
{
return Json(appSetting, JsonRequestBehavior.AllowGet);
}
//Handle non-existent settings here...
throw new Exception("This setting does not exist");
}
Alternatively, it has been suggested by Chris Marisic in the comments that you may want to absolutely limit this to just a specific set of key/values for developer reasons. Therefore, here is a quick example of that...
public JsonResult GetAppSettings()
{
var appSettings = new Dictionary<string, string>();
appSettings.Add("myAppSetting1", AppSettings["myAppSetting1"]);
appSettings.Add("myAppSetting2", AppSettings["myAppSetting2"]);
appSettings.Add("myAppSetting3", AppSettings["myAppSetting3"]);
return Json(appSettings, JsonRequestBehavior.AllowGet);
}
Note the JsonRequestBehavior.AllowGet in the JsonResults (MVC 2 only). This is because, by default, ASP.NET MVC 2 will not allow GET requests on actions which return a JsonResult. You can circumvent this by adding the JsonRequestBehaviour, but I should probably mention that you should consider doing a post request in order to retrieve this information, and remove this behaviour in your action.
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