I've got a Controller, and in one of the ActionResult
s, there is a javascript value being returned that I can access via:
Request.QueryString["frequency"]
But I need to use that same variable in another ActionResult
. How can I do this?
I know that I can't set a
string frequency = "";
at the top of the Controller, and then just set in the 1st ActionResult
.
HTTP is stateless, every request has it's own state and Controller instance.
You can use TempData
which use Session
but delete the value after you read it.
When you say ActionResult
, I assume you mean your Action methods within the controller that return Actionresult
s? e.g.:
public class HomeController : Controller {
// GET: /Home/
public ActionResult Index() {
var freq = Request.QueryString["frequency"];
// ** Want to persist freq here **
HomeIndexViewModel model = FooLogic.BarIndex();
return View();
}
// GET: /Home/Detail
public ActionResult Detail() {
var freq = **Want to access freq here**;
HomeDetailViewModel model = FooLogic.BarDetail(freq);
return View();
}
}
As your question refers to ActionResult, this could be any kind of ActionResult - without knowing exactly what you are doing this answer outlines a general approach to choosing a state persistence store.
The key questions to ask yourself are:
- who will need to see this value - just the user who's request generated the value, or other users as well,
- if user specific, are they logged in, do they have a session?
- how long will they need to be able to see it for (the next request only, or maybe the next day)
- where will they expect to see it (just in the current browser, or in another session)
Your options for data persistence are many and varied, each fulfilling a slightly different role, though many overlap with others in their potential use (e.g. Session
and TempData
). I've listed many of them below, all of which could solve your problem depending on the exact scenario. The first two (View-persisted data or TempData) are most likely to be useful for you, but in the absence of more information others may actually be the ones you need.
There used to be at least Nine Options for Managing Persistent User State in ASP.NET, and many of those still apply in MVC.
ViewData and ViewBag
- Available to: current user within the current request, but you can use it to "store state" within the generated html ready to be passed to future requests
- Only relevant if your ActionResult is actually a View (rather than a redirect, or another ActionResult like a FileStreamResult)
- Lets you pass data from the current controller action into the current view being generated meaning you can insert it into client side objects that could send it back in the next request such as:
- hidden form fields;
- query string parameters for the next request; or
- javascript variables for ajax requests.
An example of this would be to pass your freq
variable into the View for the Index method (for instance by using ViewBag.Frequency = freq;
, and then use it with @Html.ActionLink
. E.g.:
@Html.ActionLink("Click for the Next Action", "Detail", "Home",
new { frequency = ViewBag.Frequency }, null)
Your Detail action then becomes:
public ActionResult Detail(int frequency) { ...
Similar approaches would allow you to use the value in hidden form fields, or in javascript for future AJAX requests by setting a javascript variable using var freq = @Html.Raw(Viewbag.Frequency);
, for instance.
Note: There is a school of thought (which I think is a good one) that you shouldn't be using these, instead you should create a strongly typed class (such as the above HomeIndexViewModel
) for the Model for each view to allow you to better test your Controller Actions and Views.
Advantages of ViewData
/ViewBag
:
- It maintains statelessness on your server, so that you don't have to worry about worker processes being recycled, the next request going to a different server in your web farm etc.
- It is potentially "back button proof" as your "state" sits in the page that has been rendered
Disadvantages:
- Only useful Actions that render html
- Not all data should be back-button proof, some data should constantly evolve and therefore stored as server-side state (e.g. a page hit count)
TempData
- Available to: Current User, within this and the next request
- By default (in MVC 4) this is implemented by
SessionStateTempDataProvider
(see the documentation)
- It is really meant to be used when your ActionResult is a redirect to another Action, that way you know the exact scope and lifetime of the data you have stored.
Ostensibly this is designed to do exactly what you want, but there are considerations.
- It relies on session state, so works for web farm and web garden scenarios only if you have configured Session state appropriately.
- Session state may also not persist between worker process recycles depending on your setup.
- You also have to worry about what happens in a "back button" or F5 scenario as the data may not be available a second time.
Session
- Available to: Current User, within the current session for them. Scope also depends on how you have configured session state (e.g. to be local to the application domain, or database backed and available across the web farm)
This has all the same considerations as TempData
, but you choose when to delete the value from the Session
. It is really intended for general information relevant to the current session (e.g. a simple shopping cart that the user wouldn't expect to see if they close and reopen the browser, or visit the site on their mobile phone later on).
Cache
- Available to: All Users, but only within the current application domain (so beware worker process recycling, web farms, web gardens etc.)
You can access this through the HttpContext property of your Controller. E.g.:
HttpContext.Cache["Frequency"] = freq;
Cookies
- Available to: Current user, but only from the browser they used for the original request
Cookies are often overlooked as state persistence - but they are very useful for some kinds of data. Remember that if the user expects to see data associated with a user identity, then the cookie won't help if they log in on another computer, use an Incognito/Private browser session etc.
Database
- Available to: All users, or just the current user, for as long or short a time as you like - you choose.
Databases are the daddy of state persistence. Your application code should be viewed as volatile, and able to deal with restarts, web farm scenarios etc. etc. If you want to store data, use a database. By "Database" I mean a data persistence medium in any form, from SQL Server to Redis, Azure file storage, Azure table storage, Amazon S3 etc.
Other options
There are other options, they are not commonly as commonly used though. For instance, you can implement your own caching (example here), or use ...
Static or singleton classes
- Available to: all users, all requests to that worker process on that server
- Data will persist within a single worker process only (so this has implications for web farms and web gardens), and only until the asp worker process restarts
- Thread-safety is still a concern, but at least you can encapsulate thread-safety logic within that class
- Very rarely useful due to the coupling to the lifetime of the worker process, and to the single server
What not to use
Controller class fields (instance)
- Available to: current user, current request only
(Disclaimer: I believe that a new Controller is created for each request in all versions of MVC, but if that is not the case then you would never use these to store state)
Theoretically you'll never use these (unless this is a 10-minute application for a demo to your colleagues):
- As instance fields on a class persist only for the duration of the class (therefore the duration of the current request) if you want to store state (data) for the request you should be using variables within your Action method to better convey intent/manage scope of the objects/values.
- So if you are using instance fields in your controller, you are probably sharing data for controller methods you are calling from your Action.
- This means you are probably using your controller to enact business logic.
- Current best practice dictates that Controllers should be a collection of Actions that call business logic (including any generation of view models), (thin, not fat, controllers).
- Ergo: Controller class instance fields indicate you should restructure your code
The times you need instance fields on a controller are when providing common services to all actions, such as IoC interfaces, but these are not storing state within or across requests.
Controller class fields (static)
- Available to: all users, all requests to that worker process on that server
- Not a good idea - static fields will be available to all users on all threads, so you have to worry about thread-safety. There are better state stores available out of the box if you want to share data between all users, such as
Cache