Update: This post was written for MVC 1. TempData behaves completely differently in MVC 2 Beta and higher. In these versions, TempData is cleared only when it is read (or when the session expires).
Many people seem to be confused about the TempData feature in ASP.NET MVC. TempData behaves like the ViewData dictionary, except that it persists until the next request from the same browser.
Because of this, TempData should only be used when you know exactly what the next request is going to be. If you set TempData and your action then returns a ViewResult, then the next request, whatever it happens to be (an AJAX request, another page the user opened in a different tab, etc.), is going to see the TempData value you set, and no other request will see it. I’m guessing that this is never the behavior you want.
Realistically, this means that you should only use TempData immediately before you redirect (in other words, immediately before you return a RedirectResult), because this is the only time when you are sure what the next request from that browser is going to be. I actually think that RedirectData would be a better name for this property. Someone, perhaps Steve McConnell, once noted that you should never include "temp" in a variable name, since variables are, by definition, temporary. "Temp data," to me, is synonymous with "variable." The (functionally similar) "flash" feature in Rails is even more badly named.
If your controller action returns a ViewResult, and you are tempted to put data into TempData, don’t. Use ViewData, instead, in this case.
This may lead to a situation where you might need to have a view look into both ViewData and TempData for a key. One example would be if you have an area in your Site.Master to display error messages on the page. It is likely that one controller action will need to send a message to the view via ViewData (because that action returns a ViewResult), while another action will need to send a message to the view via TempData (because that action returns a RedirectResult, as part of a POST/redirect/GET cycle). In this case, you can use code like this in the Site.Master:
<%= ViewData["Message"] ?? TempData["Message"] %>
Under the hood, TempData works by using server-side Session state, so a distributed session cache like Velocity can be used if your application runs on a server farm. Since the lifetime of data put into TempData is limited to the next request, it is fairly easy to use without worrying about growing the session very large, especially since the most common use for TempData is to store a short error string. But the usual rules on what you can put into a Session apply. If you set the Session state mode to out of process, then the data you put in it must be serializable.
{ 8 } Comments
Xepol, it’s close to impossible, in non-pathological situations, to mess up TempData when doing a redirect, even with concurrent browser requests.
The docs about form data refer to a POST/redirect/GET scenario, which counts as "across requests" to me.
Craig, I’d rather disagree that "close to impossible" is an acceptable way of dealing with reliability issues. This WILL happen sooner or later.
Here is the detailed analysis of the race condition:
http://stackoverflow.com/questions/235665/is-there-a-possible-race-condition-when-using-asp-net-mvc-tempdata-across-a-redir
Craig, I’d rather disagree that "close to impossible" is an acceptable way of dealing with reliability issues.
Wait a second, George; you’re misquoting me.
Nowhere did I claim that "close to impossible" is a way of dealing with reliability issues. Rather, I said that (1) the intended purpose of TempData is for redirects (I have since confirmed this with a member of the MVC team directly), and (2) it’s close to impossible to mess up TempData when doing a redirect, in non-pathological situations. The link you gave, and, in particular, the two comments from members of the MVC team at that link, support this conclusion.
Yes, it is hypothetically possible to mess this up. Hence, using it for something more critical than the display of an error message would be a mistake. But I haven’t suggested that. Certainly, if someone had suggested passing credit card information, etc., this way, I would disagree with that. But that’s a strawman here.
There is not a choice here between a "reliable" method and an "unreliable" method. We should not pretend that there is some "reliable" method of passing data across a redirect. You could put the data in the Session, but I would argue that the application is more likely to get it wrong this way than with TempData, which is built for the sole purpose. In other words, TempData is probably not perfect, but it is probably a better choice than the alternatives.
One more thing: I think it’s arguably a bug that TempData is cleared during an AJAX request. Given that the purpose of TempData is to provide an alternative to ViewData suitable for handling redirects, it’s probably never correct to clear it out on an AJAX request, which will never be a response to a redirect. The framework has a Request.IsAjaxRequest property, which could conceivably be used to determine whether or not clear TempData at the end of the request.
You could use the routeValues:
RedirectToAction("MyAction", new MyValues() { ErrorType = 1, Test = true });
This will put the values in the URL. For small amounts of data, this solves the problem without a race condition.
If you’re really, really concerned about the race condition and want to store more data, or don’t want sensitive server-side only data passed via URL, you could do something like this:
Guid guid = Guid.NewGuid();
Session[guid.ToString()] = myData;
RedirectToAction("MyAction", new MyValues() { g=guid.ToString() });
And then in your view, check the session for a value named with the Guid passed as the "g" parameter in the URL.
As soon as the view starts rendering you could grab the session data, pull it into the view and clear out the session.
If it cannot find that guid then they have either visited the same guid URL twice, or their session has expired.
Like Craig points out, the latter shouldn’t be a problem because if you only use this technique on redirects then your session is not going to timeout.
If the guid is not found, fall back to the default implementation of the view.
However, if you find yourself ever needing to use this situation then you’re probably doing something more deep rooted wrong. Have a think about your whole process - this should be a last resort tool in an obscure situation. Most problems can be resolved in a much purer fashion!
I haven’t considered side issues like session data maximum size, but then temp data maximum size was never discussed, and to be honest I don’t know the details!
Awaiting Hole Picking
Josh, there’s nothing wrong with using query string parameters, as with your first suggestion, except that, as you note, there is a maximum length, and the value is exposed to the end-user. Since the action itself may use query string parameters, it’s hard to know how much you can add reliably. Worse, the maximum length is browser-specific.
As for TempData size and session data size, they are the same, because TempData is session. I would not suggest using session in lieu of TempData, though, because, as I said, TempData already uses session, and using TempData makes your intentions clear.
Thank you, I’ve been looking for this. I need to retain certain amount of data and keep the url clean.
For example, adding products from a search page to a basket where input data can be invalid, or out of stock or whatever. Those which r valid need to be processed and those which r not will be warned. This is a solution.
Jinn
Glad I found this. Found myself losing notice after window.location=blabla.
Turns out I’ve forgot to upgrade Mvc framework.
{ 3 } Trackbacks
[...] it to pass data to an action when we redirect (Craig Stuntz goes so far as to call TempData “RedirectData” for this very reason). If you’re using TempData for any other purpose than passing [...]
[...] requirement is actually quite easy to implement using ASP.NET MVC’s TempData container. Craig Stuntz has a good post on this poorly-named container. In a nutshell, it’s a session-baked store that persists its [...]
[...] can use TempData to store your model (this also will be GET request, but model will not be passed in route values [...]
Post a Comment