问题
I have a ViewModel called AssetSearchModel with a property of type: List.
When I post from the ViewModel, all the properties of my List get binded back to the model successfully, except for this one: public DomainsDto DomainControl { get; set; }
which is defined at the bottom.
That domaincontrol has a couple of properties and then its own nested list... none of its properties get set, they are all null
public class AssetSearchModel
{
public List<SearchControlModel> SearchControls { get; set; }
public AssetSearchModel()
{
}
}
public class SearchControlModel
{
/// <summary>
/// Asset identifier.
/// </summary>
[DisplayName("AssetId ID")]
public int SearchOptionID { get; set; } // this gets bound ok
[DisplayName("Asset Customer ID")]
public int AssetCustomerID { get; set; } // this gets bound ok
[DisplayName("Portal ID")]
public int PortalID { get; set; } // this gets bound ok
[DisplayName("Calling Page")]
public string CallingPage { get; set; } // this gets bound ok
[DisplayName("Display Control")]
public string DisplayControl { get; set; } // this gets bound ok
[DisplayName("Display Text")]
public string DisplayText { get; set; } // this gets bound ok
public string ControlValue { get; set; } // this gets bound ok
[DisplayName("Search Table")]
public string SearchTable { get; set; } // this gets bound ok
[DisplayName("Search Column")]
public string SearchColumn { get; set; } // this gets bound ok
[DisplayName("Source Table")]
public string SourceTable { get; set; } // this gets bound ok
[DisplayName("Source Display Column")]
public string SourceDisplayColumn { get; set; } // this gets bound ok
[DisplayName("Source Value Column")]
public string SourceValueColumn { get; set; } // this gets bound ok
[DisplayName("SourceCustomFilter")]
public string SourceCustomFilter { get; set; } // this gets bound ok
[DisplayName("SortOrder")]
public int SortOrder { get; set; } // this gets bound ok
[DisplayName("DomainControl")]
public DomainsDto DomainControl { get; set; } // THIS DOES NOT BIND
public SearchControlModel()
{
}
}
public class DomainsDto
{
public int DomainMasterId; // this does not bind
public string DisplayName; // this does not bind
public List<DomainDto> Domains; // this does not bind
public DomainsDto()
{
}
}
[DataContract(Name = "Domain", Namespace = "http://www.comcom.com/")]
public class DomainDto
{
[DataMember]
public int DomainID { get; set; } // this does not bind
[DataMember]
public string DomainName { get; set; } // this does not bind
[DataMember]
public bool Checked { get; set; } // this does not bind
public DomainDto()
{
}
}
and my view:
@for (int y = 0; y < Model.SearchControls.Count; y++)
{
@Html.Label("Search option: " + Model.SearchControls[y].DisplayText)
<br />
@Html.HiddenFor(x => x.SearchControls[y].DisplayText)
@Html.HiddenFor(x => x.SearchControls[y].CallingPage)
@Html.HiddenFor(x => x.SearchControls[y].DisplayControl)
@Html.HiddenFor(x => x.SearchControls[y].SourceCustomFilter)
@Html.HiddenFor(x => x.SearchControls[y].SearchTable)
@Html.HiddenFor(x => x.SearchControls[y].SearchColumn)
@Html.HiddenFor(x => x.SearchControls[y].SourceValueColumn)
if (Model.SearchControls[y].DomainControl != null)
{
@Html.HiddenFor(x => x.SearchControls[y].DomainControl.DisplayName)
@Html.HiddenFor(x => x.SearchControls[y].DomainControl.DomainMasterId)
if (Model.SearchControls[y].DomainControl.Domains != null)
{
for (int z = 0; z < Model.SearchControls[y].DomainControl.Domains.Count; z++)
{
@Html.HiddenFor(x => x.SearchControls[y].DomainControl.Domains[z].DomainID)
@Html.HiddenFor(x => x.SearchControls[y].DomainControl.Domains[z].DomainName)
@Html.HiddenFor(x => x.SearchControls[y].DomainControl.Domains[z].Checked)
<br />
}
}
}
<br />
<br />
<hr width=100px />
}
the html (sorry for the single line, it wouldn't let me paste it in differently)
<label for="Search_option:_Product_Lines">Search option: Product Lines</label> <input id="SearchControls_1__DisplayText" name="SearchControls[1].DisplayText" type="hidden" value="Product Lines" /> <input id="SearchControls_1__CallingPage" name="SearchControls[1].CallingPage" type="hidden" value="AssetSearch" /> <input id="SearchControls_1__DisplayControl" name="SearchControls[1].DisplayControl" type="hidden" value="M" /> <input id="SearchControls_1__SourceCustomFilter" name="SearchControls[1].SourceCustomFilter" type="hidden" value="26" /> <input id="SearchControls_1__SearchTable" name="SearchControls[1].SearchTable" type="hidden" value="AssetDomainValue" /> <input id="SearchControls_1__SearchColumn" name="SearchControls[1].SearchColumn" type="hidden" value="Brand" /> <input id="SearchControls_1__SourceValueColumn" name="SearchControls[1].SourceValueColumn" type="hidden" value="DomainCodeValueID" /> <input id="SearchControls_1__DomainControl_DisplayName" name="SearchControls[1].DomainControl.DisplayName" type="hidden" value="Product Line" /> <input id="SearchControls_1__DomainControl_DomainMasterId" name="SearchControls[1].DomainControl.DomainMasterId" type="hidden" value="26" /> <input id="SearchControls_1__DomainControl_Domains_0__DomainID" name="SearchControls[1].DomainControl.Domains[0].DomainID" type="hidden" value="43680" /> <input id="SearchControls_1__DomainControl_Domains_0__DomainName" name="SearchControls[1].DomainControl.Domains[0].DomainName" type="hidden" value="YORK" /> <input id="SearchControls_1__DomainControl_Domains_0__Checked" name="SearchControls[1].DomainControl.Domains[0].Checked" type="hidden" value="False" /> <input id="SearchControls_1__DomainControl_Domains_1__DomainID" name="SearchControls[1].DomainControl.Domains[1].DomainID" type="hidden" value="43683" /> <input id="SearchControls_1__DomainControl_Domains_1__DomainName" name="SearchControls[1].DomainControl.Domains[1].DomainName" type="hidden" value="Johnson Controls" /> <input id="SearchControls_1__DomainControl_Domains_1__Checked" name="SearchControls[1].DomainControl.Domains[1].Checked" type="hidden" value="False" /> <br />
回答1:
Ah no, the model binder works only with properties only, not fields.
So replace:
public class DomainsDto
{
public int DomainMasterId; // this does not bind
public string DisplayName; // this does not bind
public List<DomainDto> Domains; // this does not bind
public DomainsDto()
{
}
}
with:
public class DomainsDto
{
public int DomainMasterId { get; set; } // this does bind
public string DisplayName { get; set; } // this does bind
public List<DomainDto> Domains { get; set; } // this does bind
public DomainsDto()
{
}
}
But really, what's the whole point of stuffing a gazzilion of hidden fields in a form? You already have this stuff in your db or something. So simply put a hidden field containing an id that will allow you to go and fetch the data wherever this data resides. I've always wondered people why are people so desperately trying to reinvent the ViewState for MVC. It's just not the way this pattern is meant to be used. You are no longer doing classic WebForms.
Here's a rule that should drive you when designing an ASP.NET MVC view:
- whatever fields the user is supposed to modify use corresponding input fields (textboxes, radios, ddls and stuff)
- whatever fields the user is not supposed to modify, it shouldn't appear in the form. You should have at maximum one hidden field per form containing at maximum an unique identifier allowing you to retrieve the information you need
Conclusion: use view models.
So the real solution to your problem is to replace:
[DisplayName("DomainControl")]
public DomainsDto DomainControl { get; set; } // THIS DOES NOT BIND
with:
public int DomainControlId { get; set; }
and using a single hidden field for this id in the view. That's it.
来源:https://stackoverflow.com/questions/11585499/mvc-nested-model-binding-only-partially-working-what-am-i-missing