I found that I shouldn't use Session a lot in ASP MVC here, here and in other places.
So, I want to know if it's better to use TempData like I did below or not.
public ActionResult Action1()
{
if (SomeCondition)
{
/*
I want to show alert to user based on this value that should appear in Action2 view
So, is it better to:
1. Session["user"] = "something";
2. TempData["user"] = "something";
*/
return RedirectToAction("Action2");
}
return View();
}
public ActionResult Action2()
{
/*
1. I can read Session["user"] in the view
2. TempData["user"] = TempData["user"].ToString();
Now I can read TempData in the view
*/
return View();
}
TempData is a provider that uses Session by default. It can be changed to be a cookie-based provider, though.
The only real difference is that TempData stores the data only until it is read again, where Session will store the data until a timeout expires.
There is no perfect solution for storing data between requests. When possible, you should avoid it. In MVC you can do this fairly easily by loading the data into the View and posting the ViewModel back to the controller where you can read the data again.
Also, see Think twice about using session state for some possible alternatives to session state.
Related
Hi I cant find the way to pass data between Action methods within one controller. The simplified code example is as follows:
public class HomeController : Controller
{
int latestID;
// GET: Home
public ActionResult Index()
{
latestID = 50000;
return View();
}
// GET: Generate
public ActionResult Generate()
{
// using the latestID here
return View();
}
}
Index loads when the app starts and latestID is set to 50000. But when I click button mapped to API GET request Generate, the latestID is suddenly null. I tried to implement TempData and Session but with no luck. Any help would be greatly appreciated!
I feel there are 2 possibilities here i.e. the value to be retrieved is for a user session or its global across all the users using the website.
If the value is user specific then it can be managed through session stage management like Viewbag. Or if this value is expected to be maintained on the server side alone then it needs to be retrieved again through some persistence mechanism like database or memory cache.
If the value is common across all the users using the Website then it can be achieved through the Dependency Injection of a singleton object (this can be a database manager or cache manager again or a simple in memory object). The static objects can be used as well but it wouldn't be ideal as the application wouldn't support horizontal scalability across instances.
Yesterday i was working on my asp.net mvc application where i was trying to cache some data in a field while a certain controller was in scope. The field kept clearing every time a new view got opened.
Question:
Is it possible to keep your Controller in scope while you're browsing Views that are handled by said Controller?
private static List<string> _listOfStrings;
[Authorize]
public ActionResult ToView1()
{
_listOfStrings = new List<String>(){"test","test2"};
var model = new Model();
return View(model);
}
[Authorize]
public ActionResult FromView1ToView2()
{
var model = new Model(_listOfStrings);
//the issue at hand is that '_listOfStrings' is not persisted.
return View(model);
}
Anyone got an idea if this is possible? (The list is big, so i would prefer not sending it through the model into the view and vice versa)
You need to read about an ASP.NET MVC lifecycle.
Short answer: no, you can't cause Controller is destroyed after a call of an action. Detail answer: you can store (cache) data in a TempData or in a Session properties.
_listOfStrings is a different variable to listOfStrings. You are not writing to or reading from the static field. In the code you have presented, _listOfStrings is not even defined.
Use one static variable and this code will work.
I have 8 pages labeled Step0-Step7 that are used to save data incrementally to a model called dr405. After Step7, I need to display an Upload.cshtml that creates a folder based on DR405Profile.CurrentUser.TangiblePRopertyID from my custom Profile provider. So, as of right now I'm not posting anything from from Step7 to the Upload.cshtml. After Upload.cshtml I display an UploadSummary.cshtml that simply list the files located in the directory based on DR405Profile.CurrentUser.TangiblePRopertyID. Now, I have to take the user to a review page that displays the DB persisted data for the dr405 model. Does this mean I have to pass my model through Upload and UploadSummary as well even though those pages don't interact with the model?
My plan is to pass the ID as a hidden parameter from
step7 -> Upload -> UploadSummary -> Review(id) <--post accepts ID as parameter. I'm not sure if this is the best way.
Important point
I want to understand if I can do the same with the model
Step7(model) --> Upload(model) -->UploadSummary(model) -->Review(id Or Model)
public ActionResult Review(string id)
{
var service = new DR405Service();
var dr405 = db.dr405s.FirstOrDefault(d => d.TangiblePropertyId == id);
return View(dr405);
}
public ActionResult UploadSummary()
{
var saveLocation = Path.Combine(Server.MapPath("\\"), "returns\\" + DR405Profile.CurrentUser.TangiblePropertyId);
ViewData["files"] = Directory.GetFiles(saveLocation).ToList() ;
return View();
}
[HttpPost]
public ActionResult Upload(HttpPostedFileBase uploadfile)
{
var saveLocation = Path.Combine(Server.MapPath("\\"), "returns\\" + DR405Profile.CurrentUser.TangiblePropertyId);
System.IO.Directory.CreateDirectory(saveLocation);
uploadfile.SaveAs(Path.Combine(saveLocation, Path.GetFileName(uploadfile.FileName)));
ViewData["UploadStatus"] = String.Format("File name: {0}, {1}Kb Uploaded Successfully.", uploadfile.FileName, (int)uploadfile.ContentLength / 1024);
return View();
}
You have a few options to persist the data across requests.
You can use the MVC TempData feature. You can use the Peek/Keep feature of TempData to keep it around until you need to dispose of it (as by default once the data is accessed it is deleted).
Session would also work, but isn't recommended due to it being harder to test in a unit test.
This is perfectly fine. Taking in the ID of a database entry is standard practice. It requires less bandwidth, allows MVC to resolve the correct route/controller/action faster, and doesn't give bad guys as much surface are for sending malicious data to your server.
Hi,
I have a action that looks like this :
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Register(AdRegister adRegister, IEnumerable<HttpPostedFileBase> files)
The AdRegister is a complex class and I need to pass this in to a redirect method further down in the Register action, like this :
return this.RedirectToAction("Validate", adRegister);
The Validate action looks like this :
public ActionResult Validate(AdRegister adRegister)
I do know that I can pass simple parameters but in this case itĀ“s a complex object. This example do not work, the adRegisterĀ“s properties will be null.
Is this posible and if so, how?
BestRegards
More Information : Register action will take the adRegister and do som magic on it, then It will be sent to the Validate action. The Validate action will return a validation page to the user. When the user hit the grant button the adRgister will be filled from the form and then sent to the vValidate post where it will be saved. I have looked in to place the adRegister in cache or database temporarily but it will be better if I could simple pass it to the next action.
One possibility would be to pass the simple properties in the query string:
return RedirectToAction(
"Validate",
new {
foo = adRegister.Foo,
bar = adRegister.Bar,
... and so on for all the properties you want to send
}
);
Another possibility is to store it in TempData (for the lifetime of the redirect) or Session (for the lifetime of the ASP.NET session):
TempData["adRegister"] = adRegister;
return RedirectToAction("Validate");
and then retrieve it from TempData:
public ActionResult Validate()
{
adRegister = TempData["adRegister"] as AdRegister;
...
}
Yet another possibility (and the one I would recommend you) is to persist this object in the POST method in your datastore:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Register(AdRegister adRegister, IEnumerable<HttpPostedFileBase> files)
{
...
string id = Repository.Save(adRegister);
return RedirectToAction("Validate", new { id = adRegister.Id });
}
and then fetch it from the data store after you redirect:
public ActionResult Validate(string id)
{
AdRegister adRegister = Repository.Get(id);
...
}
an idea would probably create a session variable and pass around a Key that references that session variable if the object is required acorss a few views?
ASP.NET MVC's tempdata should be perfect for this.
That said, TempData or Session is one option, but has some downsides like being quite violate and oftentimes murky or difficult to debug. What might be preferable is to "stash" the temporary value in a persistent store, such as the user's profile or your own database, then pass a key through the validate method which can then load the data from said store. This also opens up the possibility of recovering abandoned carts and such.
I'd like to take data entered in an MVC user form and display it in a different view.
The class has the following private variable:
IList<string> _pagecontent = new List<string>();
The following action accepts a FormCollection object, validates it, and passes it on to the "Preview" view as a List:
[Authorize(Roles = "Admins")]
[ValidateInput(false)]
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult UpdateContent(FormCollection collection)
{
if (ModelState.IsValid)
{
string PageToInsert = collection["PageToInsert"];
string PageHeader = collection["PageHeader"];
string PageBody = collection["PageBody"];
//validate, excluded...
_pagecontent.Add(PageToInsert);
_pagecontent.Add(PageHeader);
_pagecontent.Add(PageBody);
}
return RedirectToAction("Preview", _pagecontent);
}
The Preview view has the following Page Directive for passing a strongly typed object List:
<%# Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Template.Master" Inherits="System.Web.Mvc.ViewPage<List<string>>" %>
I would expect to be able to use the Model object to get my data, but alas I cannot. At the following line, I get an error index out of bounds exception, stating the index must be non-negative and less than the size of the collection:
<% if (Model[0].ToString() == "0") { %>
And some strange parameters have been added to the URL, as it resolves to
http://localhost:1894/Admin/Preview?Capacity=4&Count=3
So I have two questions:
When I call RedirectToAction and pass it my List, why is it inaccessible in the view's Model object?
What is the proper way to go about doing what I'm trying to do, namely pass a collection of strings to a view for display there?
Try using TempData. It is like a single-shot session object. You put values you want into TempData, immediately redirect and get them out. There is a good writeup here: http://blogs.teamb.com/craigstuntz/2009/01/23/37947/
Be careful when using TempData. It works great in a single server environment but in a cloud environment it may not work as expected since you cannot guarantee that the request will hit the same machine. This happens because TempData rely on the asp.net session. But if you are using other session manager like SQL or AppFabric Cache it will work fine.
The second parameter to RedirectAction is routeValues, not model.
protected internal RedirectToRouteResult RedirectToAction(string actionName, object routeValues);
Try using TempData for the model. Its for persisting data between redirects.
The problem with RedirectToAction is it's returning a HTTP 302 and the browser is then on it's own going and doing a brand new HTTP request. You may want to consider using a cookie and/or session object to persist the data between requests.
This is not working because RedirectToAction is actually sending back a Http 302 to the browser. When the browser receives this 302, it does a new request to the server asking for the new page. New request, new temp variables.
You will also face this problem when you try to save/edit/delete something and for some reason you deny it and you have to return the old form again.
So, instead of:
return RedirectToAction("Preview", _pagecontent);
Put the Preview logic in a separate method and just call it:
return PreviewLogic(_pagecontent);
You can also use the TempData[] dic to persist data for the next request like others have said, but then you will not avoid the 302 additional round trip to the server.
It sounds like you're trying to do:
public ActionResult UpdateContent(FormCollection form) {
...
return View("Preview", _pagecontent);
}
Note that a redirection is supposed to be a "clean slate" for the browser (except for things like the auth cookie). You don't get to tell the browser to pass information along to the next request, since the next request should be able to stand on its own. All you get to do is tell the browser what URL to request next. In ASP.NET MVC, when you pass an arguments-object to RedirectToAction, the public properties of that object are appended as query-string parameters to the generated URL.
Can't you just make 2 action results with the same name and mark 1 of them with HttpPost?
public ActionResult UpdateContent(FormCollection preview = null)
{
return View(preview);
}
[HttpPost]
public ActionResult UpdateContent(FormCollection collection = null, bool preview = false)
{
if (preview)
return UpdateContent(collection);
else
return UpdateContent(null);
}
It looks like you are looking for the UpdateModel command:
Check out ScottGu's blog post on the topic:
Improved UpdateModel and TryUpdateModel methods