views:

163

answers:

2

The following is the code for my controller:

    [AcceptVerbs(HttpVerbs.Post)]
    public ActionResult Edit(int id, Actor actor)
    {

        try
        {
            actorRepository.Save(actor);
            return RedirectToAction("Index");
        }
        catch
        {
            return View("Edit");
        }
    }

The view that calls has a partial view that is strongly typed to the Actor class. For some reason, there are a few fields that are not being bound to the class. The fields in question are Address, Address2, and ZipCode. They are populated on the page, but they come back null. Every other field is there, just not these.

Any ideas? Also, how could I write a unit test to duplicate this behavior?

Update public class Actor { public string MiddleName{ get; set;}

    [Required(ErrorMessage = "First Name is Required")]
    [RegularExpression(@"\w*", ErrorMessage = "Last Name Contains Illegal Characters")]
    public string FirstName { get; set; }

    [Required(ErrorMessage = "Last Name is Required")]
    [RegularExpression(@"\w*", ErrorMessage = "Last Name Contains Illegal Characters")]
    public string LastName { get; set; }

    [DataType(DataType.PhoneNumber, ErrorMessage = "Please Enter a Valid Phone Number")]
    [Required(ErrorMessage = "Phone Number is Required")]
    public string PhoneNumber { get; set; }

    [Required(ErrorMessage = "Address is Required")]
    [RegularExpression(@"\w*", ErrorMessage = "Address Contains Illegal Characters")]
    public string Address { get; set; }

    [RegularExpression(@"\w*", ErrorMessage = "Address2 Contains Illegal Characters")]
    public string Address2 { get; set; }

    [RegularExpression(@"\w*",ErrorMessage = "State Contains Illegal Characters")]
    [Required(ErrorMessage = "State is Required")]
    public string State { get; set; }

    [Required(ErrorMessage = "Zip Code is Required")]
    [RegularExpression("\b[0-9]{5}(?:-[0-9]{4})?\b",ErrorMessage = "Please Enter a Valid Zip Code")]
    public string ZipCode { get; set; }

    [Required(ErrorMessage = "Even in theater, you have to choose a gender")]
    public bool? Gender { get; set; }

    [Required(ErrorMessage = "Cell Phone Number is Required")]
    public string CellPhone { get; set; }

    public int ActorId { get; set; }

    [DataType(DataType.MultilineText, ErrorMessage = "Illegal Characters in Notes")]
    public string Notes { get; set; }

    [Required(ErrorMessage = "Email Address is Required")]
    [DataType(DataType.EmailAddress)]
    public string EMail { get; set; }

    [Required(ErrorMessage = "City Is Required")]
    public string City {get; set;}
}

<fieldset>
<legend>Fields</legend>
<p>
    <label for="MiddleName">MiddleName:</label>
    <%= Html.EditorFor(m=>m.MiddleName) %>
    <%= Html.ValidationMessage("MiddleName", "*") %>
</p>
<p>
    <label for="FirstName">FirstName:</label>
    <%=Html.EditorFor(m=>m.FirstName) %>
    <%= Html.ValidationMessage("FirstName", "*") %>
</p>
<p>
    <label for="LastName">LastName:</label>
    <%= Html.TextBox("LastName", Model.LastName) %>
    <%= Html.ValidationMessage("LastName", "*") %>
</p>
<p>
    <label for="PhoneNumber">PhoneNumber:</label>
    <%= Html.TextBox("PhoneNumber", Model.PhoneNumber) %>
    <%= Html.ValidationMessage("PhoneNumber", "*") %>
</p>
<p>
    <label for="Address">Address:</label>
       <%=Html.EditorFor(m=>m.Address) %>
       <%= Html.ValidationMessage("Address", "*") %>
</p>
<p>
    <label for="Address2">Address2:</label>
       <%=Html.EditorFor(m=>m.Address2) %>
    <%= Html.ValidationMessage("Address2", "*") %>
