System.Web.Caching.Cache in ASP.NET - asp.net

I just discovered System.Web.Caching.Cache used in a project that I am working on and I am having a hard time finding more information on it.
My question is how this cache is persisted? Is it client-side (similar to ViewState), server-side (Session)? Entirely different?
Example:
protected string FileContent
{
get
{
return Cache[FILE_UPLOAD_KEY + Id] as string ?? GetFileUpload();
}
}

It's a server-side, application-wide cache.
One instance of this class is created per application domain, and it
remains valid as long as the application domain remains active.
Information about an instance of this class is available through the
Cache property of the HttpContext object or the Cache property of the
Page object. (Cache Class, MSDN)
It grants the ability to set time limits and so forth on cached objects. And it doesn't promise the object will be there when you need it again. It keeps items in cache only so long as there is sufficient memory to do so.
So, it's not intended for passing objects between page views (use ViewState or Session for that) or controls (use Items for that). It's intended to cache global objects (accessible in any request from all clients) that are expensive to build.

It's persisted at the server, and it's global across sessions, like Application. So when you set a value in the Cache, it's available to all users until it expires.
EDIT
The example you've got probably isn't quite right (unless GetFileUpload() actually writes to the cache). Generally your calls to cache look something like:
string GetSomeStringFromCache()
{
string someString = Cache[SomeKey] as string;
if (someString == null)
{
someString = GetStringUsingSomeExpensiveFunction();
Cache.Add(SomeKey, someString, /*a bunch of other parameters*/);
}
return someString;
}
This will put it in the Cache if it's not already there, but if it is, it will just use it.

Related

in-memory cache for ASP.NET web app

I need to cache a very small amount of data for a maximum of one hour for an ASP.NET web application (one instance). Obviously this needs to be thread-safe so I can access the cache from within my requests.
I want to do this "in process", and not use anything external.
What would be the easiest way to implement this?
You can user the Cache object ASP.NET provides you with.
You can create a property that returns the cached object if exist else retrieve it from db.
private myClass myProp {
get{
if (Cache["Key1"] == null)
Cache.Add("Key1", "Value 1", null, DateTime.Now.AddMinutes(60), Cache.NoSlidingExpiration, CacheItemPriority.High);
return (myClass)Cache["Key1"];
}
}
Use static variables. You could write a static cache class including your update logic (maximum of one hour) and store the retrieved data in a static member.
The class will persist in the app pool until it is recycled. This could be too often or too rarely for your use cases. But the caching ability should be fair enough.
For the thread-safety issues you could provide getter methods in this class and make use of the lock statement.

TempData implementation changes - Reasons for the change

