Get AbsoluteExpiration of HttpContext.Current.Cache - asp.net

In my asp.net application I am setting cache like this;
const string key = "MyTestKey";
object value = true;
DateTime absoluteExpiration = DateTime.Now.AddMinutes(10);
TimeSpan slidingExpiration = Cache.NoSlidingExpiration;
const CacheItemPriority priority = CacheItemPriority.Default;
CacheItemRemovedCallback onRemoveCallback = null;
HttpContext.Current.Cache.Add(key, value, null, absoluteExpiration, slidingExpiration, priority, onRemoveCallback);
How can I retrieve back absoluteExpiration of a HttpContext.Current.Cache object on next request? Or can I get TimeSpan of how long remain for cache I set up?

You cannot retrieve the expiration or other CacheItem information for the item once it has been added to the System.Web.Caching.Cache, except when the item is being removed (either explicitly by calling Remove, or by cache expiration) or updated. The only methods that exist retrieve the object return the object only.
If you want more advanced Cache functionality, you'll need to look into using something like MemoryCache instead.

Related

ASP.NET MVC - OutputCache - Does setting duration expire cached values?

Does setting the duration for OutputCache expire the cached values? Because if it does, I'm not seeing it.
[OutputCache(Duration = 1, Location = OutputCacheLocation.Client, VaryByParam = "none", NoStore = true)]
public ActionResult Index()
{
if (System.Web.HttpContext.Current.Cache["time"] == null)
{
System.Web.HttpContext.Current.Cache["time"] = DateTime.Now;
}
}
I'm new to using OutputCache so excuse the beginner question. But it was my understanding that by specifying the duration, something was suppose to happen after the allotted time. In my code snippet above, the time persists regardless of when I refresh my view.
You are confusing the OutputCache with the HttpContext.Current.Cache. The first is used to return the cached view when you hit the action, if the cache is not expired. And about that, you are right. Every 1 second it will return a new view.
However, the HttpContext.Current.Cache that you are filling with DateTime.Now, will never expire. Because you are not defining the absolute expiration
https://msdn.microsoft.com/en-us/library/system.web.caching.cache(v=vs.110).aspx
Doing this
System.Web.HttpContext.Current.Cache["time"] = DateTime.Now;
is the same as this
System.Web.HttpContext.Current.Cache.Insert("time", DateTime.Now, null, System.Web.Caching.Cache.NoAbsoluteExpiration, System.Web.Caching.Cache.NoSlidingExpiration)
Use the Insert method and define the expiration properly, and it should work.

New item added to session on every request

I found this behaviour by accident, as I return the count of items in a session in an error message and found that some sessions had as many as 120 items in them (they should have 1!). On further investigation I found that every request seems to add an item into the session. They are all negative integers, like -710, -140 -528. I can't seem to see a pattern in what number comes up.
I have checked my code for any interactions with the Session object and as far as I can tell it is not me. I store one item in the session which is my own object which has a number of other properties on it. My session state is SQL server, and I am only serialising a certain set of values that need to be kept.
Has anyone seen anything like this or has any advice on where I can troubleshoot further?
Thank you in advance.
-- Edit, as requested - first where I count the items in the session - this is done in the page load event of my master page. I loop through so I could inspect using the debugger.
int itemCount = Session.Count;
for (int i = 0; i < itemCount; i++)
{
object o = Session[i];
}
-- here is where I add my custom object to the session. This is called at session start and in my master page. It runs on a "get, but if not there, create" principle.
HttpSessionState Session = HttpContext.Current.Session;
HttpRequest Request = HttpContext.Current.Request;
if (Session == null)
return null;
SessionData sessionData = (SessionData)Session[StaticNames.SESSION_NAME];
if (sessionData == null)
{
sessionData = new SessionData();
Session.Add(StaticNames.SESSION_NAME, sessionData);
}
I also have this to get the SessionData object from the session:
public SessionData(SerializationInfo info, StreamingContext ctxt)
{
this.IsManualLogin = (bool)info.GetValue("IsManualLogin", typeof(bool));
this.HasAskedUserForLocation = (bool)info.GetValue("HasAskedUserForLocation", typeof(bool));
// ... etc, more items for all users here
int? loginID = null;
try
{
loginID = info.GetInt32("LoginID");
}
catch
{
return;
}
this.LoginID = loginID.Value;
// ... etc, more items for logged in users only
}
There is also an equivalent method for adding this data to the SerializationInfo used for SqlSessionState.
Credit to the modest jadarnel27.
It turns out the Ajax Control Toolkit NoBot control adds an integer into your session on every request. My website has an auto 40 second refresh, similar to facebook, so this probably would have brought the whole thing crashing down at some point and I am lucky to find it now. Should anyone else consider using the NoBot control, be warned about this behaviour!

