customers is a List<string>.
RedirectToAction("ListCustomers", new { customers = customers });
And when I send the list it contains 4 items, but when I receive it in my controller method it has only one item and it's of type generic list. That seems not be what I want. But how to pass more complex data than strings and integer between controller methods?
You cannot send complex objects when redirecting. When redirecting you are sending a GET request to the target action. When sending a GET request you need to send all information as query string parameters. And this works only with simple scalar properties.
So one way is to persist the instance somewhere on the server before redirecting (in a database for example) and then pass only an id as query string parameter to the target action which will be able to retrieve the object from where it was stored:
int id = Persist(customers);
return RedirectToAction("ListCustomers", new { id = id });
and inside the target action:
public ActionResult ListCustomers(int id)
{
IEnumerable<string> customers = Retrieve(id);
...
}
Another possibility is to pass all the values as query string parameters (be careful there's a limit in the length of a query string which will vary among browsers):
public ActionResult Index()
{
IEnumerable<string> customers = new[] { "cust1", "cust2" };
var values = new RouteValueDictionary(
customers
.Select((customer, index) => new { customer, index })
.ToDictionary(
key => string.Format("[{0}]", key.index),
value => (object)value.customer
)
);
return RedirectToAction("ListCustomers", values);
}
public ActionResult ListCustomers(IEnumerable<string> customers)
{
...
}
Yet another possibility is to use TempData (not recommended):
TempData["customer"] = customers;
return RedirectToAction("ListCustomers");
and then:
public ActionResult ListCustomers()
{
TempData["customers"] as IEnumerable<string>;
...
}
Related
In my controller of an ASP.NET MVC project, I have a
return RedirectToAction("CreatePerson", "Home")
This View is a form that creates a person and that works fine. However, I want to RedirectToAction and pre-fill the form with data collected from a form that creates a User for the system.
How would I pass the data from the CreateUser form in the CreatePerson form?
I know that I could use parameters, but would this really be the best method if most of the time I am calling the CreatePerson view without needing those parameters.
Any help in the right direction would be greatly appreciated.
You can't send data with a RedirectAction. That's because you're doing a 301 redirection and that goes back to the client.
So better use TempData
Assuming you will have model to createperson with following properties:
public class CreatePersonData
{
public string name {get; set;}
public string address {get; set;}
}
Now fill the model and store in TempData
CreatePersonData person=new CreatePersonData();
person.name="SomeName";
person.address="SomeAddress";
TempData["person"]=person;
return RedirectToAction("CreatePerson", "Home")
Now while receiving just receive it from tempdata and pass the filled model to the view
public ActionResult CreatePerson()
{
CreatePersonData person=new CreatePersonData()
var loadPerson= TempData["person"];
person = loadPerson;
return View(person);
}
UPDATE
As #StephenMuecke made a point of loosing data with TempData you might need to use .Keep or .Peek with TempData to retain the value for future requests
Ex:
with .Peek
//PEEK value so it is not deleted at the end of the request
var loadPerson= TempData.Peek("person");
or with .Keep
//get value marking it from deletion
var loadPerson = TempData["person"];
//later on decide to keep it
TempData.Keep("person");
or as #Stephen said just pass the id and select the user from database
Ex:
return RedirectToAction("CreatePerson", "Home", new { ID = User.ID });
Now in your CreatePerson ActionResult just get it from db as below:
public ActionResult CreatePerson(int ID)
{
CreatePersonData person=new CreatePersonData();
var user=(from u in tbl_user select u where u.ID=ID);
person.name=user.name;
person.address=user.address;
return View(person);
}
UPDATE 2
You can combine both of the above approaches like storing data in TempData and passing the ID with routeValues and check if TempData isn't null then fallback to retrieval of data using ID approach.
Ex:
public class CreatePersonData
{
public string Id{get; set;}
public string name {get; set;}
public string address {get; set;}
}
public ActionResult CreatePerson(int ID)
{
CreatePersonData person=new CreatePersonData();
var loadPerson=(CreatePersonData)TempData.Peek("person"); //cast the object from TempData
if(loadPerson!=null && loadPerson.Id==ID)
{
person=loadPerson;
}
else
{
var user=(from u in tbl_user select u where u.ID=ID);
person.name=user.name;
person.address=user.address;
}
return View(person);
}
Many Overloads exist for this
protected internal RedirectToRouteResult RedirectToAction(string actionName, object routeValues);
What you want to do is right click on that Method and View Definition, you should see many overloads for it
// Summary:
// Redirects to the specified action using the action name and route values.
//
// Parameters:
// actionName:
// The name of the action.
//
// routeValues:
// The parameters for a route.
//
// Returns:
// The redirect result object.
1- create an object of your model
2- you can use session to pass parameter to your other action,
3- then put the object in a viewbag agg get it in your view
I am having previously defined action which return the List<T>() int Json Format using JsonResult.
I want to use the same action's JSON result into another action.
For better understanding,
My Common Action is::
public JsonResult GetReferences(long CustomerID)
{
var CustomerReference = new List<ReferenceViewModel>();
//Code to push data into CustomerReference
return Json(CustomerReference.OrderBy(x=>x.ReferenceName));
}
I want to use the above JsonResult into my other Action as::
public ActionResult MassUpdate(List<long> CustomerIDs, List<long> DocumentIDs)
{
List<ReferenceViewModel> JsonCustomerReference = GetReferences(CustomerID);
}
But I am not able to access the JsonResult here. How can I do that?
GetReferences is returning a JSONREsult which is not a List. why dont you try abscracting this a bit:
private IEnumerable<ReferenceViewModel> getReferences(long CustomerID)
{
var customerReferences = new List<ReferenceViewModel>();
//Code to push data into CustomerReference
return customerReferences;
}
public JsonResult GetReferences(long CustomerID)
{
return Json(customerReference(CustomerID).OrderBy(x=>x.ReferenceName));
}
public JsonResult MassUpdate(List<long> CustomerIDs, List<long> DocumentIDs)
{
var allData = CustomerIDs.SelectMany(x => customerReference(x)).OrderBy(x=>x.ReferenceName);
return Json(alldata);
}
I am making some assumptions here. But i think this is what you are kind of going for. you could also change the return type of getReferences to an IEnumerable and do the sorting in each Action if that is what you are going for. I updated my answer to reflect that change.
I hope that you want something like this.
JsonResult hold data in Data Property. Which is object so you need to cast it back.
List JsonCustomerReference = (List)((JsonResult)GetReferences(CustomerID)).Data;
I want to pass more then one parameter from RedirectToAction method
how can I pass?
My One Action Method
[HttpPost, ActionName("SelectQuestion")]
public ActionResult SelectQuestion(string email,List<QuestionClass.Tabelfields> model)
{
List<QuestionClass.Tabelfields> fadd = new List<QuestionClass.Tabelfields>();
for (int i = 0; i < model.Count; i++)
{
if (model[i].SelectedCheckbox == true)
{
List<QuestionClass.Tabelfields> f = new List<QuestionClass.Tabelfields>();
fadd.Add(model[i]);
}
}
return RedirectToAction("Question", new { email = email, model = fadd.ToList() });
}
My another Action Method
[HttpGet]
public ActionResult Question(string email,List<QuestionClass.Tabelfields> model)
{
}
I am not getting values in model.
You cannot pass a collection of complex objects in urls when redirecting.
One possibility would be to use TempData:
TempData["list"] = fadd.ToList();
return RedirectToAction("Question", new { email = email});
and then inside the Question action:
var model = TempData["list"] as List<QuestionClass.Tablefields>;
The way that I solved this problem was to serialize the list to a JSON object using the JsonConvert method from the Newtonsoft.Json nuget package. Then the serialized list can be passed as a parameter and then deserialized again to re-create the original list.
So in your SelectQuestion method you would use this code:
return RedirectToAction("Question",
new {
email = email,
serializedModel = JsonConvert.SerializeObject(fadd.ToList())
});
And in your Question method, you would use this code to deserialize the object.
[HttpGet]
public ActionResult Question(string email, string serializedModel)
{
// Deserialize your model back to a list again here.
List<QuestionClass.Tabelfields> model = JsonConvert.DeserializeObject<List<QuestionClass.Tabelfields>>(serializedModel);
}
Important, this adds the model as a query string parameter to your url, so only do this with really simple small objects, otherwise your url will be too long.
This is probably not even active anymore, but I'll leave how I did it here to maybe help someone else.
I solved this using a simple Redirect instead of a RedirectToAction:
List<int> myList = myListofItems;
var list = HttpUtility.ParseQueryString("");
myList.ForEach(x => list.Add("parameterList", x.ToString()));
return Redirect("/MyPath?" + list);
Then, on your other method:
public ActionResult Action(List<int> parameterList){}
RedirectToAction method Returns an HTTP 302 response to the browser, which causes the browser to make a GET request to the specified action.
You should either keep the data in a temporary storage like TempData / Session . TempData uses Session as the backing storage.
If you want to keep it real Stateless, you should pass an id in the query string and Fetch the List of items in your GET Action. Truly Stateless.
return RedirectToAction("Question", new { email = email,id=model.ID });
and in your GET method
public ActionResult Question(string email,int id)
{
List<QuestionClass.Tabelfields> fadd=repositary.GetTabelFieldsFromID(id);
//Do whatever with this
return View();
}
Assuming repositary.GetTabelFieldsFromID returns a List of TabelFields from the Id
I'm trying to extract the parameters of my URL, something like this.
/Administration/Customer/Edit/1
extract: 1
/Administration/Product/Edit/18?allowed=true
extract: 18?allowed=true
/Administration/Product/Create?allowed=true
extract: ?allowed=true
Someone can help? Thanks!
Update
RouteData.Values["id"] + Request.Url.Query
Will match all your examples
It is not entirely clear what you are trying to achieve. MVC passes URL parameters for you through model binding.
public class CustomerController : Controller {
public ActionResult Edit(int id) {
int customerId = id //the id in the URL
return View();
}
}
public class ProductController : Controller {
public ActionResult Edit(int id, bool allowed) {
int productId = id; // the id in the URL
bool isAllowed = allowed // the ?allowed=true in the URL
return View();
}
}
Adding a route mapping to your global.asax.cs file before the default will handle the /administration/ part. Or you might want to look into MVC Areas.
routes.MapRoute(
"Admin", // Route name
"Administration/{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
If it's the raw URL data you are after then you can use one of the various URL and Request properties available in your controller action
string url = Request.RawUrl;
string query= Request.Url.Query;
string isAllowed= Request.QueryString["allowed"];
It sounds like Request.Url.PathAndQuery could be what you want.
If you want access to the raw posted data you can use
string isAllowed = Request.Params["allowed"];
string id = RouteData.Values["id"];
public ActionResult Index(int id,string value)
This function get values form URL
After that you can use below function
Request.RawUrl - Return complete URL of Current page
RouteData.Values - Return Collection of Values of URL
Request.Params - Return Name Value Collections
You can get these parameter list in ControllerContext.RoutValues object as key-value pair.
You can store it in some variable and you make use of that variable in your logic.
I wrote this method:
private string GetUrlParameter(HttpRequestBase request, string parName)
{
string result = string.Empty;
var urlParameters = HttpUtility.ParseQueryString(request.Url.Query);
if (urlParameters.AllKeys.Contains(parName))
{
result = urlParameters.Get(parName);
}
return result;
}
And I call it like this:
string fooBar = GetUrlParameter(Request, "FooBar");
if (!string.IsNullOrEmpty(fooBar))
{
}
In order to get the values of your parameters, you can use RouteData.
More context would be nice. Why do you need to "extract" them in the first place? You should have an Action like:
public ActionResult Edit(int id, bool allowed) {}
I'm not familiar with ASP.NET but I guess you could use a split function to split it in an array using the / as delimiter, then grab the last element in the array (usually the array length -1) to get the extract you want.
Ok this does not seem to work for all the examples.
What about a regex?
.*(/|[a-zA-Z]+\?)(.*)
then get that last subexpression (.*), I believe it's $+ in .Net, I'm not sure
I have a ViewModel like so:
public class ProductEditModel
{
public string Name { get; set; }
public int CategoryId { get; set; }
public SelectList Categories { get; set; }
public ProductEditModel()
{
var categories = Database.GetCategories(); // made-up method
Categories = new SelectList(categories, "Key", "Value");
}
}
Then I have two controller methods that uses this model:
public ActionResult Create()
{
var model = new ProductEditModel();
return View(model);
}
[HttpPost]
public ActionResult Create(ProductEditModel model)
{
if (ModelState.IsValid)
{
// convert the model to the actual entity
var product = Mapper.Map(model, new Product());
Database.Save(product);
return View("Success");
}
else
{
return View(model); // this is where it fails
}
}
The first time the user goes to the Create view, they are presented with a list of categories. However, if they fail validation, the View is sent back to them, except this time the Categories property is null. This is understandable because the ModelBinder does not persist Categories if it wasn't in the POST request. My question is, what's the best way of keeping Categories persisted? I can do something like this:
[HttpPost]
public ActionResult Create(ProductEditModel model)
{
if (ModelState.IsValid)
{
// convert the model to the actual entity
var product = Mapper.Map(model, new Product());
Database.Save(product);
return View("Success");
}
else
{
// manually populate Categories again if validation failed
model.Categories = new SelectList(categories, "Key", "Value");
return View(model); // this is where it fails
}
}
But this is an ugly solution. How else can I persist it? I can't use a hidden field because it's a collection.
I would use the repository to fetch whatever data is needed and don't think it's an ugly solution:
[HttpPost]
public ActionResult Create(ProductEditModel model)
{
if (!ModelState.IsValid)
{
// manually populate Categories again if validation failed
model.Categories = Repository.GetCategories();
return View(model);
}
// convert the model to the actual entity
var product = Mapper.Map(model, new Product());
Database.Save(product);
// I would recommend you to redirect here
return RedirectToAction("Success");
}
To further refactor this I would recommend you watching the excellent Putting Your Controllers on a Diet video presentation by Jimmy Bogard.
I typically implement my lists (for drop downs) as a readonly property. When the View gets the value the property is self contained on what it needs to return the values.
public SelectList Categories
{
get
{
var categories = Database.GetCategories(); // made-up method
return new SelectList(categories, "Key", "Value");
}
}
If necessary you can grab the currently selected item (i.e. validation failed) from the property containing the id that was posted and bound to the instance of your class.
In my case I have a BaseModel class where I keep all those property list as class attributes.
Like in the following sample:
public IEnumerable<SelectListItem> CountryList
{
get
{
return GetCountryList().Select(
t => new SelectListItem { Text = t.Name, Value = Convert.ToString(t.CountryID) });
}
}
GetCountryList() is a function that ask a Singleton for data. This would only happen once in the app lifecycle
Another way for doing this, and if those lists are pretty big, would be to have a static utility class with the lookup table that returns the SelectListItem.
If you need to access a list that change from time to time then simply dont use a Singleton class.