I have configured an asp.NET web application to use forms authentication and store the session in a custom table. So far so good, when I log in the authentication persists across browser sessions. I am trying to get to grip with the possibilities of storing the session like so but I come up against a problem early on.
If I add variable/value pairs to the session object, and output the session variables to the page, the variable/value pairs are displayed as expected, but when I close and reopen the browser the correct session reloads as expected (which I confirm by outputting the sessionid) but the variable/value pairs are gone.
Is there something very simple I am missing here as from what I understand the session variables should also be available across browser sessions(strings are serializable right?).
List<SessionVar> sessionVars = new List<SessionVar>();
protected void Page_Load(object sender, EventArgs e)
{
Session[Session.SessionID] = Session.SessionID;
LoadSessionData();
}
protected void btnSessionVariable_Click(object sender, EventArgs e)
{
Session[txtVariableName.Text.Trim()] = txtVariableValue.Text.Trim();
txtVariableName.Text = string.Empty;
txtVariableValue.Text = string.Empty;
LoadSessionData();
}
private void LoadSessionData()
{
sessionVars.Clear();
foreach (string key in Session.Keys)
{
sessionVars.Add(new SessionVar(key, (string)Session[key]));
}
gridView.DataSource = sessionVars;
gridView.DataBind();
}
Session state is NOT maintained when a browser (or browser tab) closes and is restarted.
The session cookie is no longer available for the new browser window so a new session will start. That ASP.NET assigns the same id again is just a coincedence.
Related
I have a crystal report in my asp.net web application which has a lot of report parameters (about 15). Previously I was using querystring to pass them but it was being unsecure.
Now I am mapping all parameters to a Hashtable, storing them in session & passing to the report viewer. Now If user opens multiple instance of reports in different browser tabs, the session values get messed up. When I navigate pages, wrong reports are displayed.
Please advise me a good method to pass my parameters to report.
Damien.
How about creating a simple DTO for storing all report parameters. Save this DTO in session and access in report viewer page where your viewer control resides.
To cross over the session issue when user opens multiple instances in browser, you can do a simple trick. When user provides parameter, and clicks on "Show Report", at that point create a guid, store value inside the session using key as this guid, and pass this guid as query string parameter to your report viewer page. This way each instance of report viewer page is aware which session value to be brought out.
Something like this
public class AttendanceDTO
{
public int EmployeeId {get;set;}
public string Month {get;set;}
}
And then in your "Report parameter page"
/* JUST NOTEPAD CODE, SYNTAX MIGHT VARY */
protected override ShowReport_Click(object sender, EventArgs e)
{
string guid = new Guid();
AttendanceDTO dto = new AttendanceDTO()
{ EmployeeId = txtEmployee.Text;
Month = txtMonth.text
};
session[guid] = dto;
Response.Redirect("ReportViewer.aspx?Guid=" + guid);
}
And then inside your Report Viewer Page
string guid = Request.QueryString["Guid"]; //Null check etc..
AttendanceDTO dto = (AttendanceDTO) session[guid];
//Provide dto values as parameters to your report viewer control and then clean the session
[You would need to make sure that you clear out the respective session value after you get it used.]
[Above code is just from notepad to give you an idea. On top of this you can bring more innovation]
Session Objects are shared among multiple tabs of same browser since server stores only one session cookie for a browser. So it is pretty obvious for the values to messup in several tabs on single browser.
One of the possible solution:
Use ViewStates instead of session to store HashTable.
Refer the code below for a demo, I have used ArrayList instead is HashTable for the sake of simplicity.
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
ViewState["ht"] = new ArrayList();
//initialize viewstate with arraylist on first pageload.
}
}
protected void btnAddElement_Click(object sender, EventArgs e)
{//adding new elements to the arraylist
if (ViewState["ht"] != null)
{
ArrayList ht = (ArrayList)ViewState["ht"];
ht.Add(TextBox1.Text);
ViewState["ht"] = ht;
}
}
protected void Button2_Click(object sender, EventArgs e)
{
if (ViewState["ht"] != null)
{
ArrayList ht=(ArrayList)ViewState["ht"];
foreach (object a in ht)
{//write code to pass parameters to the crystal report
Response.Write(a);
}
}
}
}
Since Viewstates work at page level, therefor the values in the collection will not mess up this time.
Hope this helps.
is there an easy way, to store all needed global variables in sessions at once, before the PostBack starts? Or have I to store them in each step where I change them?
I will do something like:
// Global variable.
bool test = true;
// Store all needed information in a session.
protected void Before_globalvariable_is_set_to_default_value(...)
{
Session["Test"] = test;
...
}
protected void Page_Load(object sender, EventArgs e)
{
if(IsPostBack)
{
//if(Session["Test"] != null)
//{
test = (bool)Session["Test"];
Session.Contents.Remove("Test");
//}
}
}
Is something like that possible?
Additional Information
At the Page_Load (!IsPostBack) I check if the user gets more vision, if he gets, I set a global var to true. Later in my code I check if that var is true and add additional columns to a GridView.
Now if a PostBack occurs, I can’t check that var, because I lose the information. I knew that I need to store the information in a Session. If I set the Session at the time where I set the global var to true, I get problems with the session timeout (If the user is on the site, but doesn’t do something for a while). So I thought it will be good, if I set the Session shortly before I lose the information of the global var and delete the Session after reinitialization.
That’s my idea, but I don’t know if something like that is possible.
Edit2:
If I do following it works:
//Global variable
bool test = false;
protected void Page_PreRender(object sender, EventArgs e)
{
Session["Test"] = test;
}
protected void Page_Load(object sender, EventArgs e)
{
if (IsPostBack)
{
test = (bool)Session["Test"]; // Session is true!!!
Session.Contents.Remove("Test");
}
else
{
test = true; // Set at the PageLoad the var to true.
}
}
I’m a little bit confused, I thought PreRender is after the PageLoad, why suddenly the test var is true and if I remove the PreRender it isn’t?
Greetz
If you're worried about losing a specific value between requests, because you've maintained the state of that variable in the Session object and it might have been cleared by a timeout, you could consider using another, more durable, mechanism to save the state: for example, cookies or database.
If the value only needs to live during that one Request, you can use class-level fields of the code-behind class. Set them in the Init or Load phase, then you can use those values in all other phases.
For a lifetime of just a single request:
public partial class MyPage: Page
{
private bool test = true;
public void Page_Load(...)
{
// maybe set 'test' to another value
}
public void Button_Click(...)
{
// you can still access 'test'
}
public void Page_PreRender(...)
{
// you can still access 'test'
}
}
If however you need that value to live from request to the next postback, you can use ViewState instead of Session. Advantage: no timeout as it is stored in the html and posted back from the browser along with other data. Disadvantage: it only works in postback-scanario's, not when you link to a different page.
What i am doing is whenever users logs in I store his username in Session Object
Now what i want on the Admin Page is the List of ACTIVE USERS (i.e No of Users which are presently working with the Application (usernames in Session Objects)
Is there any way of doing that..
???
Thanks
Based on your comment to Davide Piras, if you are storing Session["user"] =username then you are only storing one element since you are always using the same key.
I would put everything in a List<string>, for example.
Something like this in your login page:
List<string> activeUsers = Cache["ActiveUsers"] as List<string>;
if(activeUsers==null)
activeUsers = new List<string>();
activeUsers.Add(username_of_person_logged_in);
Cache["active_users"]=activeUsers;
Then in your "Admin" page...
List<string> activeUsers = Cache["ActiveUsers"] as List<string>;
if(activeUsers!=null)
{
foreach(var item in activeUsers)
{
//do something with them
}
}
Note: I changed it to Cache since Cache is shared across all users. Session will not work since it will be only valid on a per-user basis. Thanks to #CheckRaise for his comment.
The Session object cannot be accessed outside of its own session. If you need an administrator to be able to see all the active sessions, you need to use the Application object. For example, in global.asax:
protected void Application_Start(object sender, EventArgs e) {
Application["Users"] = new List<string>;
}
Then, to add a user (possibly when they click 'Log in'):
Application.Lock();
((List<string>)Application["Users"]).Add(username);
Application.UnLock();
You should also remove the user in Session_End:
protected void Session_End(object sender, EventArgs e) {
Application.Lock();
((List<string>)Application["Users"]).Remove(username);
Application.UnLock();
}
Given this Global.asax.cs:
using System;
using System.Web;
namespace Foo.Web {
public class Global : HttpApplication {
private const string IntroductionPageShownSessionKey = "IntroductionPageShownSessionKey";
protected void Application_AcquireRequestState(object sender, EventArgs e) {
ShowIntroductionIfNotYetShown();
}
private void ShowIntroductionIfNotYetShown() {
if (HttpContext.Current.Session != null) {
var introductionPageShown = Convert.ToBoolean(Session[IntroductionPageShownSessionKey]);
if (!introductionPageShown) {
if (Request.Path.EndsWith("/Introduction.aspx")) {
Session[IntroductionPageShownSessionKey] = true;
}
else {
Response.Redirect("~/Introduction.aspx" + Request.Url.Query);
}
}
}
}
}
}
User hits webapp and is shown Introduction.aspx
User continues using webapp for a few minutes (ASP.NET_SessionId: ublbhu45ji31e055ywqu0555)
User falls idle (doesn't perform any postbacks) for a few minutes
User performs postback
User is shown Introduction.aspx
Second inspection of user's ASP.NET_SessionId cookie still shows ublbhu45ji31e055ywqu0555
Why is the user shown Introduction.apsx the second time inside the same ASP.NET Session? I'm familiar w/ the risk in setting session variables just before a redirect in the same postback, but that doesn't apply here, right?
Keep in mind that the Session itself likely has a shorter lifetime than the session cookie being sent to the browser and the ID value set in that cookie. In fact, the browser can continue to submit an old session ID, and the server will accept it and create a new session from it, if the old was expired.
The implications being two possibilities:
1) The session is timing out due to the timeout config value (I know not the case in your particular instance)
2) What we've figured out since in your case via the comments to this question: the AppDomain was shutting down or being recycled.
What would be the best practice to persist property values of an aspx-page?
I have done the following, is there some neater way?
public string DataTable
{
get
{
return _DataTable;
}
set
{
_DataTable = value;
ViewState["DataTable"] = value;
}
}
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
DataTable = Request["dataTable"].ToString();
}
else
{
DataTable = ViewState["DataTable"].ToString();
}
}
You have multiple places where you can persist data,each with it's own pros and cons, and with it' own lifespan:
ViewState - stored individually on each page as a hidden (somewhat encrypted) item on the client. Remember that the data has to make a round trip to the client and back on each postback, so generally it's not a good ideea to store large ammounts of data
HiddenItem - a hidden input control. Works the same as ViewState, except it's not enrypted and you can use the values in the JS from the client
QueryString - same as hiddenitem, but seriously, use it only for small ammounts of data. I think some browsers have a limit on the URL length
Session - has the advantage of being able to store larger ammounts of data, as this is stored on the server end, not on the client. You can get into trouble if the client is using the back/next buttons on the browser, and you need to be carefull about maintaining session data accross server farms(ie. multiple servers running the same webapp)
Cache - almost identical to Session, except you can access it from other sessions. This is better to use for "globally" accesable data (ie. stuff everyone uses in you app)
Static Properties - works the same as cache, but you cannot share it across webfarms, so each member of the webfarm will have it's own static value in it's loaded assembly.
I would do it like this:
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
TableName = Request.QueryString["tableName"];
}
public string TableName
{
get { return ViewState["tableName"] as string; }
set { ViewState["tableName"] = value; }
}
I don't like using Request["tableName"] alone since it has to search in more places. Usually I know where I'm sending the parameter.
Also DataTable is a type, so it's best to not use it as a property name.