10

I have the following POCO classes:

public class Location
    {
        public int LocationId { get; set; }
        public string Name { get; set; }
        public string Street { get; set; }
        public string City { get; set; }
        public string State { get; set; }
        public string ZipCode { get; set; }
        public string Country { get; set; }
        public float? Latitude { get; set; }
        public float? Longitude { get; set; }
        public string PhoneNumber { get; set; }
        public string EmailAddress { get; set; }
        public string Website { get; set; }
        public virtual ICollection<Program> Programs { get; set; }
        public virtual ICollection<PlayerType> PlayerTypes { get; set; }
    }

public class PlayerType
    {
        public int PlayerTypeId { get; set; }
        public string Name { get; set; }
        public int SortOrder { get; set; }
        public bool IsActive { get; set; }
        public virtual ICollection<Location> Locations { get; set; }
    }

And a View Model Class

public class LocationViewModel
    {
        public Location Location { get; set; }
        public IList<PlayerType> SelectPlayerTypes { get; set; } 

        public LocationViewModel()
        {
            Location = new Location();
        }

    }

Within my Create Form, I have defined the model as

@model Locator.Models.LocationViewModel

And have fields like the following:

div class="editor-label">
    @Html.LabelFor(model => model.Location.Name)
</div>
<div class="editor-field">
    @Html.EditorFor(model => model.Location.Name)
    @Html.ValidationMessageFor(model => model.Location.Name)
</div>

In my controller to handle the POST I have

[HttpPost]
        public ActionResult Create(LocationViewModel location)
        {
            if (ModelState.IsValid) {
                locationRepository.InsertOrUpdate(location.Location);
                locationRepository.Save();
                return RedirectToAction("Index");
            }
            location.SelectPlayerTypes = golferTypeRepository.All.Where(p => p.IsActive).ToList();
            return View(location);
        }

The problem is that I have a Location object but none of the properties are to set to the values entered in the form.

Am I doing something wrong here?

Thanks

Mike
  • 2,561
  • 7
  • 34
  • 56
  • I might be off, but I think your initializing `Location` in the constructor is throwing it off. Can you try removing that line (and instantiating the property outside of the model itself)? – Kirk Woll May 24 '12 at 19:27
  • I instantiated it in the Create ActionResult (not the post) and now when I post, Location is null. – Mike May 24 '12 at 19:31
  • Yeah, that's what I hoped you'd try. Oh well, guess that wasn't the issue. – Kirk Woll May 24 '12 at 19:35
  • When you say none of the properties are to set to the values entered in the form - where/how are you determining this? – user1166147 May 24 '12 at 20:06
  • 1
    I updated the Create constructor to be public ActionResult Create(LocationViewModel model, Location location) and it's working. – Mike May 24 '12 at 20:09

10 Answers10

52

Here's the problem:

[HttpPost]
public ActionResult Create(LocationViewModel location)

Do you see it? It's the name of your action argument: location.

Look at your view model now, it has a property named Location:

public Location Location { get; set; }

This confuses the model binder. It no longer knows whether you need to bind the LocationViewModel or its property.

So simply rename to avoid the conflict:

[HttpPost]
public ActionResult Create(LocationViewModel model)
Darin Dimitrov
  • 1,023,142
  • 271
  • 3,287
  • 2,928
38

Just to contribute another possible reason: I've spent couple of hours looking for an error in my code and the reason for the binder not working in my case was using a model with public fields rather than public properties:

public int CustomerName;

and it should be:

public int CustomerName { get; set; }

Been working on ASP.NET MVC for 3 years now and I never came across this before. Hope it saves somebody some frustration ;)

harriyott
  • 10,505
  • 10
  • 64
  • 103