Expiring a cached item manually/immediately

I'd like to place an item in the ASP.NET cache, say for instance a dataset, and not have it expire at all, until an event occurs in my application that requires the item to be refreshed. But until that happens, it should not expire, ever.
So a) is it possible to set a cached item never to expire (apart from setting expiry say 1 year in the future), and b) how does one manually force an item to expire?
Thanks!
I'm thinking this will work for you. Then when you're ready to refresh the item, you remove it form the cache manually and set it again...
Page.Cache.Add("object",
"something",
null,
System.Web.Caching.Cache.NoAbsoluteExpiration,
System.Web.Caching.Cache.NoSlidingExpiration,
CacheItemPriority.NotRemovable,
new CacheItemRemovedCallback((s, o, r) =>
{
// some callback code if you want...
}));
UPDATED (better demo):
private int _counter = 0;
protected void Page_Load(object sender, EventArgs e)
{
// You can add the cache key like this
AddToCache("key", () => "some object " + _counter++);
//Any time you want to refresh the value, you can call RefreshCachedValue
RefreshCachedValue("key");
RefreshCachedValue("key");
RefreshCachedValue("key");
RefreshCachedValue("key");
RefreshCachedValue("key");
// In this demo, the cached value is now "some object 5"
}
private void AddToCache(string key, Func<object> getValueFunction)
{
Page.Cache.Add(key,
getValueFunction(),
null,
System.Web.Caching.Cache.NoAbsoluteExpiration,
System.Web.Caching.Cache.NoSlidingExpiration,
CacheItemPriority.NotRemovable,
new CacheItemRemovedCallback((s, o, r) =>
{
AddToCache(s, getValueFunction);
}));
}
private void RefreshCachedValue(string key)
{
Page.Cache.Remove(key);
}
is it possible to set a cached item never to expire (apart from setting expiry say 1 year in the future)
Sort of - the Insert method has an overload that supports this: (reference)
DataSet myDataSet = getDataSet();
Page.Cache.Insert("MyDataSetCacheKey", myDataSet)
This will add the object to the cache with no sliding expiration and no absolute expiration, however it uses the default priority, not NotRemovable. If you wanted to force that, you'd have to write an extension method for Insert yourself.
how does one manually force an item to expire?
I'm assuming that you mean here 'I've cached this data forever, but now I want to change it'. In which case, you wouldn't expire it, you'd just remove it from the cache:
Page.Cache.Remove("MyDataSetCacheKey")
Logically, there's no difference between the item being removed from the cache because it expired, was flushed by the server trying to scavenge memory or you removing it manually.
There are some kinds of CacheDependency, you can use FileBased CacheDependency referring to an empty file.
If your data changes, just overwrite the file. As soon as the file changes, the cache will be reset.
http://msdn.microsoft.com/en-us/magazine/cc163955.aspx

ASP.net any way to cache things like this?

