Uniquely identify two instances of one browser that share Session state? - c#

I want to ensure a user isn't editing the same form data in two different browser tabs or windows (of the same web browser instance). The intention is to stop the user from accidentally overwriting their own data as they continue through a very long form process. On the server, ongoing data input through the screens is collected into the Session.
Assume for any browser, all tabs and windows run in the same instance of it (i.e. not each in a separate process). Obviously the browser tabs and windows share the same cookies in this scenario so cookie modification seems out of the question for viable solutions. This is also the reason they are sharing the same session state.
Considering that the form is already created and this is one of the final touches, how can I use ASP.NET, hopefully easily, to oversee this
"feature"?

You could try something like this:
Store an integer as Session["LastRequest"]. Put this into a hidden field on the page. For every request, you add one to the integer.
On postback, make sure that no other request has been made by checking that Request.Form["LastRequest"] is equal to Session["LastRequest"].
If you need to check for multiple instances before the postback happens you should be able to do so using AJAX calls.

Every time you render a form, set the value of some hidden field to random string. Store the same string in Session state. When the user posts back, check if the two values are equal. If not, this must be a re-post.

You cannot distinguish two http POSTs for the same page even if they are from different tabs.
It's like the famous back-button problem - they can post, press back, and repost.
The usual solution is hidden tracking fields, but it's very hard to make them reliable.
If it's a wizard-type process, it should be simple to detect if they are overwriting fields that have already been entered, and show a warning.

During the rendering of your specified page, generate a GUID and save in session. Write a generic handler, that keep track that for a specified page, no two GUID exists.
Following data structure will help.
class MultipleOpenedPage{
string guid;
string pageURL;
DateTime timeStamp;
bool IsMultiplePageOpened(List<MultipleOpenedPage> list)
{
///logic
}
}

Because of the stateless nature of the web, I don't believe there is a reliable way to differentiate between two browser windows. However, you can store a flag in Session that a given long running process is in progress. In this way, you don't care if they try to rerun the process from the same browser window or multiple browser windows. The trick is going to be handling situations where the process fails and doesn't get a chance to reset the flag so that the process can be run again.

I got round this by creating a base class inheriting from System.Web.UI.Page and in the page_load/init event, creating an object containing information specific to the user instance.
This was, each new page that's created get's it own instance of the object and can therefore maintain different states/properties which can be used to make distinct edits to data on the same page.
Just a thought as it's a bit of a different way around things.

Related

Do I need a ConcurrentDictionary? Will regular Dictionary do?

I currently am using a ConcurrentDictionary to hold a collection of login names that have authenticated with my API. I do this to prevent duplicate logins from other web clients (a requirement of the system architecture). If a user authenticates with a login that is already "logged in" they are given a choice ...
Continue and the previous login will be expired
Cancel and the current session will be logged out
I am using a ConcurrentDictionary because it is supposed to be thread safe which is important in an environment where multiple clients are accessing the API.
What I am asking is if the ConcurrentDictionary is needed because I am running into trouble deleting all items in the collection that match a given key. Is a ConcurrentDictionary called for in this case? If not, would a plain Dictionary suffice? If not, and a ConcurrentDictionary is needed is there a method that will remove all entries matching a given key? All I can see is TryRemove() which only seems to remove a single entry.
The direct answer to your question:
Yes, you need a ConcurrentDictionary. You are sharing state across several threads.
Remember, a dictionary has one entry per key. That's the definition of what a Dictionary is, and a ConcurrentDictionary doesn't change that.
A fuller and more complete answer to your requirement is below.
The whole solution is short sighted as you have no connection with the session infrastructure to know when a user's session has timed out and effectively caused them to be logged out. Additionally there is no coordination with other instances of your app if you ever think about deploying to a cloud platform that spins up new instances.
In other words, you are putting yourself in a situation that makes it very difficult to scale your app without breaking this feature.
Probably one of the most robust ways of handling the single session requirement is to use your database:
Have a field that keeps track of the last session ID your user had when logging in.
Add a session listener to clear the field when the session times out
If the session ID is not the same as what's in the field, you know you have a new login attempt.
If you need complete control over the session ID, then supply your own session id manager (may be necessary to include an encoded server ID in it).
You'll find that the requirement is much more involved than it sounds on the surface. You can't think like a desktop application in the web space--which is precisely where this requirement even comes from.

Setting session variable causes slowness in FileContentResult rendering in MVC

