I am having an issue with my session variables. I am adding objects to session, so that when i navigate to the next page (building a sort of questionnaire) i can retrieve these values in another Controller action, and use the stored value to make some calculations.
It is also said that this functionality is used for the user to go backwards through the questionnaire, should they wish to change an input to get a different outcome. However upon going backwards, the variables are gone, and my controller action code fails and brings back a null reference exception.
Any thoughts on why this is doing this? I am adding the the variable to session using following way:
Session["UserInfo"] = myObject;
and retrieving like so
InputData data = (InputData) Session["UserInfo"];
any reasons why i'm losing these objects. If i go back and click submit again , they're back in session and usable on the following page. I don't think this is a session timeout issue as i do it well within 20mins, usually like 30secs (as i'm developing the system and testing and making changes etc.)
I have solved this, my issue was the GET and POST requests on my action controllers. I was not specifying two controller actions explicitly (HTTPGET and HTTPPOST) so all requests going to the same action, and therefore was over-riding the session variable with NULL as nothing was being posted, as a ajax get request.
Setup up two action results HTTP POST and HTTP GET one with input parameters and the other without. Never crossed my mind, until had a cup of coffee and a fresh brain.
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
We developing Web Application using mvc4. In many scenario we will get value from user in First Page/View which we need to keep in some varaiable until user reach Final Page/View. He/She may pass 4-5 views to reach final view from first view.
To keep Value in MVC. We had 3 ways.
1. Global Variable - But if i assign value in one action method. the value will be reset in another action method. So we dropped it.
2.Session - But we need to keep more then 5 values in each session. So we dropped it.
3.Static Varibale - Which works like charm. but in Multiple user it caused concurrency issue.
Is any other ways in mvc to keep values? please guide me.
Static variables will persist for the life of application domain, that is why you are seeing the concurrency issues with multiple users.
See: Static Variables and their implications in ASP.Net websites
There shouldn't be any problem in storing five values in a session. You can have List<T> and store that in session. Like:
List<string> someValues = new List<string> {"A","B","C","D", "E",};
HttpContext.Current.Session["userValues"] = someValues;
To retrieve it:
var someValues = HttpContext.Current.Session["userValues"] as List<string>;
if(someValues != null)
{
// found
}
The only thing you should consider is the size of data. Sessions are stored at server level for each user, storing too much data could cause a problem, though it depends on your configuration.
You may also see: What is ViewData, ViewBag and TempData? – MVC options for passing data between current and subsequent request
I've some code like the following;
We're going to create a Note but we may know the CustomerId when we do so I've two URLs;
public ActionResult CreateByCustomer(int id)
{
Session["ncAppointmentId"] = 0;
Session["ncNoteDate"] = null;
SetConsultant(0);
return RedirectToAction("Create", "Note", new { id = id });
}
public ActionResult Create(int id = 0)
{
int _CustomerId = id == 0 ? Convert.ToInt32(Session["CustomerId"]) : id;
Session["TemplateIds"] = null;
and so on.......
ViewBag.CustomerId = _CustomerId;
When I look at the performance in Firebug the RedirectToAction causes a GET with a '302 Found' status which can incur up to 1 second's delay.
If I change the RedirectToAction line to
return Create(0);
Then the GET doesn't happen and performance is improved.
But I'm looking for opinions on what the downside is or what I've missed ?
The RedirectToAction result tells the client to request a new page, so it will naturally incur overheads since the client is now having to make a second request to your server. Sometimes this is necessary, sometimes - as in your example - not.
Generally I use RedirectToAction to return the client to a specific page after performing an action such as creating a note. This can be useful if you're on a page that lists notes and you want to refresh the page after creating a new one. The end result is that the page is refreshed, and the Create action does not appear in the user's browser history.
If your Create method returns a View, there can be some interesting side effects of calling it directly. In general the MVC code will handle it, but you can get some weird results - like the client's URL being different to what you expect in the subsequent requests, etc. If you're OK with this, fine.
Another option would be to get rid of the CreateByCustomer action and simply call the Create view with a parameter - named customerID for instance. This gives you the same ability to call it different ways without having to have multiple entry points. The client's location would reflect (in the query string) the difference between Create and Create?customerId=12345 which may or may not be what you're after.
<opinion>
Some Style Notes:
If you're storing lots of session data, create a class to hold it instead of creating lots of entries in Session[].
It's not particularly difficult to use jQueryUI to create an in-page editor for your notes rather than defining a view - check out this example. More elegant too :P
</opinion>
The RedirectToAction method is going to return an HTTP response that has a Found status code and a Location URL pointing to the place you are redirecting the client. The cost is simply another GET request, which I would not consider expensive. The decision to redirect or not should be made based on whether it conceptually makes sense, not based on whether you are making one less GET request.
I don't entirely understand the motivation here, however. If you elaborate on why you are trying to redirect, maybe I can help you choose a pattern that makes more sense.
Typically, you would not name a method Create* in an HTTP API. The idiomatic, correct prefix is Post* or Put*, depending on whether you are adding a new resource (but naming it) or creating/replacing a resource (and naming it), respectively.
The big difference is regarding if you want the url to change to the "Create" one. If it's ok to show whatever you are showing with that url, then avoid the redirect. Redirect is useful when you have an old url and you want it to point to a new one and also in the situation when you want to avoid saving new stuff if the user refresh de page (as it will refresh only the redirect request and not the post).
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
In a controller action I receive a variable from a redirect in a TempData variable
public ActionResult ChangePassword()
{
string t = (string)TempData["myVariable"]; // works ok when coming from the redirect
[..]
}
As I need to persist that datum for another call, I try to reassign it before returning the view.
public ActionResult ChangePassword()
{
string t = (string)TempData["myVariable"];
[..]
TempData["myVariable"] = TempData["myVariable"];
return View();
}
I immediately submit a POST request from the rendered page back to ChangePassword, but this time TempData["myVariable"] is null. Maybe I'm doing something stupid, but how to get the wanted result? I don't want to use a Session variable (it would persist much longer and I'd be working on ensuring manually that the variable is cleared to prevent the pollution of Session variables). I could repost it via the form (a hidden variable) but I'd prefer to keep the variable only server-side.
I think you're looking for TempData.Keep()
TempData only persists within the context of the current request. If you are returning content to the client, and then the client is posting back, you can't use that. Your options are pretty standard, and basically only as you described:
Use a form variable (as you stated - and I'm guessing if it's a change password field then it may be sensitive)
Use a session variable (as you stated also!)
Persist the variable elsewhere in your application - custom database field or user profile or similar
Personally I'd go with a session provider, or try to avoid returning content to the client with the immediate post back altogether, if possible...
If myVariable is not a critical information security you can persit it to Hidden field (change the view) and post it to next action request.