I am sure that I have made some painfully obvious blunder(s) that I just cannot see. I am hoping one of you can set me straight.
I my session management is working perfectly except that if a user on one machine enters data, a user who starts a session on another machine will also retreive the session information from the first. Not so good. :(
I call my sessions like this:
UserInfo userinfo = UserInfo.Session;
My session mgt class uses this:
static UserInfo userInfo;
static public UserInfo Session
{
get
{
if (userInfo == null)
{
userInfo = new UserInfo();
userInfo.ResetSessionTime();
}
return userInfo;
}
}
I read and write the data like this. I realize that I could serialize the entire class, but it seems like a lot more overhead to serialize and deserialize an entire class each time the class is called as opposed to just grabbing the one or two items I need.
Decimal _latitude;
private String SessionValue(String sKey, String sValue, String sNewValue)
{
String sRetVal = "";
if (sNewValue == null)//not wanting to update anything
{
if (sValue == null)//there is no existing value
{
sRetVal = (String)System.Web.HttpContext.Current.Session[sKey];
}
else
{
sRetVal = sValue;
}
}
else
{
System.Web.HttpContext.Current.Session[sKey] = sNewValue;
sRetVal = sNewValue;
}
return sRetVal;
}
public Decimal Latitude
{
get { return SessionValue("Latitude", _latitude); }
set { _latitude = SessionValue("Latitude", _latitude, value); }
}
Thanks for your help
1) You're using statics for your UserInfo, which means that a single instance of this class is shared among all requests coming to your web server.
2) You're not only storing values in the session (which isn't shared among users) but also in an instance variable, which in this case WILL be shared among users.
So the value of _latitude is causing you this issue. A simple solution is this:
public class Userinfo
{
public Decimal Latitude
{
get { return System.Web.HttpContext.Current.Session["Latitude"]; }
set { System.Web.HttpContext.Current.Session["Latitude"] = value; }
}
}
A better, more testable version would be:
public class UserInfo
{
private HttpSessionStateWrapper _session;
public UserInfo(HttpSessionStateWrapper session)
(
// throw if null etc
_session = session;
)
public Decimal Latitude
{
get { return _session["Latitude"]; }
set { _session["Latitude"] = value; }
}
}
In the second instance, within a request you just construct a new instance of the HttpSessionStateWrapper (using the current Session) and pass it to the UserInfo instance. When you test, you can just pass in a mock Wrapper.
No matter what, the UserInfo instance shouldn't be shared among sessions and it should write and read directly from the Session. Don't try to prematurely optimize things by keeping local versions of your session values. You aren't saving any time and you're just opening yourself up to bugs.
This happens because you store your user info in a static field. Static instances are shared between all requests, and lives the entire lifetime of your application.
In other words, all your users will get the same UserInfo instance from UserInfo.Session.
To fix this you could:
Serialize the whole class into session. I don't know which other properties you have, but I would guess it would not be too much of an overhead.
Create an instance of UserInfo per request, so that the user always reads from a new instance, which in turn will refresh it's values from Session.
Related
I am developing an ASP.NET MVC 4 application. userMenus is a static variable that is loaded every time a user logs in.
public class MenuCL
{
public static List<UserMenu> userMenus = new List<UserMenu>(); // the static variable
}
public class UserMenu
{
public decimal MenuID { get; set; }
public string MenuName { get; set; }
public string Controller { get; set; }
public bool Permission { get; set; }
}
I use that static variable to check whether or not the logged in user has permission to a menu/controller in a custom authorize filter.
It works fine when a single user is logged in, but when two or more users are logged-in, it's all messed up, I mean the error page("you don't have access to this page") is displayed to a user that has permission to the menu/controller.
Only now I realized it's the static variable that is causing all the trouble, after I read this :
The static variables will be shared between requests. Moreover they will be initialized when application starts, so if the AppDomain, thus application gets restarted, their values will be reinitialized.
So I need a replacement for this static variable. Anyone has any suggestion?
You can still use a static field which is a property that provides access to a session variable.
public static List<UserMenu> UserMenus
{
set
{
Session["UserMenus"] = value;
}
get
{
return Session["UserMenus"] == null ? new List<UserMenu>() : (List<UserMenu>) Session["UserMenus"];
}
}
In order to get this working on a web farm which uses a session state server (or sql server), you need to put [Serializable] attribute on top of UserMenu.
I don't think, this way you need to modify your code very much.
My question is, why do you want to use static variable? Do you want to share the values across the application? In this case you can better use session.
Updated
Assume lst as a non static List of UserMenu. Then you can use the following method to store it in session and get it bak whenever you want.
To store
Session["usemenulist"] = lst;
To get it back
try
{
lst = (List<UserMenu>)Session["usemenulist"];
}
catch
{
}
Note
If you are getting the values from the database lo load it to the List for the first time, then you can query database to get it from the database whenever you want, instead of storing it in the session. (This is another option apart from Session, you may try this way also if you want.)
When my user in the students Role login to the system, he can select various classes that he's enrolled. I already have a filter that'll redirect him to the select class page so he must select a class to access the system, and change it anytime he wants and the whole system's context will change.
As for now, i'm storing IdClass in the session variable, using the code below, and the system uses it to filter all the related queries and functions, like showing all the lessons from the current class. My question is: is this a good practice? Is this right or is there any better and efficient way? I'm trying to follow patterns.
[Serializable]
public sealed class Session
{
private const string SESSION_FOO = "STUDYPLATFORM_GUID";
private Session()
{
this.IdClass= 0; // Construct it to 0 so it evaluate as there's no Class selected.
}
/* This is the session's public IdClass that
i can get and set throughout the application. */
public int IdClass { get; set; }
public static Session Current
{
get
{
if (HttpContext.Current.Session[SESSION_FOO] == null)
{
HttpContext.Current.Session[SESSION_FOO] = new Session();
}
return HttpContext.Current.Session[SESSION_FOO] as Session;
}
}
}
I'm trying to architect a strongly typed complex session class with temp data like behavior. I'm a bit confused as to the how to how to set the values to ensure data persistance.
I have a few questions below the code sketch
public class SomeService : ISomeService
{...
ServiceObject _serviceObject;
public SomeService(){
_serviceObject = new ServiceObject();
}
public SomeMethod(int z){
if (ServiceObject.A != null){
_serviceObjct.A = SomeMethodToGenerateA(z);
}
return _serviceObjct.A;
}
[Serializable]
private class ServiceObject{
public string A {
get { return GetFromSession().a;}
set { var sO = GetFromSession();
sO.a = value;
StoreInSession(sO);}
}
prop B ...
prop C ...
prob D ...
private const string ServiceObjectKey = ServiceObjectKey
private void StoreInSession( ServiceObject nO ){
Session[ServiceObjectKey] = nO;
}
private ServiceObject GetFromSession( ){
return (ServiceObject)Session[ServiceObjectKey];
}
public void ClearObject(){
Session[ServiceObjectKey] = new ServiceObject();
}
}
}
q1. Do session variables work by reference? ie. would
public string A {
get { return GetFromSession().a;}
set { var sO = GetFromSession();
sO.a = value;}
}
work instead of
public string A {
get { return GetFromSession().a;}
set { var sO = GetFromSession();
sO.a = value;
StoreInSession(sO)}
}
q2. Are there any additional considerations I should be thinking about for this object? At some point it may have to be moved into an out-of-proc storage. It going to access session a bunch (like up to 12 times per request!)
q3. does this design pattern have a name?
if you get 2/3 questions you win!
Also, I intend to use this as a temp data object
.At end of request in the onactionexecuted method, I'm going to clear the object [ if(!actionresult is RedirectResult), then reset the object]
Meta: (if this question is more appropriate on a forum then on the SO site, please pm me with where I should post it and I'll promptly remove it. I'm kind of aware this q isnt a good s.o. fit.)
How to avoid redundant second query to database with using MVVM pattern on view model:
public class DataFormViewModel : INotifyPropertyChanged
{
private int companyId
public int CompanyId
{
get { return companyId; }
set
{
companyId = value;
RaisePropentyChanged("FindingStatuses");
RaisePropentyChanged("StatusCount");
}
}
public List<FindingStatus> FindingStatuses
{
get
{
return FindingStatusService.GetAvalableStatuses(CompanyId);
}
}
public int StatusCount
{
get { return FindingStatuses.Count; }
}
}
i.e. when CompanyId was changed by DataBinder FindingStatuses will be executed and then StatusCount will be executed, that will execute FindingStatuses again.
I'm not sure I'd bind the property directly to a database operation in the first place. Why not have a local List<FindingStatus> representing the "last fetched" statuses, and then explicitly refresh it?
Apart from anything else, property access is usually expected to be reasonably cheap - making a database call every time you access either of those properties sounds like a bad idea to me.
Like Jon already mentioned, accessing properties are expected to be cheap, something you can do a thousand times without any sideeffect.
I would cache the result of your database access and return the cached object on any following request. Ie
private IList<FindingStatus> _findingStatuses;
public IList<FindingStatus> FindingStatuses
{
get
{
if (_findingStatuses == null)
{
_findingStatuses = FindingStatusService.GetAvalableStatuses(CompanyId);
}
return _findingStatuses;
}
}
And then you would of course have to clear your cache before raising the notification
public int CompanyId
{
get { return companyId; }
set
{
companyId = value;
_findingStatuses = null;
RaisePropentyChanged("FindingStatuses");
RaisePropentyChanged("StatusCount");
}
}
The best way to avoid multiple (and useless) queries to the database, is implement a simple cache layer in the Data Access Layer.
1- Ask the cache if he already has an updated result
2- Else query the database
Here is a cache class you can try: http://www.codeproject.com/KB/recipes/andregenericcache.aspx
I am building a class to store User ID and User Role in a session. I'm not sure how this class will behave when multiple users are on the site at the same time. Does anyone see a problem with this?
public static class SessionHandler
{
//*** Session String Values ***********************
private static string _userID = "UserID";
private static string _userRole = "UserRole";
//*** Sets and Gets **********************************************************
public static string UserID
{
get
{
if (HttpContext.Current.Session[SessionHandler._userID] == null)
{ return string.Empty; }
else
{ return HttpContext.Current.Session[SessionHandler._userID].ToString(); }
}
set
{ HttpContext.Current.Session[SessionHandler._userID] = value; }
}
public static string UserRole
{
get
{
if (HttpContext.Current.Session[SessionHandler._userRole] == null)
{ return string.Empty; }
else
{ return HttpContext.Current.Session[SessionHandler._userRole].ToString(); }
}
set
{ HttpContext.Current.Session[SessionHandler._userRole] = value; }
}
}
The code you posted is the exact replica of some code we have here.
It has been working fine for 2 years now.
Each users access is own session. Every request made to the server is a new thread. Even though 2 request are simultaneous, the HttpContext.Current is different for each of those request.
You'll get a new session for each connection. No two users will ever share session. Each connection will have its own SessionID value. As long as the user stays on your page (doesn't close the browser, etc.) the user will retain that session from one request to the next.
This will work fine for mutiple users accessing your application as there will be different sessionid generated for all deffrent users accessing application concurrentely. It will work in similar way if you have defined two different session variables in your system.
It will be like wrapping tow session states using static wrapper class SessionHandler.