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!
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
}
The system I am working on does not use standard ASP.NET Auth/ Membership facilities for logging users in/ out. Therefore after logging the user in I want to issue a new Session ID to the user in order to prevent Session trapping/ Hijacking. The problem i have is that although I have been able to successfully create a new session with a new ID and copy the various components to the newly created session eg. session["value"]. By the end of the code excerpt below the newly created session is the current HTTPContext's session, and has the session values that were copied accross. However after performing a Response.Redirect the new session is in action, but none of the session["values"] have persisted across the two requests. As you can see from the code below i've tried adding the values to a number of collections to avail.
Any help would be amazing!! Thanks in advance
bool IsAdded = false;
bool IsRedirect = false;
HttpSessionState state = HttpContext.Current.Session;
SessionIDManager manager = new SessionIDManager();
HttpStaticObjectsCollection staticObjects = SessionStateUtility.GetSessionStaticObjects(HttpContext.Current);
SessionStateItemCollection items = new SessionStateItemCollection();
foreach (string item in HttpContext.Current.Session.Contents)
{
var a = HttpContext.Current.Session.Contents[item];
items[item] = a;
}
HttpSessionStateContainer newSession = new HttpSessionStateContainer(
manager.CreateSessionID(HttpContext.Current),
items,
staticObjects,
state.Timeout,
true,
state.CookieMode,
state.Mode,
state.IsReadOnly);
foreach (string item in HttpContext.Current.Session.Contents)
{
var a = HttpContext.Current.Session.Contents[item];
newSession.Add(item,a);
}
SessionStateUtility.RemoveHttpSessionStateFromContext(HttpContext.Current);
SessionStateUtility.AddHttpSessionStateToContext(HttpContext.Current, newSession);
manager.RemoveSessionID(HttpContext.Current);
manager.SaveSessionID(HttpContext.Current, newSession.SessionID, out IsRedirect, out IsAdded);
return newSession.SessionID;
Maybe I'm missing something here but won't this work:
Session["mysession"] = mySessionObject;
Basically it appears it's not possible since you can only add session variables once there has been one round trip to the client to create the corresponding session cookie. Therefore I had to create the new new session (with new ID) so that by the time I came to adding session variables, the client cookie had the appropriate session id: annoying since this in reality is issuing the new session ID before the user is authenticated.
Interestingly, it seems a little strange that issuing a new Session ID is exactly what the standard asp.net authentication/ membership functionality does but is able to maintain session variables, and yet doing it manually it doesn't....are there some methods for this that are not being exposed to us mere developers maybe....
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()
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