In ASP.NET MVC 2, the lifespan of an entry in the TempDataDictionary was just one HTTP Request.
That translated to setting a value in one request, redirecting, and having access to the same item at the other end of the line. After this the entry would be no longer available, regardless of whether you read the value out of the dictionary at the latter end of the line or not.
Since ASP.NET MVC 3 (I believe), this implementation detail has changed quite significantly.
Entries in the TempDataDictionary are now only removed once they've been read.
MVC 4
public object this[string key]
{
get
{
object obj;
if (!this.TryGetValue(key, out obj))
return (object) null;
this._initialKeys.Remove(key);
return obj;
}
}
and
public bool TryGetValue(string key, out object value)
{
this._initialKeys.Remove(key);
return this._data.TryGetValue(key, out value);
}
MVC 2:
public object this[string key] {
get {
object value;
if (TryGetValue(key, out value)) {
return value;
}
return null;
}
and
public bool TryGetValue(string key, out object value) {
return _data.TryGetValue(key, out value);
}
Since most people seem to put items in the TempData collection in one request and immediately read them back out in the immediate next request, the functionality seems roughtly the same.
In scenarios where this is not the case such as wanting to read the TempData entry if redirected to one place, and expecting it to have been removed if requesting other resources and navigating back, this change has quite an impact.
No longer is the entry available for one http request but is available over many HTTP requests, be it only available to one single get on the dictionary.
I'd like to know more about this implimentation change, what were the reasons for the change, was this simply to cater for multiple redirects or are there deeper benefits?
Secondary to that, I'm intrigued to know if there's anything built in that now caters for single HTTP request sharing of data in the same way that TempData used to cater for?
You're correct that TempData keys are only cleared if they’ve been read (or after the user’s session expires) but this has been the case since MVC2, (http://forums.asp.net/post/3692286.aspx)
I'd like to know more about this implimentation change, what were the
reasons for the change, was this simply to cater for multiple
redirects or are there deeper benefits?
This change prevented problems that arose in MVC 1, such as TempData keys being deleted before they were read. So yes, the primary benefit is in avoiding these problems when you have multiple re-directs, or interleaved requests. In addition, the RedirectToRouteResult or RedirectResult methods now automatically call TempData.Keep() to prevent clearing of keys, even after they've been read so keep that in mind as well.
In scenarios where this is not the case such as wanting to read the
TempData entry if redirected to one place, and expecting it to have
been removed if requesting other resources and navigating back, this
change has quite an impact.
You’re correct, if you've been coding under the assumption that the TempData keys are cleared automatically you could run into unexpected problems. You can call TempData.Clear() to manually remove all keys from the TempDataDictionary, or TempData.Remove(key) to remove a specific key. You can also use TempData.Peek() to read the value of a TempData key without flagging it for removal from the TempDataDictionary.
Secondary to that, I'm intrigued to know if there's anything built in
that now caters for single HTTP request sharing of data in the same
way that TempData used to cater for?
I'm not aware of any new objects or functions that replicate the original implementation of TempData. Essentially we still use TempData but have to be mindful that the data persists until read and clear the dictionary manually if needed.

Is it possible to make a webservice per user?

I have a webservice (.asmx) and I want it to retain a value on a per-user basis. Is this possible?
ie (pseudo-code)
MyWebservice
{
object perUserVariable = something;
[WebMethod]
public void myMethod()
{
if (something == null)
{
something = doBigExpensivedatabaseCall();
}
return something;
}
}
You can use ASP.NET's session mechanism.
Change your WebMethod attribute, so that it will look like that:
[WebMethod(EnableSession = true)]
This is normally achieved by cookies, or by sending the session id in the query string (both ways are completely handled by ASP.NET). The former is the default, to achieve the latter, just set cookieless="true" in your config.web file.
Yes, you would need to pass in some kind of user identifier to specify who the user is, do your operation and instead of storing it in something you will to use a durable or semi-durable store such as Cache or Session. Then look that value up from the Cache or Session instead of a local member.
Also fwiw the way you have that configured the fact something isn't static means it would be null on every single request because it would be newly initialized. Making it static however would then server the individual instance of something to each and every request there after.
This is why you need to use a store that can differentiate on the user such as Cache[userid+"something"] or the Session["something"] instead.
Your choices seem to be using some type of caching system like session or memcache.
Session will require a session id passed as a cookie to the requests. Other caching providers could probably key off of a post value like the userid.

Using session like ViewState

I am fixing an ASP.NET application that makes heavy use of session to track per-page data. One of the problems is that the session bleeds between pages.
ViewState would be a better solution, except:
The data is not serializable
There is too much data to be sending back and forth each postback
So I want to:
create a page key for the session data (i.e. stick a random GUID in a hidden field)
expire data from an abandoned page even if the overall session is active
Is there a good way to expire partial session data?
The following temporary storage locations are available:
Session. This follows a user around and uses a cookie. It can be configured to use a URL param to retrieve. Session can also be configured to be stored in process(inproc) on the web server, in SQL Server, or in a state server. InProc can store any data type, but the others require the type to be serializable.
Cache. Data stored in Cache is available to be used by any user in an session. It works similar to session, as the objects are retrievable via a key. One of the nicer features of cache is that you can control how long things are stored, and you can consume event when they expire. You can store anything here, but you may run into issues with using it in a webfarm.
HttpContext. This is scoped to the current request. Remember, requests can be webservice calls, calls to get webpages to get HTML, or calls to a service that returns images. Anything can be stored here.
ViewState. View state is scoped to a page. Must be serializable.
You may want to examine cache. If you're using a webfarm, it won't work, but you could use a GUID of some sort as the key that you map back to a session.
I would probably do it this way:
Create an object to store the state information you want to be page specific. If different pages need different information, create multiple classes.
Store this object in a single session key: Session["PageSpecific"]; for example.
Create a class which inherits from System.Web.UI.Page.
In the OnLoad event of the base class, clear the session key if the the page is not performing a postback.
Create and call an overloadable method to populate the session object.
Instead of inheriting from System.Web.UI.Page in each of your pages, inherit from your new base class.
Something like this (warning: air code. May contain syntax errors):
public class PageBase
: System.Web.UI.Page
{
protected overrides OnInit(System.EventArgs e) {
base.OnInit(e);
if(!this.IsPostBack) {
Guid requestToken = System.Guid.NewGuid();
ViewState["RequestToken"] = requestToken;
Session["PageSpecific" & requestToken.ToString()] = InitializePageSpecificState();
}
}
protected virtual object InitializePageSpecificState() {
return new GenericPageState();
}
//You can use generics to strongly type this, if you want to.
protected object PageSpecificState {
get {
return Session["PageSpecific" & ViewState["RequestToken"].ToString()];
}
}
}
Perhaps on each page !IsPostBack or through a base page you could null out all references to session data not pertaining to that page. This would "expire" the data each time the user goes to another page in the site.
If the user leaves the site or goes in-active, there's not much you can do until the session expires, but in this scenario there would only be one page worth of session data per user.

Static data in an ASP.NET page - threadsafe?

The background to this question is that I need to use some user session data in a (static) WebMethod. I have created a static property that references the data I need like so:
private static UserWebSession UserWebSession
{
get
{
return (UserWebSession)HttpContext.Current.Session["UserWebSession"];
}
}
I can then call this in my page's static WebMethod.
My question is, is this technique thread safe? Or will this property's value be updated with every new page request - in other words, it will return the UserWebSession for the user who most recently requested the page?
That's fine - HttpContext.Current is designed precisely for this sort of thing. You won't get a previous user's session.
It's dependent on the thread though (I believe) - so if you start any extra background threads, they won't be able to see the current context.
Also be aware that although this call is safe in terms of not getting the wrong context, the normal concurrency caveats apply when it comes to what you actually do with the context.
I don't know that a Page Method is able to access Session state. If it can, then you may be ok. I recall that access to Session state is serialized, so that only one request at a time can arrive for a given session.

Resources