MVC passing complex object to action - c#

I have two actions in my controller when a user call one i need to redirect it to another one and pass a complex object :
first action :
public virtual ActionResult Index(string Id) {
var input = new CustomInput();
input.PaymentTypeId = Id;
return RedirectToAction(MVC.Ops.SPS.Actions.Test(input));
}
second action :
public virtual ActionResult Test(CustomInput input) {
return View();
}
The probelm is that the input arrives null at the second action. how can i solve it?

You can solve this using temp data to temporarily hold a value from which the second method retrieves that value.
public virtual ActionResult Index(string Id)
{
var input = new CustomInput();
input.PaymentTypeId = Id;
TempData["TheCustomData"] = input; //temp data, this only sticks around for one "postback"
return RedirectToAction(MVC.Ops.SPS.Actions.Test());
}
public virtual ActionResult Test()
{
CustomInput = TempData["TheCustomData"] as CustomInput;
//now do what you want with Custom Input
return View();
}
You can keep your tempData going so long as it is never null using the .keep() method like this,
if (TempData["TheCustomData"] != null)
TempData.Keep("TheCustomData");

I want to make sure that you know that RedirectToAction creates new HTTP request so this create another GET and all you can pass is RouteValueDictionary object which is like having query string parameters which is list of key and value pairs of string. That said, You can't pass complex object with your way of code however the TempData solution mentioned by #kyleT will work.
My recommendation based on your code is to avoid having two actions and redirect from one another unless you are doing something more than what you have mentioned in your question. Or you can make your Test action accepting the id parameter the your Index action will contain only RedirectToAction passing the id as route parameter.
Edit:
Also, If you have no primitive properties you can pass your object like the following (Thanks to #Stephen Muecke)
return RedirectToAction("Test",(input);

Related

Does going from one Action method to the other action method clear class variables?

So in the same controller I have a Login Action method like this:
public ActionResult Login()
{
LoginModel model = this.LoginManager.LoadLoginPageData();
this.ForgotPasswordMethod = model.ForgotPasswordMethod;
return View(model);
}
Notice I set a variable there: ForgotPasswordMethod
So now when there on that page if they click on a link, it call another action result in the same controller class like this:
public ActionResult ForgotPassword()
{
if (!string.IsNullOrWhiteSpace(this.ForgotPasswordMethod) && this.ForgotPasswordMethod.Trim().ToUpper() == "TASKS")
return View();
return null; //todo change later.
}
Notice I tried to read the value of ForgotPasswordMethod , but it was NULL but it is NOT null when I am in the Login() method. So what should I do?
ASP.NET MVC was designed to return back to a cleaner, more straightforward web world built on HTTP, which is stateless, meaning that there is no "memory" of what has previously occurred unless you specifically use a technique that ensures otherwise.
As a result, whatever state you set via one ActionResult will no longer be the same state that exists when another ActionResult is invoked.
How do you "fix" this? You have a variety of options, depending on what your needs are:
Render the value to the client and post the value back to your second ActionResult method.
Store the value as a header and check that header.
Store the value in a cookie and check the cookie.
Store the value in session.
Store the value in a database.
Store the value in a static dictionary.
what if you store forgotpasswordmethod in Viewbag like
public ActionResult Login()
{
LoginModel model = this.LoginManager.LoadLoginPageData();
Viewbag.ForgotPasswordMethod = model.ForgotPasswordMethod;
return View(model);
}
then in the link of your page you can pass the value from the ViewBag
<a href=#Url.Action("ForgotPassword", "Name of your Controller", new { methodName = ViewBag.ForgotPasswordMethod })>Forgot Password</a>
Change your forgotpassword to
public ActionResult ForgotPassword(string methodName)
{
if (!string.IsNullOrWhiteSpace(methodName) && methodName.Trim().ToUpper() == "TASKS")
return View();
return null; //todo change later.
}

passing model to RedirectToAction without using session or tempdata?

I m modifying an existing code.
From one action I use RedirectToAction to transfer execution control to another action. I need to pass Model with RedirectToAction as well. My idea is it can't be done directly by passing Model to 2nd action without using Session or tempData. But still want to ask is there a technique to pass model with RedirectToAction ? I don't want to put Model in Session or TempData.
Thanks
You can try something like that, but it doesn't feel like a natural action:
public ActionResult Index()
{
return RedirectToAction("AnotherAction", new
{
Parameter1 = Parameter1,
Parameter2 = Parameter2,
});
}
[HttpGet]
public ActionResult AnotherAction(ModelClass model)
{
//model.Parameter1
//model.Parameter2
return View(model);
}
I would expect the code of the previous answer to throw an exception when attempting to implicitly convert an object of parameter1 and parameter2 to ModelClass.
That being said, The best way is to just pass the id of the entity to your new action, then access your repository to initialize your model with the id that has been passed. Lets assume you have initialized a user with a UserID property.
return RedirectToAction("NextAction", new { id = user.UserID });
Then in NextAction just initialize the model with the passed id

Return action doesn't change URL

I was looking for a way to redirect with POST and the solutions I've found suggest simply using the action's name I want and fill the params. All of this within the same controller (let's call it Home)
[HttpPost]
public ActionResult Zoro(NameOfMyModel model, string stringName)
{
//Do whatever needs to be done
return Foo("bar",123);
}
[HttpPost]
public ActionResult Foo(string Name, int Age)
{
//Code here that use the params
return View();
}
So that works great except that when you look at the url, it doesn't show /Home/Foo, it shows /Home/Zoro. Can I fix this without using RedirectToAction? If I use it, I get this: Home/Foo?Name=bar&Age=123 Which I don't want.
Instead of calling directly calling Foo() use RedirectToAction() with this overload of it.
The way you doing calls the action on server but not actually redirects, if you want the url to change you have to redirect to the action:
return RedirectToAction("Foo", new {Name = "bar", Age = 123});
UPDATE:
As in comments mention how to keep the data temporarily ,you can use TempData[] for it:
TempData["Name"] = bar";
TempData["Age"] = 123;
return RedirectToAction("SomeAction");
and in that action you can get it from TempData:
public ActionResult SomeAction()
{
string Name = TempData["Name"] as string;
int Age - TempData["Age"] as int;
return View();
}
NOTE:
Note that RedirectToAction() only works with actions which are HttpGet, it will not work with HttpPost actions.

Can anyone explain CreatedAtRoute() to me?

From the template for Web API 2, a post method is always like this:
[ResponseType(typeof(MyDTO))]
public IHttpActionResult PostmyObject(MyDTO myObject)
{
...
return CreatedAtRoute("DefaultApi", new { id = myObject.Id }, myObject);
}
I don't understand this CreatedAtRoute() method. Can anyone explain it to me?
The CreatedAtRoute method is intended to return a URI to the newly created resource when you invoke a POST method to store some new object.
So if you POST an order item for instance, you might return a route like 'api/order/11' (11 being the id of the order obviously).
BTW I agree that the MSDN article is of no use in understanding this. The route you actually return will naturally depend on your routing setup.
When you use CreatedAtRoute, the first argument is the route name of the GET to the resource. The trick that is not so obvious is that, even with the correct method name specified, you must thus use the Name param on the HttpGet attribute for it to work.
So if the return in your POST is this:
return CreatedAtRoute("Get", routeValues: new { id = model.Id }, value: model);
Then your Get method attribute should look like this even if your method is named Get:
[HttpGet("{id}", Name = "Get")]
Calls to your Post method will not only return the new object (normally as JSON), it will set the Location header on the response to the URI that would get that resource.
NOTE the field names in the routeValues field names need to match the binding names in the target route, i.e. there needs to be a field named id to match the {id} in HttpGet("{id}"
Finally, in some cases, it should be mentioned that the CreatedAtAction helper can be a more direct solution.
In .net core WebAPI, you use this method to return a 201 code, which means that the object was created.
[Microsoft.AspNetCore.Mvc.NonAction]
public virtual Microsoft.AspNetCore.Mvc.CreatedAtRouteResult CreatedAtRoute (string routeName, object routeValues, object content);
As you can see above, the CreatedAtRoute can receive 3 parameters:
routeName
Is the name that you must put on the method that will be the URI that would get that resource after created.
routeValues
It's the object containing the values that will be passed to the GET method at the named route. It will be used to return the created object
content
It's the object that was created.
The above example shows the implementation of two methods of a simple controller with a simple GET method with the bonded name and the POST method that creates a new object.
[Route("api/[controller]")]
[ApiController]
public class CompanyController : Controller
{
private ICompanyRepository _companyRepository;
public CompanyController(ICompanyRepository companyRepository)
{
_companyRepository = companyRepository;
}
[HttpGet("{id}", Name="GetCompany")]
public IActionResult GetById(int id)
{
Company company = _companyRepository.Find(id);
if (company == null)
{
return NotFound();
}
return new ObjectResult(company);
}
[HttpPost]
public IActionResult Create([FromBody] Company company)
{
if (company == null)
{
return BadRequest();
}
_companyRepository.Add(company);
return CreatedAtRoute(
"GetCompany",
new { id = company.CompanyID },
company);
}
}
IMPORTANT
Notice that the first parameter at CreatedAtRoute (routeName), must be the same at the definition of the Name at the Get method.
The object on the second parameter will need to have the necessary fields that you use to retrieve the resource on the Get method, you can say that it's a subset of the object created itself
The last parameter is the company object received in the body request in it's full form.
FINALY
As final result, when the Post to create a new company got made to this API, you will you return a route like 'api/company/{id}' that will return to you the newly created resource

Post data from one controller to another

I have a mvc application. I want to post 2 string from my controller to another controller in controller. Is it possible?
You can target any controller action from your form input in order to POST a string. If you want to pass a string or object from controller to controller you can use sessions or alternatively a database.
Additionally if you want to pass strings or objects between action methods in the same controller you can use the TempData collection as well.
Yes, You can use TempData for that like...
public ActionResult Sample1()
{
TempData["Test"] = "Test1"
return RedirectToAction("Sample2");
}
public ActionResult Sample2()
{
var test= TempData["Test"] as string
return View( test);
}

Categories