Parameters in RedirectToAction don't passed down - asp.net

I'm trying to pass down parameters to my Action.
There are several parameters that I need to pass down.
When debugging, I found that the simple types of parameters got their values, whereas my own class parameters is null.
return RedirectToAction("Histories", new {MyUser = user, sortOrder = "name_desc" });
And here is the Action method:
public ActionResult Histories(ApplicationUser MyUser, string sortOrder, int? page)
I did a research and found , it seems that only objects which can be serialized can be passed down.
So I simply added an annotation [Serializable] on my ApplicationUser class, and it doesn't work.
So I'm wondering what's the best practice to pass down my objects?
I certainly know I can put the MyUser into Session["CurrentUser"], but I just don't like this old fashion.
Thank you.

You have not passed int? page value, it should be like this
return RedirectToAction("Histories", new {MyUser = user,
sortOrder = "name_desc",
page =1 });
or you need to use default parameter value like this
public ActionResult Histories(ApplicationUser MyUser,
string sortOrder,
int? page = 1)

Related

Not able to convert object of type {System.Data.Entity.Core.Objects.ObjectResult<int>} to int - ASP MVC 5

The stored procedure output gives me an integer value. I need to pass that value to the view page and increment it.
Controller:
var OrderId = db.USP_SEL_LAST_ORDERID();
ViewData["OrderId"] = (OrderId);
View:
var orderid = int.Parse(#ViewBag.OrderId);
Error:
The best overloaded method match for 'int.Parse(string)' has some invalid arguments
#Wheels73's answer is almost right, however you need to know that stored procedure method returns data from DbContext in form of result set, even it has only single value. You can use either FirstOrDefault() or FirstOrDefault<int>() method to ensure it returns int:
Controller
var OrderId = db.USP_SEL_LAST_ORDERID().FirstOrDefault();
ViewData["OrderId"] = OrderId; // ensure this is an integer
View
var orderid = int.Parse(#ViewBag.OrderId.ToString());
Note that System.Data.Entity.Core.Objects.ObjectResult<T> can have multiple values (with different data types), hence it is possible to ask for first int to get SP result. Also, better to use strongly-typed viewmodel class with int property rather than dynamic ViewBag or ViewData:
Model
public class ViewModel
{
public int OrderId { get; set; }
}
Controller
public ActionResult ActionName()
{
var model = new ViewModel();
var OrderId = db.USP_SEL_LAST_ORDERID().FirstOrDefault();
model.OrderId = OrderId;
// returns entire viewmodel to view, 'int.Parse' conversion not required
return View(model);
}
Similar issue:
consume int result returned from stored procedure in MVC4
How to handle ObjectResult in Entity Framework 4
See below.
var orderid = int.Parse(#ViewBag.OrderId.ToString());
Thanks

Block passing URL's parameter inside action method to the view

Is there any possibility to block (by attribute for example) passing URL's parameters into view?
public ActionResult UserForm(Guid? entityId, bool isCopy = false)
{
ViewBag.IsNewOrCopy = entityId.IsNullOrEmptyOrCopy();
if (ViewBag.IsNewOrCopy)
return View(new UserFormViewModel() {Roles= service.GetRoles(entityId, isCopy) });
return View(service.GetUser(entityId.Value));
}
For example I would like to stop passing entityId parameter because when I update changes via post method the entityId (#Html.HiddenFor(m => m.EntityId)) is also passed via viewmodel although I would like it to be passed as null variable.

FromUri binding when querystring is empty

If we have the following controller action in Web API
public async Task<HttpResponseMessage> GetRoutes(
[FromUri] MapExtentQuery extent,
[FromUri] PagingQuery paging)
{
...
}
with
class MapExtentQuery {
public int X { get;set; }
public int Y { get; set; }
}
class PagingQuery {
public int Skip { get; set; }
public int Top { get; set; }
}
and we make a GET request to /routes both parameters (extent and paging) will be null.
If the request contains at least one querystring parameter, for instance
/routes?x=45
then both complex parameters will get initialized, so in the case of the 2nd route
extent.X = 45
extent.Y = 0
paging != null (but Skip and Top will be 0 of course).
Why does the [FromUri] binder work this way? It makes little or no sense.
I would understand if it initialized only the parameter that contains a property that matched at least one of the querystring values.
The problem is, this behaviour requires us to check when parameters are null (which happens only in the case that no querystring parameter was provided) and then initiliaze them ourselves.
Because obviously those complex params might have constructors which would set some property values to default.
It is because when you do not have query string and the parameters have FromUri attribute, the parser for the query string does not run and all values are not binded - you receive null.
If you have query string, the binder runs and instantiates all FromUri parameters and primitive types with their default values and then tries to fill in the properties from the query string values. This leaves you with two instantiated parameters but only one having the populated value because that is all the query string has.
As for why it works this way - most likely performance - this way they will not need to find which parameters they need to instantiate - they instantiate all of them and then find the properties. It is faster and performance is critical in the action and model binding.

RedirectAttributes changes object id between controllers

I am using Spring MVC and Hibernate in my project. Also I have 2 controllers UserController and BookController where BookController redirects to the users page and I am passing a Book object in addition.
I've found I can do this with RedirectAttributes but the problem is that the id of the passed Book object is changed during this transition to user.id.
BookController.java
public class BookController {
#RequestMapping("/users/{user_id}/books/edit/{book_id}")
public String editBook(#PathVariable("user_id") int user_id, #PathVariable("book_id") int book_id, final RedirectAttributes redirectAttrs){
bookDetail = this.bookService.getBookById(book_id)
redirectAttrs.addFlashAttribute("bookDetail", bookDetail);
System.out.println(bookDetail);
return "redirect:/users/"+user_id;
}
}
Prints: id=8, title=Motylek, description=Some description, user_id=2.
UserController.java
public class UserController {
#RequestMapping("/users/{id}")
public String detailUser(#ModelAttribute("bookDetail") Book bookDetail, #PathVariable("id") int id, Model model){
User u = this.userService.getUserById(id);
model.addAttribute("user", u);
model.addAttribute("bookDetail", bookDetail);
System.out.println(bookDetail);
return "user";
}
}
Prints: id=2, title=Motylek, description=Some description, user_id=2.
Do you have and idea why this happens or is it a bug? Thanks.
I'm going to assume that your Book class has a property called id, ie. a getter or setter called getId() and setId(..).
When Spring parses the request URL, it stores path segments as declared in the corresponding #RequestMapping. So for
/your-app/users/2
and
#RequestMapping("/users/{id}")
It will store
id=2
as a request parameter.
Spring will then proceed to generate an argument for
#ModelAttribute("bookDetail") Book bookDetail
It will check the various request, session, servlet attributes for an entry with the name bookDetail. (If it doesn't find one, it will create one and add it to the request attributes.) In your case, it will have found the object in the HttpSession. It will then bind any request parameters to matching object properties. Since the parameter above is called id, it will be bound to the Book property id.
You should be good by changing
#RequestMapping("/users/{id}")
to
#RequestMapping("/users/{user_id}")
along with the corresponding #PathVariable.

Use a viewmodel with web api action

I just read this post by Dave Ward (http://encosia.com/using-jquery-to-post-frombody-parameters-to-web-api/), and I'm trying to throw together a simple web api controller that will accept a viewmodel, and something just isn't clicking for me.
I want my viewmodel to be an object with a couple DateTime properties:
public class DateRange
{
public DateTime Start { get; set; }
public DateTime End { get; set; }
}
Without changing anything in the stock web api project, I edit my values controller to this:
public IEnumerable<float> Get()
{
DateRange range = new DateRange()
{
Start = DateTime.Now.AddDays(-1),
End = DateTime.Now
};
return Repo.Get(range);
}
// GET api/values/5
public IEnumerable<float> Get(DateRange id)
{
return Repo.Get(range);
}
However, when I try to use this controller, I get this error:
Multiple actions were found that match the request:
System.Collections.Generic.IEnumerable1[System.Single] Get() on type FEPIWebService.Controllers.ValuesController
System.Collections.Generic.IEnumerable1[System.Single] Get(FEPIWebService.Models.DateRange) on type FEPIWebService.Controllers.ValuesController
This message appears when I hit
/api/values
or
/api/values?start=01/01/2013&end=02/02/2013
How can I solve the ambiguity between the first and second get actions?
For further credit, if I had this action
public void Post(DateRange value)
{
}
how could I post the Start and End properties to that object using jQuery so that modelbinding would build up the DateRange parameter?
Thanks!
Chris
The answer is in detail described here: Routing and Action Selection. The Extract
With that background, here is the action selection algorithm.
Create a list of all actions on the controller that match the HTTP request method.
If the route dictionary has an "action" entry, remove actions whose name does not match this value.
Try to match action parameters to the URI, as follows:
For each action, get a list of the parameters that are a simple type, where the binding gets the parameter from the URI. Exclude
optional parameters.
From this list, try to find a match for each parameter name, either in the route dictionary or in the URI query string. Matches are
case insensitive and do not depend on the parameter order.
Select an action where every parameter in the list has a match in the URI.
If more that one action meets these criteria, pick the one with the most parameter matches.
4.Ignore actions with the [NonAction] attribute.
Other words, The ID parameter you are using, is not SimpleType, so it does not help to decide which of your Get methods to use. Usually the Id is integer or guid..., then both methods could live side by side
If both of them would return IList<float>, solution could be to omit one of them:
public IEnumerable<float> Get([FromUri]DateRange id)
{
range = range ?? new DateRange()
{
Start = DateTime.Now.AddDays(-1),
End = DateTime.Now
};
return Repo.Get(range);
}
And now both will work
/api/values
or
/api/values?Start=2011-01-01&End=2014-01-01

Resources