MVC Model Binding Persisting Complex Properties on Model - asp.net-mvc

I have the following setup
public class Profile
{
//Some Properties
//Some Methods
}
/*Model Class*/
public class LineItem
{
public Profile Profile {get;set;}
}
In my controller. I have the two following Actions
public ActionResult GetRequest(){
LineItem model = new LineItem();
model.Profile = new Profile(){/*Initialize Properties*/};
return View(model);
}
public ActionResult PostRequest(LineItem item(){
item.Profile .... /*Profile Is Null*/
return View(...);
}
I have verified that the Profile property on the LineItem model class is set on the GetRequest, and returned to the view, but I want to know how to persist that complex property when that model is submitted. I know that people typically use hidden fields for model persistance, and that the session is also available, but it seems that I should be able to tell the ModelBinder sometimes (possibly through an attribute or annotation) to persist complex properties on my model.
How can this be done?

You can use #Html.HiddenFor(model => model.Profile.Id) #Html.HiddenFor(model => model.Profile.Name) etc. for each property of Model.Profile you need to persist. You shouldn't touch model binder - it will be binded automatically by using standart model binder.
If you use other html helpers, that generates input tags for some property - you shouldn't use #Html.HiddenFor() for this property.

Related

MVC Model Updating

I have a Model which is a business layer class and I pass that to the view through the controller in the following manner:
public ActionResult Edit(int id)
{
var MyModel = MyDatabaseInstance.Listings.GetByID(id);
return View(MyModel);
}
In the control for the update I have the following:
[HttpPost]
public ActionResult Edit(int id, FormCollection collection)
{
try
{
// TODO: Add update logic here
return RedirectToAction("Index");
}
catch
{
return View();
}
}
What I really want is to be able to get back the model object that I have used during the initial binding. Is that possible? If I change the arguments of the edit as such:
public ActionResult Edit(Listing MyModel)
it complains that there is "No parameterless constructor defined for this object." and my model cannot have a parameterless constructor.
I would suggest that you use a ViewModel rather than binding directly to the entity from your database.
Using a ViewModel has the following advantages (not exhaustive).
Views often have specific requirements to how you display data. If you use the model from your DB then your going to endup adding unnecessary properties to your model.
Security, you don't want to expose properties on your model to automatic binding when posting back to the controller.
Validation requirements may be different for your view than your entity model.
Just easier to change a ViewModel if your presentation requirements change.

How to use ModelState to clear my form after I post my form data,my view model is a viewmodel which has two models?

my view model is a viewmodel which has two models,
publish class ViewModel
{
public Student StudentModel{get;set;}
public Teacher TeacherModel {get;set;}
}
I has two forms called StudentForm and TeacherForm,Which binding StudentModel and TeacherModel respectively.Now I fill in some data in StudentForm and submit it. I use ModelState.Clear(); to clear my StudentForm data ,but It seems not only clear the data of the StudentForm but also the TeacherForm,How can I only clear the data of the StudentForm?
to shorten, you may just do
foreach (var key in ModelState.Keys.Where(m => m.StartsWith("StudentModel")).ToList())
ModelState.Remove(key);
In such case you will have to remove each property of StudentModel from your ModelState i.e.
Syntax:
ModelState.Remove("PropertyName");
Example:
ModelState.Remove("Id");
ModelState.Remove("Name");
ModelState.Remove("Marks");
Edit: For specific model property in among two
ModelState.Remove("StudentModel.Name");
ModelState.Remove("StudentForm"),Here is the MSDN link for this, http://msdn.microsoft.com/en-us/library/dd470162%28v=vs.108%29.aspx
While returning the model from action you can reinitialize the student object
For eg:
[HttpPost]
public ActionResult ActionName(ViewModel model)
{
model.StudentModel = new StudentModel();
return View(model);
}
Note: ModelState Key contains your ViewModel Name, like:
ModelState.Remove("YourViewModelName.YourPropertyName");

MVC sending data from View to Controller

I am quite new to MVC 3.
I know how to send a strongly typed object from a Controller to a View. What I have now, is a View which contains a table/form which consists of that data.
The user can change that data whilst they're are in that View (html page).
When they click on "Save", how do I send the data from the View back to the Controller so that I can update my database.
Do I overload the Controller method so that it accepts a parameter of the model type? Can you please provide some source code.
(Please do not show code of persisting data to a database, I know how to do that part).
Thank you very much for helping me.
I would also prefer using #Html.BeginForm()
I like creating an action method made for my post data. So let's say you have a UserViewModel:
public class UserViewModel
{
public int Id { get; set; }
public string Name { get; set; }
}
Then a UserController:
public class UserController
{
[HttpGet]
public ActionResult Edit(int id)
{
// Create your UserViewModel with the passed in Id. Get stuff from the db, etc...
var userViewModel = new UserViewModel();
// ...
return View(userViewModel);
}
[HttpPost]
public ActionResult Edit(UserViewModel userViewModel)
{
// This is the post method. MVC will bind the data from your
// view's form and put that data in the UserViewModel that is sent
// to this method.
// Validate the data and save to the database.
// Redirect to where the user needs to be.
}
}
I'm assuming you have a form in your view already. You'll want to make sure that the form posts the data to the correct action method. In my example, you'd create the form like so:
#model UserViewModel
#using (Html.BeginForm("Edit", "User", FormMethod.Post))
{
#Html.TextBoxFor(m => m.Name)
#Html.HiddenFor(m => m.Id)
}
The key to all this is the model binding that MVC does. Make use of the HTML helpers, like the Html.TextBoxFor I used. Also, you'll notice the top line of the view code I added. The #model tells the view you'll be sending it a UserViewModel. Let the engine do work for you.
Edit: Good call, did that all in Notepad, forgot a HiddenFor for the Id!
In MVC, the act of scraping out data from POST or GET HttpRequests is referred to as Model Binding - there are plenty of SO questions relating to this.
Out of the box, MVC will bind your Get and Post variables based on convention, e.g. a form field with the name 'FormName' will be bound back to a parameter on your controller with the same name.
Model binding also works for objects - MVC will instantiate an object for your controller, and set the properties with the same name as your form.

MVC 3 Model Binding without Key

I have a question about ASP.NET MVC3 model binding. If I have a class I'm trying to use as a model, but I don't want the key put on the page, the model doesn't bind on a POST. Here is an example:
//Data Model
public class MyModel
{
[Key]
public string MyKey {get;set;} //Perhaps this is an ssn that I don't want on the form.
public string MyValueToGet {get;set;} //This is the value I want the user to enter.
}
//Conroller code.
public ViewResult Index()
{
MyModel model = new MyModel{ MyKey = "SecretInfo", MyValueToGet = "" };
return View(new model);
}
public ActionResult Edit(MyModel model)
{
repository.SaveChanges(model)
}
//View code.
#using(Html.BeginForm("Edit", "Home", FormMethod.Post))
{
Enter a value: #Html.EditorFor(m => m.MyValueToGet)
<input type="submit" value="Salve" />
}
So my problem is that model is null when the Edit method is called upon form submission. I can fix this by putting MyKey somewhere on the page (perhaps as a hidden field), but that is unacceptable if it is some sort of sensitive data. Is there a way to solve this problem? I am new to MVC, so I appreciate any help.
Create another unique but otherwise meaningless identifier like an (auto increment int) and use that to bind.
in other words modify your model to something like:
public class MyModel
{
[Key]
public int ID {get; set;}
public string MyKey {get;set;} //Now this can be sensitive, it doesn't matter because you no longer rely on it.
public string MyValueToGet {get;set;} //This is the value I want the user to enter.
}
EDIT
I believe your best choice would be to change the MyModel object, as it's design is flawed. The primary key in the majority of cases (and I think this is one of them) should be a simple auto incrementing integer, meaningless apart from it's role as the table's key.
While Luke's suggestion to use Session is a viable option and a solution that would work, I would personally do something similar to what I'll explain here, as it would seem to me to be more of the 'mvc way' of doing things.
Data model:
Either change your current model to something like what I suggest above, or, if that is not feasible for whatever reason (breaking dependancies or FK relationships), create a new table that can be used as a join, or proxy, if you will:
public class Proxy
{
public int ProxyId {get;set;}
public MyModel MyModel {get; set;}
}
Obviously, you'd have to do some work to populate this table, but you would then be able to use it to fetch records from MyModel without accessing the MyKey property directly.
It's not considered good practice to use your data models directly in your views, so you want to create a view model as well
public class MyModelViewModel
{
public int ModelId {get; set;}
public string ModelValueToGet {get; set;}
}
Notice we don't even need the key containing sensitive data in the view model.
Then type your view to the viewModel, not the data model, and include a hidden field for the ModelId
#using(Html.BeginForm("Edit", "Home", FormMethod.Post))
{
Enter a value: #Html.EditorFor(m => m.ModelValueToGet)
#Html.HiddenFor(m => m.ModelId)
<input type="submit" value="Save" />
}
Now in your controller you have your get method
public ViewResult Index()
{
//fetch the users record from the database
//if you're using the Proxy table, you'll want to write a LINQ query here
//instantiate a viewModel and populate it's properties using the fetched record
//remember, the viewModel.ModelId should be set to MyModel.ID or Proxy.ProxyId
//render the view
}
And the post method
public ViewResult Edit (MyModelViewModel viewModel)
{
//fetch the users record from the database using viewModel.ModelId
//If you're using the proxy table, you'll need to use that LINQ query again here
//update the record you fetched with the new data the user just entered
//you have complete control here of what gets updated and what stays the same
//pass the updated record to the repository to save the changes.
//redirect the user to be on their merry way
}
I think that's about as well as I can lay it out. Hope it makes sense.
An alternative is to encrypt the id before sending it to the client. Check this post for more information on how to accomplish this.
Asp MVC 3: Modifiy Values Sent to View

Custom Model Binder - How to revalidate

I'm using ASP.NET MVC 2 and want to figure out how to re-trigger the validation on my model after it has been populated using a custom binder.
So, I start with a couple of EF classes which are associated, Booking and Traveller (each booking can have one or more travellers)
Here's the buddy class I'm using to place balidation on Booking:
[MetadataType(typeof(Booking_Validation))]
public partial class Booking {
// partial class compiled with code produced by VS designer
}
[Bind(Include="Name")]
public class Booking_Validation {
[Required(ErrorMessage="Booking name required")]
public string Name { get; set; }
[AtLeastOneTraveller(ErrorMessage="Please enter at least one traveller")]
public EntityCollection<Traveller> Travellers;
}
public class AtLeastOneTraveller : ValidationAttribute {
public override bool IsValid(object value) {
if (value != null)
return ((EntityCollection<Traveller>)value).Count > 0;
return true;
}
}
I use a custom model binder to populate the booking and it's associated travellers, except that ModelState.IsValid seems to be set even before my custom model binder has had a chance to add the travellers to the booking object, even even after doing so, ModelState["Travellers"] still contains the validation error saying there must be at least one traveller attached.
Is there any way to re-trigger validation after the custom model binder has done its thing?
Have you tried the TryValidateModel method on the Controller class?
http://msdn.microsoft.com/en-us/library/system.web.mvc.controller.tryvalidatemodel.aspx
try this:
http://shazwazza.com/post/Custom-MVC-ModelBinder-with-Complex-ModelsObjects-using-built-in-MVC-Validation.aspx
Once you have fixed the error items, you can clear the ModelState using
ModelState.Clear();
and then revalidate using
ModelState.IsValid

Resources