Using a ModelBinder to get an object by an ID - asp.net-mvc

I have multiple controller actions that takes an id
public ActionResult Get(int? id) {
...
}
public ActionResult Delete(int id) {
...
}
public JsonResult GetJson(int? id) {
...
}
I'm thinking its best practice to use a ModelBinder (SomeObjectFromIDModelBinder) on each action, so the obtaining of the object is separated from the controller, and keeps the action methods smaller.
The reason I don't want it to be called SomeObjectModelBinder is because I also have a need to recreate Models from JSON, so have a SomeObjectFromJsonModelBinder which handles recreating the 'SomeObject' from a JSON string.
I'm thinking this is a suitable usage of ModelBinders (naming convention), but just wanted clarification. Thoughts?

You don't need to do anything to get the ID. THe MVC handler will look for simple values of the same name as the method parameters in the Form data, the Query String, and the Route.
So you could have a route /{controller}/{action}/{id} with defaults of action="Get", id=1.
then the id could be specified in the URL as either /Home/Get/3 or /Home/Delete?id=6.
Alternately, you could have a textbox with an id of "id" and a submit button in a form that posts to "/Home/Get".
ModelBinders are intended to allow you to have action methods that are classes by creating the instance and populating the properies from the Form data, Query string, or Route data.

I've decided that it is acceptable to do what I was asking, and having multiple ModelBinders that will bind different data to a Model.

Related

Why does my viewmodel name have to be 'model'?

I've been working with MVC for quite a while. I made a form to submit my data using an entity model and as per requirement had to add tags too so I updated the view and the actionmethod to use a viewmodel instead. Like this:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(PostwTagsVM post)
{
}
Surprisingly, the model was null.I couldn't find out why but then decided to rename the object as below:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(PostwTagsVM model)
{
}
Surprisingly, I get the data in the model now.
I know I can work like this but if i really needed to name by model object something else other than 'model'. Whats happening here?
It does not have to be named model.
If in the first case, your model is null, its because your PostwTagsVM model contains a property named post.
The parameter can be named whatever you want, except that it cannot be the same name as one of the properties in your model.
The reason is that your form would be sending back a name/value pair that is (say) post=someValue. The DefaultModelBinder looks for a matching name, sets the value of the property named Post to someValue, but then also finds a parameter named post and tries to set that to someValue, which fails (because you cannot do PostwTagsVM post = "someValue";), and the model becomes null.

Pass a big object to an action on a controller from a view

I want to pass a big object to a controller's action from a view. Like so:
View
<div>#Html.ActionLink("Send us an email", "Index",
"Email", new { o = #Model.Exception }, null)</div>
Controller
public class EmailController : Controller
{
[AllowAnonymous]
public ActionResult Index(object o)
{
new BaseServices.Emailer().SendEmail(o);
return View();
}
}
The thing is: the object being passed is so large that I guess that MVC is unable to make an argument out of that and add it to the route table/dictionary. So, my email controller's Index action is never called. The code bombs off somewhere in between.
No, you can't do this. ASP.NET MVC is not some magic. It relies on standard HTTP and HTML. And as you know in HTTP when you are using a GET request, there's no notion of .NET objects. You cannot ask how to pass an object in a web application because this is not defined.
There's a notion of query string parameters. So that's what you can pass => simple query string parameters:
#Html.ActionLink(
"Send us an email",
"Index",
"Email",
new { id = Model.Exception.Id, text = Model.Exception.Text },
null
)
Where the magic comes is that ASP.NET MVC will now use the 2 simple query string parameters (id and text) to map them to the corresponding properties of your view model inside the controller action.
But of course for this to work ASP.NET MVC needs to know the type of the model. You cannot just use object because this type doesn't have id nor text properties.
So:
public ActionResult Index(MyViewModel o)
Now but what about sending complex types? Well, the question that you have to ask to yourself is why on the first place this type was passed to the view? Was it because tfhe user was supposed to edit some of its properties? Is so then you should use an HTML form containing input fields allowing the user to edit them.
But since you have stuck this object into an anchor then what's the point? The server could fetch this object from wherever it fetched it in the first place. So all you need is to pass a simple id to the server:
#Html.ActionLink(
"Send us an email",
"Index",
"Email",
new { id = Model.Exception.Id },
null
)
and have the controller action take this id as parameter:
public ActionResult Index(int id)
Alright now you know the id => therefore you could retrieve the corresponding entity from wherever this entity is persisted.
Now some people might suggest you storing the object into the session before rendering the view and then retrieving this object from the session. Personally I am not a big fan of the session as it introduces state into the application. This means that you can never invoke the second action without first invoking the first action. This also means that you cannot bookmark the second action into the browser favorites. This also means that if you are running your application in a web-farm you can no longer store the session in-memory => you will have to use an out-of-process storage for this session. Sessions are way too much of a hassle.
You can't really pass it in the view.
Instead, consider storing the exception in TempData in the controller that renders the view....
public ActionResult DisplayErrorAndOptionToEmailIt()
{
TempData["LastError"] = m.Exception;
return View();
}
and then when the request comes in retrieve it from temp data and email it
public ActionResult SendTheEmail()
{
var e = TempData["LastError"] as Exception;
if (e != null)
{
EmailHelper.SendExceptionEmail(e);
}
return View();
}
On a side note, it's not the best practice to store complete objects. If possible, store only what you need:
public ActionResult DisplayErrorAndOptionToEmailIt()
{
TempData["LastError"] = m.Exception.Message;
return View();
}

How to access a query string in MVC