</p>
<p>
    <label for="State">State:</label>
    <%= Html.TextBox("State", Model.State) %>
    <%= Html.ValidationMessage("State", "*") %>
</p>
<p>
    <label for="ZipCode">ZipCode:</label>
    <%= Html.TextBox("ZipCode", Model.ZipCode) %>
    <%= Html.ValidationMessage("ZipCode", "*") %>
</p>
<p>
    <label for="Gender">Gender:</label>
    <%= Html.TextBox("Gender", Model.Gender) %>
    <%= Html.ValidationMessage("Gender", "*") %>
</p>
<p>
    <label for="CellPhone">CellPhone:</label>
    <%= Html.TextBox("CellPhone", Model.CellPhone) %>
    <%= Html.ValidationMessage("CellPhone", "*") %>
</p>
<p>
    <label for="ActorId">ActorId:</label>
    <%= Html.TextBox("ActorId", Model.ActorId) %>
    <%= Html.ValidationMessage("ActorId", "*") %>
</p>
<p>
    <label for="Notes">Notes:</label>
    <%= Html.TextBox("Notes", Model.Notes) %>
    <%= Html.ValidationMessage("Notes", "*") %>
</p>
<p>
    <label for="EMail">EMail:</label>
    <%= Html.TextBox("EMail", Model.EMail) %>
    <%= Html.ValidationMessage("EMail", "*") %>
</p>
<p>
    <label for="City">City:</label>
    <%= Html.TextBox("City", Model.City) %>
    <%= Html.ValidationMessage("City", "*") %>
</p>
<p>
    <input type="submit" value="Save" />
</p>
</fieldset>
A: 

Here's how you can test model binders:

http://stackoverflow.com/questions/253574/how-to-test-custom-model-binders-in-asp-net-mvc

Only you will test DefaultModelBinder.

As for fields, you haven't shown the Actor source code. But since you talk about fields, I'd guess that you really use fields - but ASP.NET MVC model binders won't bind to fields - only to properties. So if you have

public class Actor
{
   public string ZipCode;
}

you'll need to change to

public class Actor
{
   public string ZipCode { get; set; }
}

Another reason may be that you have

public class Actor
{
   public Address Address { get; set; }
}

and you use partial to show Address; so it's named "ZipCode" but it has to be named "Address.ZipCode". Or maybe your Address doesn't have parameterless constructor... In other words, without seeing the source code there's too much to guess.

With update info, this is non-sense. Bad fields look the same as good ones. But try Html.TextBox instead of EditorFor. Try to look at POST data using FireBug (or IE Dev), check that fields names and values come correctly. If they do - the problem is with model binder, if they're bad/absent - something's wrong with the page. Also, try to check ModelState.IsValid in your controller action, as well as ModelState errors.

queen3
I'm using auto-properties, which are just syntactic sugar for the fields on the implementation side. More to come in a moment, it was very late when I wrote the post and I didn't have the brains to keep posting code.
KevDog
The fields were originally Html.TextBox, I switched them to EditorFor to see if it made a difference. It didn't, and since they are rendering identically on the page using both methods, I'm pretty much ruling out that as a cause. As a check, I created a new strongly typed view for the Add method and got the same error with the same fields.Time to dig a little deeper.
KevDog
+1  A: 

There's a ModelMetaData property called ConvertEmptyStringsToNull (default = true) that converts empty strings to NULL in the DefaultModelBinder class of ASP.MVC.

You can either change this via a DataAnnotation on each property like this:

[DisplayFormat(ConvertEmptyStringToNull=false)]

or you can create your own ModelBinder and inherit from DefaultModelBinder and just override the GetPropertyValue method to ignore this setting like this:

ModelBinders.Binders.DefaultBinder = new Delphi.Mvc.KeepStringsBinder(); ModelBinders.Binders.Add(typeof(ObjectBase), new Delphi.Mvc.KeepStringsBinder());

Jared