I have a function called on every single page:
/// <summary>
/// Gets the date of the latest blog entry
/// </summary>
public static DateTime GetNewestBlogDate()
{
DateTime ReturnDate = DateTime.Now.AddDays(30);
using (var db = new DataClassesDataContext())
{
var q = (from d in db.tblBlogEntries orderby d.date descending select new {d.date}).FirstOrDefault();
if (q != null)
ReturnDate = q.date;
}
return ReturnDate;
}
It works like this website, it gets the latest blog entry date and if it's greater than the users cookie value it displays a new icon next to the blog link.
It seems rather wasteful to keep calling this function per page request, called 1:1 on the number of page requests you have. Say you have 30,000 page views per day, that's 1,250 database queries per hour.
Is there any way I can cache this results, and have it expire say every hour?
I'm aware it's a bit of a micro optimisation, but given 10 or so similar functions per page it might add up to something worthwhile. You could denormalise it into a single table and return them all in one go, but I'd rather cache if possible as it's easier to manage.
Since it's not based on the user (the cookie is, but the query doesn't seem to be) - you can just use the standard ASP.NET Cache.
Just insert the result with an expiration of 1 hour. If you like, you can even use the callback to automatically refresh the cache.
Assuming you've stored it into MS-SQL, you could even use a SqlCacheDependency to invalidate when new data is inserted. Or, if your inserting code is well-factored, you could manually invalidate the cache then.
Just use the ASP.NET Cache object with an absolute expiration of 1 hour. Here's an example of how you might implement this:
public static DateTime GetNewestBlogDate()
{
HttpContext context = HttpContext.Current;
DateTime returnDate = DateTime.Now.AddDays(30)
string key = "SomeUniqueKey"; // You can use something like "[UserName]_NewestBlogDate"
object cacheObj = context.Cache[key];
if (cacheObj == null)
{
using (var db = new DataClassesDataContext())
{
var q = (from d in db.tblBlogEntries orderby d.date descending select new { d.date }).FirstOrDefault();
if (q != null)
{
returnDate = q.date;
context.Cache.Insert(key, returnDate, null, DateTime.Now.AddHours(1), Cache.NoSlidingExpiration);
}
}
}
else
{
returnDate = (DateTime)cacheObj;
}
return returnDate;
}
You haven't indicated what is done with the returned value. If the returned value is displayed the same way on each page, why not just place the code along with the markup to display the result in a user control (ASCX) file? You can then cache the control.
Make it a webmethod with a CacheDuration?
[WebMethod(CacheDuration=60)]
public static DateTime GetNewestBlogDate()

Asp.Net Cache, modify an object from cache and it changes the cached value

I'm having an issue when using the Asp.Net Cache functionality. I add an object to the Cache then at another time I get that object from the Cache, modify one of it's properties then save the changes to the database.
But, the next time I get the object from Cache it contains the changed values. So, when I modify the object it modifies the version which is contained in cache even though I haven't updated it in the Cache specifically. Does anyone know how I can get an object from the Cache which doesn't reference the cached version?
i.e.
Step 1:
Item item = new Item();
item.Title = "Test";
Cache.Insert("Test", item, null, DateTime.Now.AddHours(1), System.Web.Caching.Cache.NoSlidingExpiration);
Step 2:
Item item = (Item)Cache.Get("test");
item.Title = "Test 1";
Step 3:
Item item = (Item)Cache.Get("test");
if(item.Title == "Test 1"){
Response.Write("Object has been changed in the Cache.");
}
I realise that with the above example it would make sense that any changes to the item get reflected in cache but my situation is a bit more complicated and I definitely don't want this to happen.
The cache does just that, it caches whatever you put into it.
If you cache a reference type, retrieve the reference and modify it, of course the next time you retrieve the cached item it will reflect the modifications.
If you wish to have an immutable cached item, use a struct.
Cache.Insert("class", new MyClass() { Title = "original" }, null,
DateTime.Now.AddHours(1), System.Web.Caching.Cache.NoSlidingExpiration);
MyClass cachedClass = (MyClass)Cache.Get("class");
cachedClass.Title = "new";
MyClass cachedClass2 = (MyClass)Cache.Get("class");
Debug.Assert(cachedClass2.Title == "new");
Cache.Insert("struct", new MyStruct { Title = "original" }, null,
DateTime.Now.AddHours(1), System.Web.Caching.Cache.NoSlidingExpiration);
MyStruct cachedStruct = (MyStruct)Cache.Get("struct");
cachedStruct.Title = "new";
MyStruct cachedStruct2 = (MyStruct)Cache.Get("struct");
Debug.Assert(cachedStruct2.Title != "new");

Resources