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.
Related
This application is running in a load balanced environment. It uses a SQL database as the session store. The app is simple and does not have it's own database.
I have created an endpoint to be called from another internal server that knows the SessionId. I want this endpoint to be able to grab a session variable by SessionId instead of from the current session.
I know that I could create a new database to do this, but it would be overkill as it would only have 1 table with 2 fields, SessionId and the one value related to it. I could even put this table in the current session database. I am trying to avoid having to add database connection code if at all possible.
Is there a way to access the variables of a different session other than the current session?
I might be wrong and if I am - downvote; the fact that the session state storage is database-based (and not in-memory) does not change the principles of how the IHttpSessionState works. I doubt you will be able to do that and I doubt you actually want to: you simply cannot access an absolutely separate context from a different context. Doing so has a lot of potential issues (thread safety, problems if the session state implementation is changed and so on). Your web application might not be aware of the session state store.
For example, if I simply use session state and have the session state configured directly in IIS, there is no way for the application thread to know it's in the database which immediately presents an issue: what if I decide to run the same application without the DB-backed session state? No changes to application code are required, just IIS reconfiguration. It might introduce unexpected behavior and/or runtime errors.
The suggestion (in the comment) to set the cookie to the known session ID is the only "way out" but from the security perspective it's less than optimal.
However, what you are trying to implement seems like a proper job for a Cache Provider. You can use SqlCacheDependency from System.Web.Caching to use the database for your cache. Then you could use the aforementioned SessionId as one of the cache identifiers.
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.
I'm trying to use the cache in an ASP.NET MVC web application to store some list data that rarely updates. I insert this data into the cache in an UpdateCache() method like this:
HttpContext.Current.Cache.Insert("MyApp-Products", products, null, DateTime.Now.AddYears(99), Cache.NoSlidingExpiration);
Then in my model I retrieve it:
public static List<string> GetProducts()
{
var cachedProducts = HttpContext.Current.Cache["MyApp-Products"];
if (cachedProducts == null)
{
UpdateCache();
cachedProducts = HttpContext.Current.Cache["MyApp-Products"];
}
return ((List<string>)cachedProducts );
}
The first time I visit the page, UpdateCache() is called as expected. If I refresh, the data comes from the cache and I do not need to call UpdateCache(). However, after maybe 15 mins, I come back to the app and the cached values are gone. My understanding was that this cache was per application, not session, so I would have expected it to still be there for myself or another user.
Is there something wrong with the way I'm storing the cache? Or is there something I'm not understanding about how the Cache in ASP.NET works in a web app?
My understanding was that this cache was per application, not
session, so I would have expected it to still be there for myself or
another user.
While the cache is per application, ASP.NET doesn't provide you any guarantees that if you stored something into the cache you will find it back there. The cache could be evicted under different circumstances such as for example your server starts running low on memory and so on. You can subscribe to an event and get notified when an item is evicted from the cache. You can also define a priority when caching an item. The higher the priority, the lower the chance of this item getting evicted.
Also since the cache is stored in the memory of the web server (by default) you should not forget the fact that your application domain could be recycled at any point by IIS. For example after a certain amount of inactivity or if it starts running low on memory or even if a certain CPU threshold usageis reached, ... and everything that is stored in the memory including the cache will simply disappear into the void and the next request in your application will start a new AppDomain.
But in any cases make sure that you check if the item is present in the cache before using it. Never rely on the fact that if you stored something into it, you will find it.
All this blabla to come to the really essential point which is something very worrying with your code. It looks like you are storing a List<string> instance into the cache. But since the cache is per application, this single instance of List<string> could be shared between multiple users of the application and of course this could happen concurrently. And as you know List<T> is not a thread safe structure. So with this code you will, at best, get an exception and at worst you will get corrupt data. So be very careful what you are caching and how you are synchronizing the access to the structure especially if you are caching a non thread-safe class.
IF this is on a full IIS, and happens around every 15minuntes. Remember to check the Idle timeout value.
That being said, if this list "never" changes, why not store it in a static array instead.
I can see how storing data in general in database is better than in static object.
For one, in case data does change, it is easier to update DB than the application.
Try explicitly setting absolute expiration when caching your object:
HttpRuntime.Cache.Insert("MyApp-Products", cachedProducts, null, DateTime.Now.AddDays(20), TimeSpan.Zero );
Note HttpRuntime is used instead of HttpContext for performance reasons even though the difference is minor.
I have a web application developed in .net 2.0
I am storing large business objects in session, which change frequently.
Some times I observe server error. Is this causing the error?
Is there any alternative way to store these objects?
Thanks in advance
Rather than storing business objects in session you would be better of with an architecture that allows you to store the data in a database, and cache data for an appropriate period of time.
Your current architecture using session will ultimately have scaling issues unless you store the session data in a database, so, you might as well get the data from the database in the first place.
Store the data in a database on a user by user basis.
EDIT
Cache the results if you are wanting to read every 5 seconds.
Might not be the solution though.
Based on the data you've provided, it doesn't seem like the issue is performance related.
On the "Object reference not set error". It seems to me that there is a thread synchronization issue. The session object is retrieved and used while/before you are trying to set the session object(s) to the value from your service call. You could add thread synchronization(ReaderWriterLock?) to see if that minimizes the error.
Quick question: Is there a "per-user" data storage object (similar to Session) that I can store data in the global scope (similar to HttpRuntime.Cache)? Almost as if Session and HttpRuntime.Cache had a baby.
Full Background: I have a ASP.NET website that was originally written for a single thread. Now I changed it so that certain actions will spawn a background thread and the browser polls a service to get status updates.
The problem I am having with this is that certain pieces of data are stored into the HttpContext.Session[] object (membership authentication token, for example). These pieces of data need to be unique to each user and accessible to the background thread. Session is not available to the background thread.
I am aware of HttpRuntime.Cache but that would require micromanagement to segment out the users and to expire it at the same time the session is expired. Session, on the other hand, automatically expires this things at the right times that I want it too and is already used by things like the SqlMembershipProvider.
My question is, is there something that behaves similar to the Session but exists in the global scope?
I don't think there is anything like you need out of the box. I would do the following though:
User the application cache
Make the key the user ID or unique identifier
Store a Dictionary or some object list in the value for the user. Use this to store all the data you require.
Consider all prepending something on the user ID if you think there could be a conflict with the user unique identifier (eg domain etc)
Make sure to set an expiry on the cached data similar to the session (eg sliding)
Try passing the HttpContext.Current object to the method on your background thread. You should be able to access the session from the background thread through currentContext.Session assuming currentContext is the HttpContext parameter that was passed in.
See this blog post on how to safely access the HttpContext object from multiple threads.
No.
Since when application pool restarts all backgound activity die I suggest to think about moving user's state to your own database or external storage. Unfortunately you'll lose automatic session management benifits (sliding expiration), but if you need backgound activity it will work better - i.e. you'll be able to move your activity out of IIS process to separate process/machine if needed later.