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
Related
First time login to the asp.net application,stored some session value
Eg: Session["Test"]="Saving Sesison";
Logout the application
When opened the browser for the second time,need to retain the same session value.
Eg: Session["Test"]="Saving Sesison";
How can i do that,can anyone help me with some solution to proceed further please.
if (!Page.IsPostBack)
{
if (Session["Test"] == null)
{
Binding data to repeater control(with out filter)
}
else
{
//Get Session value (To maintain session value across the browser)
var cookieSession = Request.Cookies["Test"]; //While opening the browser for the 2nd time,this line is getting null for all the browsers,but session is getting value for firefox & Chrome not for IE { Session["Test"] }
if (cookieSession != null &&!String.IsNullOrEmpty(cookieSession.Value))
{
Session["Test"] = cookieSession.Value;
}
Binding data to repeater control(with filter using session value)
}
}
//On Drop down selection.
protected void Dropdown_SelectedIndexChanged(object sender, EventArgs e)
{
Binding data to repeater control(based on the dropdown selected value)
Session["Test"] = Dropdown.SelectedItem.Text.ToString(); //To maintain the Dropdown selection all over the app
// Set it
if (Session["Test"] == null)
{
Session["Test"] = Guid.NewGuid().ToString();
var cookie = new HttpCookie("Test", (string)Session["Test"]);
Response.Cookies.Add(cookie);
}
}
ASP.NET Session scope is for only particular session only. So its not possible to have that kind of functionality.
But you can use Cache in same way and it will be there until you make it null or time period exceeds. But beware of fact that it will be there for every browser. So either you need to use different key(Unique key) not like 'test'
You have a few options. Though sessions should be sticky between a browser being re-launched assuming it's not in private/incognito mode. If you're finding the session is timing out too quickly you can extend it in Web.config
<system.web>
<sessionState timeout="10080" mode="InProc" />
</system.web>
Where timeout is in minutes. Note: If you're are debugging stopping and starting the debugger will reset your sessions. So will any kind of re-deployment of the application on IIS. If this is an issue for you, you should check out using something like the SQL session state provider: http://msdn.microsoft.com/en-us/library/vstudio/h6bb9cz9(v=vs.100).aspx
Another method of dealing with this is to store some kind of token in a cookie (again, only works if the browser is not in incognito/private mode, and the user data hasn't been flushed).
// Set it
if (Session["Test"] == null)
{
Session["Test"] = Guid.NewGuid().ToString();
var cookie = new HttpCookie("Test", (string)Session["Test"]);
Response.Cookies.Add(cookie);
}
// Get it
var cookieSession = Request.Cookies["Test"];
if (cookieSession != null && !String.IsNullOrEmpty(cookieSession.Value))
{
Session["Test"] = cookieSession.Value;
}
As a note using the SQL session state provider while is one of the more persistent storages there can be some serious overhead requirements. It's easy to rack up a couple of gigs worth of sessions that are being tracked.
In my experience a combination of cookies and the session provider seem to work best if you need to be very certain that some things are sticking to a users experience on the site.
Edit
So the issue with your drop down selection saver is it's always false and should never set the cookie.
protected void Dropdown_SelectedIndexChanged(object sender, EventArgs e)
{
//Binding data to repeater control(based on the dropdown selected value)
// add to Session
Session["Test"] = Dropdown.SelectedItem.Text.ToString();
// Add Cookie
var cookie = new HttpCookie("Test", (string)Session["Test"]);
Response.Cookies.Add(cookie);
}
Now to get your data back out, put this code in the actions/controllers to run BEFORE you try to access Session["Test"]
var cookieSession = Request.Cookies["Test"];
if (cookieSession != null && !String.IsNullOrEmpty(cookieSession.Value))
{
Session["Test"] = cookieSession.Value; // Should contain the selected text from the drop down
}
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!
I have some asp.net pages that read and write cookie values. During the life cycle of a page it may update the cookie value and then need to read it again further in the code. What I've found is that it's not getting the latest value of the cookie until a page refresh. Is there a way around this? Here's the code I'm using to set and get the values.
public static string GetValue(SessionKey sessionKey)
{
HttpCookie cookie = HttpContext.Current.Request.Cookies[cookiePrefix];
if (cookie == null)
return string.Empty;
return cookie[sessionKey.SessionKeyName] ?? string.Empty;
}
public static void SetValue(SessionKey sessionKey, string sessionValue)
{
HttpCookie cookie = HttpContext.Current.Request.Cookies[cookiePrefix];
if (cookie == null)
cookie = new HttpCookie(cookiePrefix);
cookie.Values[sessionKey.SessionKeyName] = sessionValue;
cookie.Expires = DateTime.Now.AddHours(1);
HttpContext.Current.Response.Cookies.Set(cookie);
}
What you're missing is that when you update the cookie with SetValue you're writing to the Response.Cookies collection.
When you call GetValue you're reading from the Request.Cookies collection.
You need to store the transient information in a way that you access the current information, not just directly the request cookie.
One potential way to do this would be to writer a wrapper class that with rough psuedo code would be similar to
public CookieContainer(HttpContext context)
{
_bobValue = context.Request.Cookies["bob"];
}
public Value
{
get { return _bobValue; }
set {
_bobValue = value;
_context.Response.Cookies.Add(new Cookie("bob", value) { Expires = ? });
}
}
I ran into needing to do similar code just this week. The cookie handling model is very strange.
Start using Sessions to store your information, even if it's only temporary.
Cookies rely on a header being sent to the browser before the page has rendered. If you've already sent information to the client then proceed to set a cookie, you're going to see this "page refresh delay" you've described.
If it's necessary to have this value, use a session variable between the time you set the cookie and when you refresh the page. But, even then I would just recommend avoiding settings cookies so late in the processing step and try to set it as early as possible.
I have an ASP.NET application that caches some business objects. When a new object is saved, I call remove on the key to clear the objects. The new list should be lazy loaded the next time a user requests the data.
Except there is a problem with different views of the cache in different clients.
Two users are browsing the site
A new object is saved by user 1 and the cache is removed
User 1 sees the up to date view of the data
User 2 is also using the site but does not for some reason see the new cached data after user 1 has saved a new object - they continue to see the old list
This is a shortened version of the code:
public static JobCollection JobList
{
get
{
if (HttpRuntime.Cache["JobList"] == null)
{
GetAndCacheJobList();
}
return (JobCollection)HttpRuntime.Cache["JobList"];
}
}
private static void GetAndCacheJobList()
{
using (DataContext context = new DataContext(ConnectionUtil.ConnectionString))
{
var query = from j in context.JobEntities
select j;
JobCollection c = new JobCollection();
foreach (JobEntity i in query)
{
Job newJob = new Job();
....
c.Add(newJob);
}
HttpRuntime.Cache.Insert("JobList", c, null, Cache.NoAbsoluteExpiration, Cache.NoSlidingExpiration, CacheItemPriority.Default, null);
}
}
public static void SaveJob(Job job, IDbConnection connection)
{
using (DataContext context = new DataContext(connection))
{
JobEntity ent = new JobEntity();
...
context.JobEntities.InsertOnSubmit(ent);
context.SubmitChanges();
HttpRuntime.Cache.Remove("JobList");
}
}
Does anyone have any ideas why this might be happening?
Edit: I am using Linq2SQL to retreive the objects, though I am disposing of the context.
I would ask you to make sure you do not have multiple production servers for load balancing purpose. In that case you will have to user some external dependency architecture for invalidating/removing the cache items.
That's because you don't synchronize cache operations. You should lock on writing your List to the cache (possibly even get the list inside the lock) and on removing it from the cache also. Otherwise, even if reading and writing are synchronized, there's nothing to prevent storing the old List right after your call to Remove. Let me know if you need some code example.
I would also check, if you haven't already, that the old data they're seeing hasn't been somehow cached in ViewState.
You have to make sure that User 2 sent a new request. Maybe the content it saws is from it's browser's cache, not the cache from your server
Here is what I am trying to do. I have a TreeView server side control (asp.net 2.0) and I need the user to be able to add nodes to it, then after all the nodes desired are added, the data should be saved to the database.
Here are some things I would like to pay attention to:
1) I don't want to save the tree data each time the new node is added, but rather keep the data in session until the user decides to save the entire tree. The question here is: can I bind the tree to ArrayList object and keep that object in session (rather than keeping the whole tree in session)? Then each time the node is added I will have to rebind the tree to the ArrayList rather than database.
2) I wish to minimize ViewState, any tips? What works best: compressing viewstate or keeping it all on the server at all times?
Thanks!
Use TreeNodeCollection as your internal array to hold in either ViewState or Session. Here's a rough mock-up of an approach you can use; far from perfect, but should set you on the right track.
TreeView tv = new TreeView();
// Button click event for 'Add Node' button
protected void AddNode(object sender, EventArgs e)
{
if (SaveNodeToDb(txtNewNode.Text, txtNavUrl.Text))
{
// Store user input details for new node in Session
Nodes.Add(new TreeNode() { Text = txtNewNode.Text, NavigateUrl = txtNavUrl.Text });
// Clear and re-add
tv.Nodes.Clear();
foreach (TreeNode n in Nodes)
tv.Nodes.Add(n);
}
}
public bool SaveNodeToDb(string name, string url)
{
// DB save action here.
}
public TreeNodeCollection Nodes
{
get
{
if (Session["UserNodes"] ! = null)
return (TreeNodeCollection) Session["UserNodes"];
else
return new TreeNodeCollection();
}
set
{
Session["UserNodes"] = value;
}
}