Grzegorz
  • 389
  • 3
  • 3
  • 1
    Wish granted - Saved me from blowing a gasket today. I had all fields and no properties. All the HTML links had no detail in them. Thanks for adding this additional answer, @Grzegorz. – edhubbell Jul 12 '13 at 19:14
  • God, I spent so much time trying to figure this out! Thank you! – LukeP May 23 '14 at 21:51
  • This answer also helped remind me that properties need to be **public**. – l p May 25 '16 at 21:55
  • This was the issue in my case. Thank You! – J86 Jun 28 '16 at 08:36
  • OMG that was my issue and i was searching for it so long. Shame on this model binder, Newtonsoft can do it without getter and setter :( – Reiner Sep 07 '17 at 21:40
  • I am sorry this is so late, but it might help somebody. I had a property: public string DeliveryAddress { get; internal set; } The "internal" obviously stopped the model binder. I think the internal option was set when I used IntelliSense to add the property to the view model class automatically. – Liam Aug 05 '22 at 10:02
5

Also late to join the party, but after 3 years of asp.net mvc, I've came across that disabled inputs are not posted, so the model binder cannot bind them of course. See here:

"Disabled elements in a form will not be submitted".

Better use readonly="readonly" over disabled. See here.

Community
  • 1
  • 1
ilans
  • 2,537
  • 1
  • 28
  • 29
3

Just to reiterate what Tod was saying, the model binder needs a 'name' attribute on the HTML element to map the properties. I was doing a quick test form by hand and only used the 'id' attribute to identify my elements.

Everything fell into place when I added the 'name' attribute.

<input type="text" id="Address" name="Address" />
2

Let me add here yeat another reason why the model binder would not work properly.

I had a model with the property ContactPhone, somewhere along the way I decided to change the name of this property to Phone, then all of a sudden model binding for this property stopped working when I was trying to create a new instance.

The problem was on the Create action in my controller. I have used the default Visual Studio scaffolding and it has created the method signature as this:

public ActionResult Create([Bind(Include = "Id,ContactPhone, ...")] Customer customer)
{ ... }

Pay attention to the Bind attribute, the scaffolder created the fields using the original name ContactPhone and as this is a string, it was not refactored. As the new field Phone was not being included, it's value was ignored by the model binder.

I hope this saves someone's time.

Good luck!

Anderson Fortaleza
  • 2,449
  • 20
  • 22
1

And another reason: Normally I use the built in editors or displays for and would have never encountered this issue. However in this case I required a semi-custom control. Basically a drop down with lots of data attributes on the options.

What I was doing, for my select tag was:

<select name="@Html.IdFor(x => x.SubModel.ID)" id="@Html.IdFor(x => x.SubModel)" class="form-control select-control">

Now everything was posting back and to the untrained eye it looked like everything should work and bind. However the IdFor helper renders sub models with an underscore. The model binder doesn't interpret underscores as a class hierarchy indicator. What should be separating them is a dot. Which comes from NameFor:

<select name="@Html.NameFor(x => x.SubModel.ID)" id="@Html.IdFor(x => x.SubModel)" class="form-control select-control">

NameFor fixed all my issues.

Tod
  • 2,070
  • 21
  • 27
1

For anyone else still experiencing an issues with this, if you name your parameter in your post method to be the same as one of your properties, the default modelbinder will also fail.

mahlatse
  • 1,322
  • 12
  • 24
1

Mine was a bit unique case, but hope it helps someone in similar context. I had my View Model implementing IValidatableObject, and the Validate method from this interface was returning a null if success. It had to return an empty IEnumerable. Hence the binding was actually happening but crashing.

Basically when you get this issue, use Fiddler to look at posted data, make sure the posted variables match the properties(not fields!) on your view models, and put a break point on the View Model constructor to ensure the Routing Engine hits the correct POST method. Good luck!

Kabali
  • 11
  • 4
0

This saved my day. Iad the following:

public ActionResult Edit(int masterId, ConclusionView conclusion)

ConclusionView has a property named Conclusion so I nearly lost my mind.

harriyott
  • 10,505
  • 10
  • 64
  • 103
ntombela
  • 1,357
  • 2
  • 19
  • 31
0

I know it's too late to comment on this.What I did is added parameter less constructor in the ViewModel and everything worked awesome.

Passenger
  • 107
  • 2
  • 13