I'm curious if there is a way to dynamically change the session ID from the context of a SessionStateStoreProvider (or some other customizable module) in C# for ASP.NET.
I am implementing a custom SessionStateStoreProvider, and I was thinking about augmenting the session ID to tell the store provider where to go look for a session. I've implemented a custom SessionIDManager, which lets me augment newly created session IDs with the required tag. The problem is that the desired value of that tag might change during the life of a session. For example, the session may be read from one location, but may need to written to a different location. In this instance the ID would have originally been tagged for location A, but on writing the store would want to write to location B. The tag should be updated to reflect location B for the next session read.
So, from the context of SessionStateProviderBase override...
public override void SetAndReleaseItemExclusive(HttpContext context,
string id, SessionStateStoreData item, object lockId, bool newItem)
... is it possible to change the session ID? I know that the HttpContext.Session.SessionID property is not settable. Is there some other way to feedback an update like this? Or is there a more appropriate to carry state like this across calls?
I'm new to C# and web development in general, so any suggestions would be appreciated.
After asking this question, I became reasonably certain that it is not possible to change the Session ID dynamically.
Instead of tagging the session ID with the location information, I ended up setting a cookie using the HttpContext passed into the SessionStateStoreProvider interfaces:
context.Response.SetCookie(new HttpCookie(...));
Related
I am trying to get the hang of MVC framework so bear with me.
Right now, the only thing I'm using the session store for is storing the current logged in user. My website is simple. For this example, consider three domain objects, Person, Meeting, and File. Users can log in and view a "members only" profile of a meeting and can add files to it, or view a meeting's public "profile" if they aren't logged in.
So, from the meeting's private profile, with a logged in user, I have a "add files" link. This link routes to FileContoller.Add(int meetingId). From this action, I get the meeting the user want to add files to using the meeting id, but after the form is posted, I still need to know which meeting the user is adding files to. That's where my question lies, should I pass the "currently interacting with" meeting through TempData, or add it to the Session store?
This is how I currently have the Add action setup, but it's not working:
public ActionResult Add(int meetingId)
{
try
{
var meeting = _meetingsRepository.GetById(meetingId);
ViewData.Model = meeting;
TempData[TempDataKeys.CurrentMeeting] = meeting; /* add to tempdata here */
}
catch (Exception)
{
TempData[TempDataKeys.ErrorMessage] = "Unable to add files to this meeting.";
return RedirectToRoute("MeetingsIndex");
}
return View();
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Add(FormCollection form)
{
var member = Session[SessionStateKeys.Member] as Member;
var meeting = TempData[TempDataKeys.CurrentMeeting] as Meeting; /* meeting ends up null here */
if (member == null)
{
TempData[TempDataKeys.ErrorMessage] = "You must be logged in to add files to an meeting.";
return RedirectToRoute("LoginPage");
}
if (meeting == null)
{
TempData[TempDataKeys.ErrorMessage] = "An error occurred. No meeting selected.";
return RedirectToRoute("MeetingsIndex");
}
// add files to meeting
TempData[TempDataKeys.Notification] = "Successfully added.";
return RedirectToRoute("AddFiles", new {meetingId = meeting.MeetingId});
}
Edit:
Based on most of the answers, can any one provide any examples on what kind of data (other than messages) should be stored in TempData vs Session?
TempData is session, so they're not entirely different. However, the distinction is easy to understand, because TempData is for redirects, and redirects only. So when you set some message in TempData and then redirect, you are using TempData correctly.
However, using Session for any kind of security is extremely dangerous. Session and Membership are entirely separate in ASP.NET. You can "steal" sessions from other users, and yes, people do attack web sites this way. So if you want to selectively stop a post information based on whether a user is logged in, look at IsAuthenticated, and if you want to selectively show information based on what type of user is logged in, you use a Role provider. Because GETs can be cached, the only way to selectively allow access to an action in a GET is with AuthorizeAttribute.
Update In response to your edited question: You already have a good example of using TempData in your question, namely, returning a simple error message after a failed POST. In terms of what should be stored in Session (beyond "not much"), I just think of Session as a user-specific cache. Like the non-user-specific Cache, you should not put security-sensitive information there. But it's a good place to stick stuff which is relatively expensive to look up. For example, our Site.Master has the user's full name displayed on it. That is stored in a database, and we don't want to do a database query for it for every page we serve. (An installation of our application is used in a single company, so a user's full name is not considered "security-sensitive.") So if you think of Session as a cache which varies by a cookie which the user has, you won't be far wrong.
The default TempData provider uses the session so there really isn't much of a distinction, except that your TempData is cleared out at the end of the next request. You should use TempData when the data needs only to persist between two requests, preferably the second one being a redirect to avoid issues with other requests from the user -- from AJAX, for example -- deleting the data accidentally. If the data needs to persist longer than that, you should either repopulate the TempData or use the Session directly.
You can use it as per your requirement. A clarification can be,
TempData Vs Session
TempData
TempData allow us to persisting data for the duration of single subsequent request.
ASP.net MVC will automatically expire the value of tempdata once consecutive request returned the result (it means, it alive only till the target view is fully loaded).
It valid for only current and subsequent request only
TempData has Keep method to retention the value of TempData.
Example:
TempData.Keep(), TempData.Keep(“EmpName”)
TempData internally stored the value in to Session variable.
It is used to stored only one time messages like validation messages, error messages etc.
Session:
Session is able to store data much more long time, until user session is not expire.
Session will be expire after the session time out occurred.
It valid for all requests.
N/A
Session varible are stored in SessionStateItemCollection object (Which is exposed through the HttpContext.Session property of page).
It is used to stored long life data like user id, role id etc. which required throughout user session.
TempData and session, both required typecasting for getting data and check for null values to avoid run time exception.
"It doesn't work" isn't very descriptive, but let me offer a couple suggestions.
Under the hood, TempData uses Session to store values. So there isn't much difference in terms of storage mechanisms or anything like that. However, TempData only lasts until the next request is received.
If the user makes an ajax request in between form posts, TempData is gone. Any request whatsoever will clear TempData. So it's really only reliable when you're doing a manual redirect.
Why can't you just simply render the meeting ID to a hidden field in your View form? You're already adding it to the model. Alternately, add it to your route as a parameter.
I prefer to maintain that kind of data in the page itself. Render meetingID as a hidden input, so it gets submitted back to the controller. The controller handling the post can then feed that meeting ID back to whatever view will be rendered, so that the meetingID basically gets passed around as long as you need it.
It's kind of like the difference between storing a value in a global variable before calling a method that will operate on it, vs. passing the value directly to the method.
I would suggest MvcContrib's solution:
http://jonkruger.com/blog/2009/04/06/aspnet-mvc-pass-parameters-when-redirecting-from-one-action-to-another/
If you don't want full MvcContrib, the solution is only 1 method + 1 class that you can easily grab from MvcContrib sources.
The TempData property value is stored in session state. The value of TempData persists until it is read or until the session times out. If you want pass data one controller view to another controller view then you should use TempData.
Use Session when the data need for the throughout application
May sound like a dumb question but here goes.
I instantiate a LIST from my homepage, the list is in a global class file, and returns all the information about the person logging in. the person, could have one or more accounts associated with the site, and therefore i need to code against a default flag to display their default account informaiton. However, i then also need to build their other account information and display this for them.
The additional account(s) are listed in a drop down box. when the drop down box fires off, instead of calling out to the class again, and retrieving all the necessary information, as i've already done this once, how can i store the object, so that it can be used?
I've looked at Session Variables, but this gets a bit messy (I have 35 fields being returned in my list), plus, the Session variables only get set the first time around, not on DDL changed.
therefore, I need a way of having quick access to the object. - what's the best approach?
As per me , Session is the best possible object for your type of requirement and on DDL changed event try to rebind the Session object with new modified values
I need to save a few things in the session. At the moment, I'm using InProc session on my development machine but I want to deploy using SQL server session and then Azure's AppFabric session.
I created an object that contains several properties, all primitive types. I'm handling the read of session like this:
SessionObject TheCurrentSession =
HttpContext.Current.Session["UserAppSession"] as SessionObject;
Basically, I'm wrapping session variables in an object that's serialized and deserialized each time the session loads and saves.
I'm wondering if it would be better to store each property in its native format and have the wrapping object read/save each of its properties in the session as a native type.
So for example, I'd have a method called RecreateSession like this:
public class SessionObject
{
public void RecreateSession()
{
this.SessionObjectProperty1 =
HttpContext.Current.Session["SessionObjectProperty1"];
this.SessionObjectProperty2 = ...;
this.SessionObjectProperty3 = ...;
}
}
I think doing so would prevent the serialization/deserialization process and could make the values accessible directly in other parts of the code (ie. an HTTP module).
Is it worth it to change my session implementation? What are the best practices for this?
Thanks for your suggestions.
Is it worth it to change my session implementation?
Only if it makes it easier for you to use.
What are the best practices for this?
You pretty much are already doing them. Creating a single object to hold several related properties (that are likely to be used together) and storing it in session instead of a bunch of separate session properties makes sense.
I always created a custom object in which each Property refers to a particular session item... I think its the best option.
I want to define a variable or object like session, to stored data about each user, and i want to access this object in many pages,
could u please tell me if there is a way.
thanks
You have onlyy a few choices, really. URL parameters, hidden form inputs, cookies, session (be careful in a load-balanced scenario) or just store/retrieve stuff from a database. RaveDB is bloomin' brilliant for this because it's so fast and document-based.
You can store data in a cookie and then in your codebehind parse the specific cookie into something like a System.Collections.Generic.Dictionary
But you should use sessions.
Edit
IF if's a KeyValuePair<String,TValue> you can set Session[kvp.Key] = kvp.Value;, if not Session["KVP"] = kvp;
If u really don't want to use session use Database mind that this is a slow method...
i've had a few situations where I wanted to minimize/eliminate session storage (due to users being on a horrible wifi connection). to handle this situation I used an encrypted query string with only their ID in the string. in the base page I would decrypt the string and pull information I needed from the database. This information would be populated into objects that I defined and since the objects were in the base page I could access the information from any pages that inherited it.
I have found many questions here about storing values in viewstate, but haven't found a good answer.
I have a situation when i retrieve large amount of data from database. Then i filter and manipulate the data according to my needs (so it is a preety heavy process). Then I put the result inside a list of custom class. For example lets say this class will be Person
List<Person> persons = new List<Person>();
private void FillPersons()
{
//Call to webservice
persons = ws.GetPersonsList();
//Do all kind of custom filtering
//Manipulate the data
}
Now the whole FillPersons() method is a heavy process that returns pretty small amount of data. And unfortunately it can't be moved to SQL and the heaviness is in the process, but that is not the point.
The point is that i need to reuse this data on the page between post backs.
Right now in order to spare the additional call to FillPersons() I mark Person class as serializeable and store the list in the viewstate, that works fine except the fact that the page becomes 1mb size because of the viewstate. According to what i have read, it is not so acceptable approach i.e. it is not secure and it blows the source code making the page heavy etc. (second is what most concerns me)
So it leaves me with a session. However session is persisted not only between postbacks, but much after it, even when user leaves the page. Or worst- the session will end before user decide to postback. So finding the best time span for session lifetime is mission impossible.
My question is what is the best practice to reuse "datasets" between postbacks?
What you guys do in such cases?
Thanks.
PS: hidden fields etc. is not an option.
You can store this kind of data in the Cache. It is application wide, so depending on what you add use the key accordingly.
var key = UserID + "_personList";
Cache.Add(key, personList, null,
DateTime.Now.AddSeconds(60),
Cache.NoSlidingExpiration,
CacheItemPriority.High,
null);
Note that you can never assume that the data is in the cache (it might have been flushed) so always check if it returns null and than refill it.
Viewstate is not a good way of storing large objects. As you mentioned your page size will get bigger and every postback will take lots of time.
I would suggest using cache. By using cache your list wont be saved there till end of session and you can set how much time it should be stored there. For caching you may use HttpCache or some distibuted caching system like AppFabric or MemCached . This nuget package will help using these cache systems.
this link will help how to configure AppFabric.
I should edit with some code to make it more helpful.
https://bitbucket.org/glav/cacheadapter/wiki/Home
var cacheProvider = AppServices.Cache; // will pick cachadapter using web.config ( can be Http, Memory, AppFabric or MemCached)
var data1 = cacheProvider.Get<SomeData>("cache-key", DateTime.Now.AddSeconds(3), () =>
{
// This is the anonymous function which gets called if the data is not in the cache.
// This method is executed and whatever is returned, is added to the cache with the
// passed in expiry time.
Console.WriteLine("... => Adding data to the cache... 1st call");
var someData = new SomeData() { SomeText = "cache example1", SomeNumber = 1 };
return someData;
});
Other than a cache (good idea by Magnus), the only other way I can think of is to keep the results of your heavy operation stored in the database server.
You mention that it takes a lot of time to retrieve the data. Once done, store it in a purposely established table with some type of access key. Give that key to the browser and use it for pulling what pieces you need back out.
Of course, without knowing the full architecture it's really hard to give a solution. So, in order of preference:
Store it back in the database with a unique key for this user.
Store it in a remote cache
Store it in a local cache
Under no circumstance would I store it in the page (viewstate), cookie (sounds too big anyway), or in session.
Have you considered using ASP.NET caching?
You should choose a key that will suite your exact needs and you will have your data stored in the server memory. But keep in mind cache is application specific and is valid for all users.
If the data you process is not often changed, the processing algorithm doesn't depend on user specific settings and it is not critical to always have the latest data maybe this is the best option I can think of.
Store your filtered collection on disk in a file. Give the file the same name as a key you can store in viewstate. Use that key to retrieve the file on postbacks. In order to keep the file system from filling up, have two folders. Alternate the days for which folder you save the files to. That way you can wipe out the contents of the folder that is not being used that day. This method has extremely good performance, and can scale with a web farm if your folder locations are identified by a network path.
I think personlist is a shared object. Does everyone use the same list? You can store on Application.
Application["PersonList"] =persons;
persons = (List<"Person">)Application["PersonList"]
Or you can Store on Static class.
public static class PersonList { public static List<"Person"> Get {get;set;} }
You should write this code to Application_Start on Global.asax file
PersonList.Get = ws.GetPersonsList();
And you can get List by using this code
persons = PersonList.Get;