Are we "doing it wrong"?
A colleague and I are messing around with an ASP.NET page to act as a "portal" to view the results from a diagnostic program on a UniData server. Although we do the odd-job of ASP/ASP.NET at work, it is not our primary language.
To access this server, we have to use UniObjects, which is an API for authenticating and using the UniData server.
We needed each user visiting the website to have to authenticate with UniData and get their own session via the UniObjects library, then be able to use it without signing in again (unless the session isn't used with in 'x' minutes).
The method we have come up with is as follows:
We have a singleton with a Hashtable. It maps Windows username with a session object.
If the user goes to our page and 'username' doesn't exist in the Hashtable, it redirects to a login page where the session object is created and added to the Hashtable if authentication succeeds. Otherwise, it grabs the users existing session object from the Hashtable and uses that for the request (unless it has expired, in which case we remove it and redirect to the login page).
Each session object (which is a wrapper object for stuff from UniObjects) has a "lastUsed" method. We need to clean-up user's sessions since we have license restrictions on users logged into the UniData server, so every time a user gets redirected to the sign-in page, it checks if any sessions have not been used in 'x' mins, in which case it closes that session and removes it from the Hashtable. It is done here so users won't experience any delay related to checking all sessions on every request, only at login.
Something is telling me that this solution smells, but I don't have enough ASP.NET experience to work out what we should be doing? Is there a better method for doing this or is it actually okay?
Since all of your users seem to be authenticated, I would suggest you think about using a different way of managing session state and timeout.
Part of the issue you have is that if a user just closes the browser without logging out, or stops using the application, you have to wait until the session times out to kill it off and free up UniObjects for your licensing issues.
My suggestion is as follows:
Add an invisible IFRAME to your
MasterPage template, or to each page
in the site if you aren't using
MasterPages.
That MasterPage will
load a KeepAlive.aspx page, that
contains a META Refresh, reloading
the page every 5 minutes.
You can
reduce the session timeout to 10
minutes (maybe even 6)
Now, if a user closes their browser windows, their session times out much quicker than usual, but if their browser window is left open, their session is persistent.
A code example and walkthrough can be seen here.
You now need to solution to prevent the user from leaving their browser window open all night and hogging your UniData licences. In this case I would implement a similar methodology, where a stagnant page (i.e. user has done nothing for 20 minutes) is refreshed to a logout ASPX page, clearing the session.
If you are using UniObjects COM, make sure you get your COM marshalling working correctly. Take a look at:
SafeCOMWrapper - Managed Disposable Strongly Typed safe wrapper to late bound COM
http://www.codeproject.com/KB/COM/safecomwrapper.aspx
Another thing to watch out for is that the dynamic array class in UniObjects COM has a threading issue that doesn't play nice with .NET. If you can, use your own dynamic array class or array splits in .NET instead of the Dynamic array class in UniObjects COM.
Sometimes when you try to access the data from the class, it shows an empty string, but when you debug it, the data is there. Don't know the root cause of this.
If you need a generic dynamic array class that works with .NET, I can supply you one.
UniObjects.NET does not have these problems to my knowledge.
Nathan Rector
When you say you are using UniObjects... are you using the COM or .NET object set? The easiest would be to use UniObject Conneciton pooling.
When you create your Singleton, are you storing it in the Application object, Session Object, or Cache Object?
I would suggest Application object, as the Session object is can do strange things. One way to handle and check timeouts would be to use the Cache Key with a CacheRemoveCallback. This way you can use a File/Path Monitor dependency to watch for a windows file change to cause a remove manually, or a timeout from the Cache dependency.
Draw back to this is that timeouts on Cache Dependencies are only driven by page activity, and if the asp.net session recycles, it may/will destory the cache dependencies.
Nathan Rector
Related
I have this piece of code:
var thisUser = Session["user"];
if (thisUser == null)
{
LogFile.Log("Logging out");
Response.Write("xp");
}
I am trying to track down why sometimes when I play with the system for a few minutes and suddenly the user session variable gets null.
It happens randomly in different scenarios.
I do not set the Session["user"] to null at any point.
Session timeout is set to 20 minutes.
I do not call Session.Clear() at any point.
Any ideas\thoughts\things I should look at as to why is it may happening?
I am using Firefox if that to any help.
The system is built with asp.net.
For more info please ask.
are you calling the same host? if the base URL is different the server will treat this as different users. for example:
http://localhost/path/to/resource and http://localhost:80/path/to/resource
both point to the same resource, but the requests are different and the session cookie will be different, or not present.
An easy way to test this is to launch your browser's developer toolbar and monitor the network traffic. compare the URLs to make sure they are the same base path and the same session cookie is passed in the request.
First of all this looks like C# and ASP.NET, not classic ASP. Now if you never clear the session yourself and the server (or the app pool) is never restarted, then the only way to lose the session is to clear the browser's cookies.
Editing the web.config will recycle the app pool, which clears the session info.
I have web page index.aspx and corresponding server side code index.aspx.cs. This C# code has a method which cannot executed in parallel if multiple clients connect to my website. How can I restrict this?
Here is what the method does. It creates a folder, zip it and make it available for the user to download. My requirement is that when one user is executing this method, some other user should not do this because it will create the same folder again which leads to corruption of data.
I tried using Session objects. But I came to know that session objects are stored per client basis.
Can anyone suggest me some solution?
My immediate advice would be: create a random folder name per request, which would allow you to run them concurrently. However, if that isn't an option then you will need to synchronize using something like lock or Mutex. However, this would only work well if you are returning the result from the current request, rather than zipping it in one request, and letting them download it the next.
Frankly, though, I think that you should do the zip in the request for the zip. Indeed, unless the file will be huge you don't even need to touch the file-system - you can create a zip in-memory using MemoryStream and any of the zip encoders (System.IO.Packaging.ZipPackage for example) - then just hand the client the data from the MemoryStream.
If you are using MVC, this is just return File(contents, contentType). With vanilla ASP.NET you need a few more steps.
The Application context or a static class is application wide. So you can store a flag which indicates that the process is already started. After the procees ended, you can delete the flag.
http://msdn.microsoft.com/en-us/library/94xkskdf(v=vs.100).aspx
And always use Application.Lock when you write to the application state and lock(mutex) when you use a static class.
In your case a static class would be a better solution, because it seems that the application context exist only for compatible purposes to classic asp: Using static variables instead of Application state in ASP.NET
static object mutex= new object();
lock(mutex)
{
//Do the work
}
If you use the classic asp.net session you do not need to do anything because session all ready lock the run of the pages from multiple users.
If you not, then you can follow what Marc suggest, use Mutex.
About the session lock:
Web app blocked while processing another web app on sharing same session
jQuery Ajax calls to web service seem to be synchronous
ASP.NET Server does not process pages asynchronously
Replacing ASP.Net's session entirely
Recently I have been asked by a client to log into a legacy site using POST and not GET (from a 3rd party site), All of the needed variables are now sent within a post instead of a query string.
The problem is that upon receiving all of variables they are stored into Session and then redirected to the correct page within the application (from the logo-in Page).
While this works perfectly while calling the page using GET, a POST call will lose all of the Session variables after
Response.Redirect(#"~/SOMEPAGE.aspx",false);
Another thing that is odd is that the Session ID will remain the same but all values will be gone.
When Using Server.Transfer the session is intact but will be lost once the Response.Redirect is used. (there is no option to change all of the code.)
Does any one know of a way to resolve this or some sort of a work around that might be used.
Thanks!!!
There are a few reasons this could happen.
You are using Session.Abandon() in your code
You are switching between a secure (https://) and insecure (http://) URL
You have some code in your global.asax that is manipulating Session, or the .Secure or .Path properties of your Response.Cookie
edit http://forums.asp.net/t/1670844.aspx
I need to maintaing the Session throughout the day? On or before session expires, how do I enforce session throughout the day without giving any session timeout either in IIS or web.config?
Note: Default Session Timeout in IIS – 20 minutes.
Thanks in Advance
The first thing you can do is decouple the session from the process by using a SQL (or other database) session state server that holds the serialized copy of the session data attached to a session id. Then you could use a persistent cookie to pass the session id back and forth. Any session that expires could then be regenerated or reinstated.
Since you're wanting to maintain a session all day (which is innately insecure), we'll assume that most security considerations have already been thrown out. If you're concerned about replay attacks, you could use an HMAC to validate cookie session requests by having the session id, datetime, username (if that's part of your data), maybe IP address in a string followed by a one-way salted hash of this data. This way you rehash the data and compare the hash sent with the request with the generated hash from that data to see if the request is valid.
Forcing the page to be refreshed every 10 minutes or so by javascript. The session timeouts are how long after the last requested page, the session becomes invalid. When you refresh the page every 10 minutes or so, the session is extended for 20 minutes on every refresh.
If you refuse to change the setting in the web.config to a longer value, you could try to force the browser to refresh within a short time of the session timeout (eg at 17 minutes). There might be other nifty ways.
If you can find a way to do without the session, it will save you pain later. Either use a caching strategy (memcached) or look at your resource usage.
eg Do you really need to serialise X into the session?
Use StateServer instead of InProc
This delegates all session handling to a separate database and removes it from the servers process. This will mitigate problems with eating up server resources which then allows you to increase the session timeout to 24 hours.
I believe this is the only viable solution.
Peiter is on the right track -- making fake requests to keep things alive. You definitely don't want to refresh the page, that leads to unwanted updates and such. You don't even need to refresh part of the page. You just need to send an ajax request every X minutes while a user has a browser open to an IHttpHandler that does have session state enabled. This is very, very effective -- we've got one app which is compltely dependent upon sessions for everything that has a 5 minute session timeout. And a session heartbeat that fires every 4.5 minutes. Its been in production upwards of 3 years and we have not lost a session yet. And yes, session state is stored in process in this case -- the use of sessions is so heavy we don't want to wait for them to go out to SQL and back.
PS: protip -- make sure to make the request and response unique; caching kicks in otherwise in certain browsers rendering this trick ineffective.
put a ajax call into a timer, to a method which does nothing "", that's how even the user is on the page but not doing anything means "totally inactive", would not log out, we have implemented the same logic and resolved our problem, our target was 3 to 4 hours, and we did not want to change the value in web.config or using any other way cause of some reasons.
that ajax call would be in a "timer", set the timer time to "any".
I've the following doubt. I've a page "MyPage" and i've declared few dictionary objects in the page class.
My doubt is
If i declare the dictionary as a private non-static object i'm not able to use it across the functions in that page class (the object is getting nulled)
But if i declare the dictionary to be static i'm able to across the object across the functions. But will that object be same across all the users who have opened the page right now (guessing that each user will have an instance of the page in turn has an instance for the page class and static variables of a class are invariable across all the instances of the class)
Is my understanding right? How to declare the object to be available across all the functions within the page class and unique for each instance of the page (user).
Update1
Okie, i find that the initialization of the dictionary object in the page class is done in a different thread (WCF Duplex callback). But in the main page thread the dictionary object is still remaining as null (uninitialized). Any ideas?
Update2
Marshalling - useful by any chance?
Update3
In reply to John-
Thanks for your reply. The problem i'm facing now is to pass the data from the WCF callback class at the client side(which will be invoked in a separate thread) to the asp.net page. So how can i do that? View state may help me to persist the data across requests. But when the callback is invoked by the server notifying for a change how should i pass the changes (data) to the UI thread (asp.net page)?
Don't do things like this.
If you need to maintain data between pages, use Session state. That's what it's for. You should remember that you get a new instance of your page class on every request. Do not use statics to keep changing data around for subsequent requests. You will probably get into trouble with multiple requests updating the data at the same time.
You can't do things like this with ASP.NET!
You seem to be treating this as though it were a desktop program - as though your class instance and all state will still be there for you next time you execute a method on the page. That's not true - when the request is complete, your page will be Disposed. Nothing about the page will remain valid.
In particular, if the callback doesn't happen before the request ends, then the callback method had better not reference anything having to do with the request, like your page. That's because the callback might fire after the request is already over! The state is corrupt or worse.
Unless you are going to have the page wait for the callback, you'd really better not use them in your pages. Instead, create a separate Windows Service or something and have it issue the requests and await the callback. The page can then use Ajax or something to ask if the request is complete, and to get the response once complete.
If you think you heard me say to call back to an ASP.NET page, then you misunderstood.
Create a Windows Service. Your Windows Service will host a WCF service that the ASP.NET application will talk to. The WCF Service will keep track of things like who's joined a chat, who's typing, etc.
The web application cannot be notified when something interesting happens. Instead, the web application will have to poll the WCF service, asking if anything interesting has happened. When something happens, the WCF service will pass the "something" back to the ASP.NET application (or possibly, back to the page, called by AJAX).
I misspoke earlier. You simply cannot use a callback contract at all in this situation. It's not like the web pages are like a desktop application, one per user, waiting to be notified. They're more like a desktop application where, when the user makes a request, you take his PC and give him a new one just like it, before the response arrives.
You are right in the second case. In your first case I'm guessing you mean that if a user clicks multiple controls on your page then the event handlers are seeing that the dictionary is null (instead of having the result from the previous event handler).
Remember that every request on a page (even from the same user) creates a new instance of your page class. That means that each time a request starts, your dictionary will be null. The only way for a variable to maintain its value between subsequent requests is to persist it server-side (for example, in user-specific session information on the server) or to push it to the client with the page content so that it can be part of the subsequent request data (so it's stored in ViewState or other storage at the client's browser between requests).
Rereading this question, there are three seperate state machines, and none of them are being coupled together - hence the problem :)
State of the "user state" - these are the key/value pairs in the dictionary, their lifetime spans multiple page requests and callbacks
State of a "page", which needs to consume the data from "user state". Pages are destroyed after each and every page request.
State of the "service call" which needs to populate the data in "user state" Service calls are generally destroyed after each invocation.
There are a few strategies that could enable you to couple the systems:
ViewState such that the state machine for "user state" is sent down as part of the state of the page, and sent back on postbacks. This may constrain how you perform service callbacks
Session such that the state machine for "user state" is stored server side, and can be accessed by key.
Static dictionary of user states, where the key for the outer dictionary would be the identity of the "user state", where the 1st page request would create the "user state" entry, and you'd need to manage teardown. (v.similar to session - though works without ASP.NET).
There are lots of nuances to each solution - I'd advise light reading :)
you are right that a static member will be the same for all instances of the page, and thus all individual users. You need to make it a non-static member if you want to access it from every method in the class. You should look into why the object is null. Are you properly instantiating it at the proper time?