views:

31

answers:

1

I'm not even quite sure where to start explaining this problem. I've been working on this for about the past 10 hours without a clue as to what the root cause is. If any additional details are needed, I'd be happy to provide. I'm just guessing at what is relevant at this point.

I have an MVC2 site with routes set up by by Steve Hodgkiss' wonderful RestfulRouting package, using the default route setup with nested controllers (e.g. /customer/{custid}/location/{locid} and such).

In this, I have one particular model that is giving me issues. For some reason, when the create page post's the data back to my server, the ModelName property in the ModelBindingContext object passed to the DefaultModelBinder (well, my custom class inherited from DefaultModelBinder, to handle grabbing objects from a repository). This happens only for this one model. And I can't spot the differences at all.

The broken model

public class RemedialItem : Entity
{
   public virtual int Id { get; set; }
....

A working model:

public class Customer : Entity
{
   public virtual int Id { get; set; }
....

Entity is just an empty class used as a marker for Reflection use.

The broken controller method in RemedialItemController.cs

[HttpGet]
public ActionResult New(int? locationId, int? applianceId)
{
   var model = ViewModelFactory.Create<CreateRemedialItemViewModel>();
   model.Categories = (from c in repository.Query<RemedialItemCategory>()
                       orderby c.Name
                       select c).ToList();
   model.RemedialItem = new RemedialItem();
   return View(model);
}

A working controller method in CustomerController.cs

[HttpGet]
  public ActionResult New()
  {
     var viewModel = ViewModelFactory.Create<SingleCustomerViewModel>();
     viewModel.Customer = new Customer();

     return View(viewModel);
  }

ViewModelFactory is an injected class that handles setting up some basic properties common to all view models (mainly is the user logged in and user details right now)

A broken viewmodel:

public class CreateRemedialItemViewModel : ViewModelBase
{
   public RemedialItem RemedialItem { get; set; }
   public IList<Location> Locations { get; set; }
   public IList<Appliance> Appliances { get; set; }
   public IList<RemedialItemCategory> Categories { get; set; }
}

A working ViewModel:

public class SingleCustomerViewModel : ViewModelBase
{
   public Customer Customer { get; set; }
}

ViewModelBase contains a handful of properties populated by the ViewModelFactory.

The broken form in thew New view for RemedialItem:

 <% using(Html.BeginForm("Create","RemedialItem",FormMethod.Post))
 {%>
 <%: Html.AntiForgeryToken() %>   
 <fieldset>      
   <legend>General</legend>      
   <div>
      <%: Html.LabelFor(m=>m.RemedialItem.Category) %>
      <%:Html.DropDownListFor(m=>m.RemedialItem.Category.Id, new  SelectList(Model.Categories,"Id","Name")) %>
   </div>
   <div>
      <%: Html.LabelFor(m=>m.RemedialItem.Item) %>
      <%: Html.TextAreaFor(m=>m.RemedialItem.Item) %>
   </div>
   <div>
      <%: Html.LabelFor(m=>m.RemedialItem.Note) %>
      <%: Html.TextAreaFor(m=>m.RemedialItem.Note) %>
   </div>
   <input type="submit" value="Create Item" />
 </fieldset>
 <%}%>  

A working New view:

<% using (Html.BeginForm("Create","Customer",FormMethod.Post)) {%>
    <%: Html.ValidationSummary(true) %>
    <%:Html.AntiForgeryToken() %>
    <fieldset>
        <legend>Fields</legend>
        <p>
        <%: Html.LabelFor(m=>m.Customer.Name) %>
        <%: Html.TextBoxFor(m=>m.Customer.Name) %>
        </p>
        <p>
        <%: Html.LabelFor(m=>m.Customer.Street) %>
        <%: Html.TextBoxFor(m=>m.Customer.Street) %>
        </p>
        [...tl;dr...]
        <p>
            <input type="submit" value="Create" />
        </p>
    </fieldset>

<% } %>

Both produce similar field names:

Broken:

<label for="RemedialItem_Item">Item</label>
<textarea cols="20" id="RemedialItem_Item" name="RemedialItem.Item" rows="2">
</textarea>

Working:

<label for="Customer_Name">Name</label>
<input id="Customer_Name" name="Customer.Name" type="text" value="" />

I apologize for the overly long code dump, in short: The working set of stuff, when posted back on the create form, has the ModelName set to Customer. The broken stuff is an empty string

Is there something I'm missing? Has anyone encountered something like this before?

A: 

I found the issue. In the ViewModel the property that held the instance of RemedialItem to display was called RemedialItem. In the action it posted to, the parameter that took the RemedialItem instance was called item, and that broke everything.

In short, when using ViewModels, make sure the parameter name that takes an object from the ViewModel is the same as the property name in the viewmodel.

There went my day.

Matt S