Can't pass viewmodel to new action by RedirectToAction - asp.net-mvc

I have a form, when user submit the form, I want to direct the user the new view to display the submitted result(transfer viewmode data to display view).
public class HomeController : Controller
{
private MyViewModel _vm;
.....
// POST: /Home/Create
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(MyViewModel vm){
//.....
//set up vm to temp data _vm
_vm = vm;
return RedirectToAction("DisplayData");
}
// GET: /Home/DisplayData
public ActionResult DisplayData()
{
//get temp data for display
return View(_vm);
}
}
When I posted the form, I can create vm and put it to temp data place _vm. But this _vm can be sent to another action DisplayData, it's null in action DisplayData(). It seems that when redirect action even in same controller, _vm is lost although it is Controller var, not action method var.
How to resolve this problem?

It creates a new instance of the controller as it is a new request therefore as you have found it will be null.
You could use TempData to store the vm, TempData persists the data for 1 request only
Good explanation here

One good way is to call
return DisplayData(_vm)
instead of
RedirectToAction("DisplayData")
DisplayData should accept a model anyway.

Related

What Views can a Controller Action return?

In ASP.NET MVC 5, does every Controller Action have to return a View with the same name as the Controller?
Here's my project. Have a webpage which contains a button to upload an image to a database. When the webpage is loaded, I want it to display a list of all the images that have already been uploaded. So, the Index (default) Action for this Controller loads the images from the database, and returns the Index View, which in turn displays the list of images:
public ActionResult Index()
{
// Load the images from the database
var images = GetImages();
return View(images);
}
On that same webpage, there is a button which allows the user to upload an image to the database. That button calls the Upload Action, which uploads the file based upon the "file" and "folder" arguments that are passed, and then finally returns the Index View again:
[HttpPost]
public ActionResult Upload(HttpPostedFileBase file, string folder)
{
// Upload the file from the specified folder
// ...
// ...
// ...
return Index();
}
However, when a user clicks on this upload button, the following error message is displayed:
The view 'Upload' or its master was not found or no view engine supports the searched locations
But I am not trying to render a View called "Upload" - I am trying to render the view called "Index", which is why I have the line return Index();.
Any help on where I'm going wrong?
Answer
Although Vitaliy and Nathan A provided adequate answers, I wanted to explain why your initial approach doesn't work because it's a great question and doesn't seem to make sense.
To get our answer we have to look at the ASP.NET MVC source code.
Before we get to that let's walk through your code.
The user visits (or POSTS to) /Controller/Upload
We do some logic and then return Index()
Index() is a method that returns its own view with its own model
MVC fails to find 'Upload' view and throws an exception
What went wrong?
Firstly know that Index() is being called and returned successfully. The model object is also being passed to the view (if one is found).
When you return Index(), it is returning View() which is an inherited method from the Controller class which returns a ViewResult.
A ViewResult inherits from ViewResultBase.
When a ViewResult is being returned it calls ExecuteResult().
Taking a look at the source code for ExecuteResult():
public override void ExecuteResult(ControllerContext context)
{
if (context == null)
{
throw new ArgumentNullException("context");
}
if (string.IsNullOrEmpty(this.ViewName))
{
this.ViewName = context.RouteData.GetRequiredString("action");
}
ViewEngineResult viewEngineResult = null;
if (this.View == null)
{
viewEngineResult = this.FindView(context);
this.View = viewEngineResult.View;
}
TextWriter output = context.HttpContext.Response.Output;
ViewContext viewContext = new ViewContext(context, this.View, this.ViewData, this.TempData, output);
this.View.Render(viewContext, output);
if (viewEngineResult != null)
{
viewEngineResult.ViewEngine.ReleaseView(context, this.View);
}
}
The key here is context.RouteData.GetRequiredString("action"). This code gets the action name so that a view can be found and rendered. Note that it is using a ControllerContext.
Because of this, your action is actually set when the Upload() method is first called. If you step through your Index() method you will see that calling context.RouteData.GetRequiredString("action") will return the string "Upload".
This is because, within the context of the user request, the action is in fact Upload (that's the page they requested).
Fun fact
If you return Index() and that method happens to alter the ViewBag (ViewData) then the ViewData will be altered regardless of what is rendered.
If your Upload() does this:
ViewBag.Test = "Upload method";
And you return Index() and your Index() does this:
ViewBag.Test = "Index method";
Then the value of Test will be "Index method".
Look up the documentation on the View method. It has several arguments you can provide, one of them being a string of the name of the view, but you always use the View() method if you want to return a view.
However, if you don't want to use the default View name (being the name of the action method), simply use a string to specify a new name like so:
public ActionResult Upload(HttpPostedFileBase file, string folder)
{
return View("Index");
}
you can do either:
return RedirectToAction("Index");
or:
return View("Index");
Just a side-note, you're not constrained to show views from the View folder for the controller name. You can do
public ActionResult something()
{
return View("../OtherView/somethingElse");
}

MVC 5 - RedirectToAction - Cannot pass parameter correctly

I have a method that looks as follows:
[Authorize]
public ActionResult Create(int? birdRowId, Entities.BirdSighting sighting)
{
...
...
}
I want to call the above method from another method in the same controller as follows:
[Authorize]
[HttpPost]
public ActionResult Create(Entities.BirdSighting birdSighting, FormCollection collection)
{
...
...
return RedirectToAction("Create", new {birdRowId = 10, sighting = birdSighting});
}
The RedirectToAction method calls the method correctly. And, the first parameter of the method being called (birdRowId) does equal 10. However, the second parameter, sighting, is always null, even though I'm passing an instantiated object with values. What am I doing wrong?
Remember, HTTP is stateless !
RedirectToAction method returns a 302 response to the client browser and thus the browser will make a new GET request to the specified URL.
If you are trying to follow the PRG pattern, I think you should not try to pass complex objects. You should only pass the ID of the resource so that the GET action can build the resource( the model) again using that ID.
return RedirectToAction("Created", "YourControllerName", new { #id=10} );
and in the Created action, read the id and build the object there.
public ActionResult Created(int id)
{
BirdSighting sighting=GetSightingFromIDFromSomeWhere(id);
// to do :Return something back here (View /JSON etc..)
}
If you really want to pass some data across (Stateless) HTTP Requests, you may use some temporary storage mechanism like TempData
Set your object to TempData in your HttpPost action method.
[HttpPost]
public ActionResult Create(BirdSighting birdSighting, FormCollection collection)
{
// do something useful here
TempData["BirdSighting"] =birdSighting;
return RedirectToAction("Created", "YourControllerName");
}
And in your GET action method,
public ActionResult Created()
{
var model=TempData["BirdSighting"] as BirdSighting;
if(model!=null)
{
//return something
}
return View("NotFound");
}
TempData uses Session object behind the scene to store the data. But once the data is read, the data is terminated.

How to add a query string from surface controller in umbraco mvc in order to persist model values

How to add a query string from surface controller in umbraco mvc . This is my current code.
Initially I wrote a code like
public ActionResult Registration(RegisterModel model)
{
//Code to insert register details
ViewBag.Success="Registered Successfully"
return CurrentUmbracoPage();
}
with this I could successful persist my ViewBag and model properties value but I could not add a query string with it.
For certain requirement I have to change the code that returns a url with querystring.
which I did as below
public ActionResult Registration(RegisterModel model)
{
//Code to insert register details
ViewBag.Success="Registered Successfully"
pageToRedirect = AppendQueryString("success");
return new RedirectResult(pageToRedirect);
}
public string AppendQueryString(string queryparam)
{
var pageToRedirect = new DynamicNode(Node.getCurrentNodeId()).Url;
pageToRedirect += "?reg=" + queryparam;
return pageToRedirect;
}
and with this my values of the properties in model could not persist and the ViewBag returned with null value.
Can any one suggest me how to add query string by persisting the values in the model and ViewBag.
Data in ViewBag will not be available on the View when it redirects. Hence you have to add message in TempData which will be available in the View after the redirect like TempData.Add("CustomMessage", "message");

Pass information back to the view when using Redirect()

I am calling a controller from more than one page and am using a returnUrl parameter to return the correct calling location:
public ActionResult EmailOrder(int id, string returnUrl)
{
var message = "The order has been emailed";
if (!string.IsNullOrEmpty(returnUrl)) return Redirect(returnUrl);
return RedirectToAction("Details", new { id, message });
}
How can I pass additional information back to the view when using Redirect(url)? In the above example, I want to be able to pass message back when returnUrl has a value.
If you are redirecting to another action method and you want to pass data that can be access in the new action method, you should use the ASP.MVC Controller TempData property. You use this as follows:
[HttpPost]
public ActionResult MyActionMethod(Order order)
{
// write your logic here to save the Order
TempData["message"] = "here is some message";
return RedirectToAction("Index");
}
Data in the TempData member will be preserved across a redirection. It will be accessible in the redirected page, and then will be removed. Once you read an entry in TempData, it will be marked for deletion.
public ActionResult RedirectedMethod()
{
//Retrieve data from TempData. It will then be marked for deletion
var data = TempData["message"].ToString();
}
If you want to get a value without marking it for deletion, you can use the "Peek" method:
var data = TempData.Peek("message")
Also, you can manually preserve a value that would otherwise be deleted by using the "Keep" method:
TempData.Keep("message")
TempData is of type TempDataDictionary.
Note that TempData uses ASP.Net Session state behind the scenes, so you must have session state turned on if you are using TempData.
For more information on TempData, see here.

How to pass thw Viewdata to all the views in my controller?

i have a dropdown list which select a value
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Screenname(FormCollection collection)
{
Viewdata["screenname"] = collection[0];
return RedirectToAction("Index", new { ScreenName = ViewData["screenname"] });
}
then i want to access this ViewData in other actions like this
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(FormCollection collection, string screenname)
{
try
{
/// thats my dataobject which creates
DataObj.SaveData(Guid.Empty, collection, screenname);
return RedirectToAction("Index", new { ScreenName = ViewData["screenname"] });
}
catch
{
return View("Error");
}
}
where index looks like this ...
public ActionResult Index(string ScreenName)
{
///thats my list
GetTable = new GetDataTable(ScreenName);
return View(GetTable);
}
First when i select the value and index gets executed properly.... but when i try to access the viewdata again it doesn't contain the value so anybody if please can help ...
or alternate method to save and retrieve data .
The ViewData object is specific for the particular action that is executing. To pass data between actions, use TempData. more on the difference between the two on MSDN.
You can also directly write to the session state through the Controller.Session property.
This has actually been covered quite often here. The solution for now is to use TempData to save the data you need before you use RedirectToAction().
If you do a search for "RedirectToAction" you'll find a number of posts covering this topic, such as this one.
The next official release of the framework will fix this.
I used a view to take the data from the user and then saved it to a static variable and then used this variable to pass the data to all the other views .
Thanks anyways

Resources