Here's my predicament. I have a page in an MVC app that's displaying a list of search results via a partial view using an ajax call. The model is a List<List<string>> representing a dynamic data set, i.e., the users choose which columns they want returned and what order they come back in. All the view is doing is a nested loop which builds out the results table.
One of the potential returned fields is an image of a barcode which is being rendered by another method returning a FileContentResult. Normally this works great, it's slick and performant, to the point where I don't really notice all of the barcodes being rendered and downloaded at all, even in a data set that's hundreds of rows long.
The problem arises when I set a session variable using HttpContext.Current.Session, even something as simple as Session["thingy"] = "thingy";. When that happens there is a drastic performance hit with the barcode images. Result sets that would take a second to load fully are now suffering from image "pop in" for up to 10 seconds after the search button is hit. A few times an image has failed to load, giving an error to the effect of "the server is too busy right now".
Does anyone out there in overflowland have any insight into what could be causing this behavior? I've found a kludgy workaround but it involves unnecessary ajax calls and extra trips to the database.
So the issue was that IIS was treating requests synchronously whenever there was anything stored in the session. So all of my calls to the barcode action were waiting until the last one had finished before moving on, hence the pop-in.
The answer was in this link posted by Alexei. Oddly enough it was the most downvoted answer that provided the easiest solution. I created a new controller for my images and refactored the barcode rendering action into it, then decorated the controller with [SessionState(SessionStateBehavior.Disabled)], forcing IIS to treat any requests to the actions in the controller as asynchronous.
I was having the same issues a while ago. Fixed it by setting EnableSessionState to ReadOnly in my web.config.
I thought it might have some negative side effects but none so far. Even posted a question here in SO looking for comments.
See here: EnableSessionState = ReadOnly - possible side effects?

How to pass a complex object between pages in a Windows Store App?

What is the best approach in passing a complex object between Pages in a Windows Store App? In the MSDN it states that only basic types of objects can be passed in order to serialize the session state, but I don't intend to serialize session state since my app will just log out and ask for log in credentials when the app is launched the next time. I plan to serialize my data when the user either hits the log out or save buttons.
Let's say that you are passing a Person object to the PersonDetailPage. Then here is what you would have on your MainPage (or any other page).
Assuming that you are working with a GridView:
this.Frame.Navigate(typeof(PersonDetailPage), (Person)e.ClickedItem);
Then on the PersonDetailPage, on the LoadState() you will verifiy if a parameter was passed, and then atribute that parameter to a variable.
protected override void LoadState(Object navigationParameter, Dictionary<String, Object> pageState)
{
if (navigationParameter != null)
{
person = (Person)navigationParameter;
}
}
Not serializing the session state could result in a suboptimal experience for your user. If you only save the state when the user logs out, it will loose state when the application suspends and is terminated by the OS because of low resources.
Take a look at the application lifecycle: as soon as the user switches to another application and your application is not in front any more, it is going to be suspended - this is your last opportunity to save the state and preserve it. Although your application can be left in memory until the user switches back, it can as well happen that the OS will shut it down if it needs resources. In this case the user will have to restart it. If you don't save your state when the application is suspended, you will loose it and the user experience will suffer.
Although you can probably come up with a custom way of preserving the state, I do suggest you stick with passing only basic type between pages which are supported by Windows runtime. This way you can take advantage of SuspensionManager included in the application templates which takes care of most of the work. It's not that difficult to pass only basic types between pages. It's probably best you only pass object ids (unique identifiers of some sort) and keep the objects somewhere else where they can be accessed by all pages. Alternatively you could make your objects serializable and pass them as serialized strings (either XML or JSON).
You can serialize your complex object, pass it as a string then deserialize the string at the second page

Threading in ASP.NET MVC 3

I am currently converting a Windows Phone 7 application to its web counterpart. The application uses a big main thread from which the data is gathered, and for the moment I have just copied and pasted it as is (just a standard thread operation), in my ASP.NET MVC controller.
Sync _Sync = new Sync();
_Sync.StartSync();
The tasks work OK, but because the thread makes use of global data set from the cookies, issues arise when accessing the page with 2 different usernames. For example, if I login with "user1" in Firefox and then try to login in Chrome with another user (say "user2"), then it will automatically change the async data gathered for the first user; meaning that I will always see data pulled out from the last user logged in (regardless of the fact that I was just logged in in Firefox with another user, initially), and not each others' separate data.
In other words, the thread doesn't start separately for each individual user. How could I fix this behavior?
Static fields and properties are shared across threads and should generally not be used to store data that pertains to a specific user or web request.
One of the following suggestions should fix your threading issues and keep your user data separate.
Create an instance of the Sync class for each request and remove any static fields / properties from the class.
Add a method to the Sync class that returns an instance of the data for a specific user instead of storing the data in static fields / properties.

Persistent Controller Members

I have a controller that needs to persist the state of a Dictionary member. On navigating to a specific action, an entry for that user is created and added to the dictionary. I require a separate ajax action to pull the object out, use it, and save it back, however it seems as though between the two events my dictionary is being collected.
Now I've tried a number of things to ensure that this dictionary stays put but to no avail. As a requirement, I need this dictionary to stay resident in memory for quick access. I understand that MVC is supposed to be stateless and I should use a different kind of backing store. Suggestions?
From your description putting the dictionary into the Session should be an option. The session store has the following properties:
Stays alive between requests.
Complete separation of different uses/sessions.
Easy to set up - no database or file to configure.
Quick access since it is normally handled in memory.
Not suitable for large amounts of data.
The session store is cleared if the application pool is recycled (which happends sometimes).
There are many backing stores available:
Session
Files
Database
...
Up to you to pick one.

Categories