ASP.NET MVC DefaultModelBinder with nested lists

2019-04-04 07:17发布

问题:

I have a View with a table representing an employee's timesheet. Days across the top, projects down the side, with each day/project intersection containing two values for regular hours and overtime.

The (simplified) class definitions for the page model are:

public class TimesheetFormModel {
    public List<Project> Projects;
    // other things...
}
public class Project {
    public string Name;
    public List<WorkUnit> WorkUnits;
}
public class WorkUnit {
    public DateTime Date;
    public decimal RegularHours;
    public decimal OvertimeHours;
}

The form elements on the page are named as follows in an attempt to get the DefaultModelBinder to pick up on them.

model.Projects[0].Name // "New Project"
model.Projects[0].WorkUnits[0].Date // "5/23/2009 12:00:00 AM"
model.Projects[0].WorkUnits[0].RegularHours // 0
model.Projects[0].WorkUnits[0].OvertimeHours // 0

model.Projects[0].WorkUnits[1].Date // "5/24/2009 12:00:00 AM"
model.Projects[0].WorkUnits[1].RegularHours // 0
model.Projects[0].WorkUnits[1].OvertimeHours // 0

model.Projects[0].WorkUnits[2].Date // "5/25/2009 12:00:00 AM"
model.Projects[0].WorkUnits[2].RegularHours // 0
model.Projects[0].WorkUnits[2].OvertimeHours // 0

// etc.

When the view is submitted however, the model parameter isn't being completely populated. model.Projects contains projects, but the Project's WorkUnits field is empty. Does the DefaultModelBinder support nested collections like I'm trying to do? If not, what should I do?

回答1:

I eventually figured out why DefaultModelBinder wasn't picking up on the properties of WorkUnit: Because they weren't properties, they were fields. DefaultModelBinder only works with properties. Changing the class definition of WorkUnit and Project to use fields made everything click:

public class Project {
    public IList<WorkUnit> WorkUnits { get; set; }
    public string Name { get; set; }
}

public class WorkUnit {
    public DateTime Date { get; set; }
    public decimal RegularHours { get; set; }
    public decimal OvertimeHours { get; set; }
}

(Note: The source code in the original question had Project.Name defined as a field, in my actual code it was a property. This is why the Projects list was getting populated but WorkUnits wasn't.)