I was getting the query string back using:
public ActionResult Index(int id)
{
var queryString = Request["myQueryString"];
}
Then I looked at:
help-testing-mvc3-controller-that-accesses-querystring
Which states:
It is against MVC's design pattern to use HttpRequest directly. You can access the query string variables on your action as parameters.
I don't really understand this. Is what I've done against the design pattern? If it is why is that and how could it be done?
It breaks the concept of model binding. It also gets complicated with unit testing and trying to new up a new HttpContext for a test. If it was just a parameter, you could just pass the value.
The preferred (and easier to read) method would be:
public ActionResult Index(int id, string myQueryString)
{
...
}
Your action method should take most of the data submitted from your form. One of the strengths of MVC is the model binding it has within it. Check out this page, as it has a good example of this:
http://www.codeproject.com/Articles/159749/ASP-NET-MVC-Model-Binding-Part1
You can accept literals (string, bool, etc.) but also strongly typed objects in your action methods.

ASP.NET MVC, JSON post to controller action with FormCollection parameter

I have a bunch of controller actions mostly used for saving data to backend storage. For now most of them use a signature like this:
//
// POST: /WidgetZone/Create
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(FormCollection collection)
as you can see, it accepts FormCollection. This works fine with classic user views. Now I want to JSON- enable these actions. And I do it using JsonPox action filter like this:
//
// POST: /WidgetZone/Create
[JsonPox]
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(FormCollection collection)
Will this work when the action expects FormCollection?
For instance this work without issues (of course I construct Json object in my JavaScript client-side to pass it into this action):
//
// POST: /WidgetZone/Create
[JsonPox]
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(string id, string description)
It is all about a task of converting postback UI into asynchronous one, so that saves and updates would be done async. Am I on the right track? I do think that developing separate Json, XML or classic ViewResult actions is not the best way to go.
Help appreciated
This filter is based on the the OnActionExecuted method which is run after the action method is executed in order to JSON or XML serialize the returned model. What you have as input to your action method is not important. Once the action finishes execution the filter will look for the model that you stored in the ViewResult and serialize it according to the Content-Type header that's passed in the request.

How does the ASP.NET MVC UpdateModel() method work?

I'm working on my first .NET MVC application and using the NerdDinner tutorial as a reference point. One point that is intriguing me at the moment is the UpdateModel() method. (I don't like using things I don't really understand.)
Taken from the NerdDinner tutorial -
//
// POST: /Dinners/Edit/2
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(int id, FormCollection formValues) {
Dinner dinner = dinnerRepository.GetDinner(id);
UpdateModel(dinner);
dinnerRepository.Save();
return RedirectToAction("Details", new { id = dinner.DinnerID });
}
My main question is how does the UpdateModel() get access to the formValues passed in the Edit method? Why is the collection not passed in explicitly as a parameter to the method?
UpdateModel() is a Controller helper method that attempts to bind a bunch of different input data sources (HTTP POST data coming from a View, QueryString values, Session variables/Cookies, etc.) to the explicit model object you indicate as a parameter. Essentially, it is only for model binding.
If you express the input parameters for your Action as a strongly-typed model (like a View Model), you've already taken all of the steps that are done behind the scenes when UpdateModel() is called. If you retrieve an object from the DataContext and edit its properties, SaveChanges() is all you need to push the updates back to the database (in this case, Save()).
Example:
//
// POST: /Dinners/Edit/2
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(DinnerViewModel incoming) {
var dinner = dinnerRepository.GetDinner(incoming.DinnerID);
dinner.Description = incoming.Description;
dinnerRepository.Save();
return RedirectToAction("Details", new { id = incoming.DinnerID });
}
However, there is a use-case for using UpdateModel() with a strongly-typed model: when you are passing in a strongly-typed model and want its values to directly replace those of an entity from the database (provided they are all named and typed the same). In this case, you would retrieve the object, use UpdateModel() on it, and its model binding operation will pull in any similarly-named and typed properties from the strongly-typed object to the retrieved object. In other words, it will perform reflection for you.
So, like your example, if you want all properties to update without specifying which to update, and your strongly-typed model and database model have similarly-named properties, you would still want to use UpdateModel() to take advantage of the reflection.
Example:
//
// POST: /Dinners/Edit/2
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(DinnerViewModel incoming) {
var dinner = dinnerRepository.GetDinner(incoming.DinnerID);
UpdateModel(dinner);
dinnerRepository.Save();
return RedirectToAction("Details", new { id = incoming.DinnerID });
}
The only advantage here (over using a FormCollection object) is that you'd have access to all other properties of the strongly-typed object (as shown by incoming.DinnerID).
Conclusion: if you're translating a strongly-typed object to a derived object, it's probably easiest to use UpdateModel(). However, it's largely unnecessary if you are simply updating a few properties of the derived object. Also, be aware that use of the Entity Framework (instead of something like Linq to SQL) makes all of this moot, as it can relate strongly-typed objects and derived objects with its own methods.
It does inspect all the HttpRequest inputs such as Form, QueryString, Cookies and Server variables. I think in this order.
Instead of passing Model object as a parameter to "Post()" action method, we are creating an instance of an Model object within the "Post()" function, and updating it using "UpdateModel()" function. "UpdateModel()" function inspects all the HttpRequest inputs such as posted Form data, QueryString, Cookies and Server variables and populate the employee object.
e.g.
[HttpPost]
[ActionName("Create")]
public ActionResult Create_Post()
{
EmployeeBusinessLayer employeeBusinessLayer =
new EmployeeBusinessLayer();
Employee employee = new Employee();
UpdateModel(employee);
employeeBusinessLayer.AddEmmployee(employee);
return RedirectToAction("Index");
}

Resources