ASP.NET MVC3 ModelBinding问题 - 获取模型中的下拉值?

时间:2011-10-23 05:36:08

标签: asp.net asp.net-mvc-3

为了解决这个问题,我失去了更多的头发,所以我真的希望你能帮忙吗?

我有一个Telerik MVC网格: Grid in View mode

我有一个自定义的EditorTemplate for Address(Address.ascx) Grid in Edit mode

我的ImporterDetails模型:

   public class ImporterViewModel : IEnumerableViewModel
    {
        public int ImporterId { get; set; }

        [Required]
        [DisplayName("Importer Name *")]
        public string ImporterName { get; set; }

        [Required]
        [DisplayName("Importer Address *")]
        public Address ImporterAddress { get; set; }

        public static ImporterViewModel CreateImporter()
        {
            return new ImporterViewModel
                       {
                           ImporterName = Guid.NewGuid().ToString().Substring(0, 5),
                           ImporterAddress = Address.CreateDummyAddress(),
                       };
        }

    }

和AddressViewModel:

[Bind(Exclude = "State, Country")]
public class Address
{
    public int AddressId { get; set; }

    [DisplayName("Address Line 1")]
    [Required]
    public string Line1 { get; set; }

    [DisplayName("Address Line 2")]
    public string Line2 { get; set; }

    [DisplayName("Postcode")]
    [Required]
    [RegularExpression(RegexConstants.AUSTRALIAN_POSTCODE_PATTERN, ErrorMessage = "Invalid post code")]
    public string Postcode { get; set; }

    [DisplayName("State")]
    public State State { get; set; }

    [DisplayName("Suburb")]
    public string Suburb { get; set; }

    [DisplayName("Country")]
    public Country Country { get; set; }

    [Required]
    public int CountryId { get; set; }

    [Required]
    public int StateId { get; set; }

    /// <summary>
    /// Creates a new dummy instance of Address
    /// </summary>
    /// <returns></returns>
    public static Address CreateDummyAddress()
    {
        return new Address
                   {
                       Country = ServiceLocatorFactory.GetCodeServiceLocator<Country>().Get(x => x.CodeValue.ToLower() == "canada"),
                       State = ServiceLocatorFactory.GetCodeServiceLocator<State>().Get(x => x.CodeValue.ToLower() == "nsw"),
                       Line1 = Guid.NewGuid().ToString().Substring(0, 15),
                       Line2 = Guid.NewGuid().ToString().Substring(0, 15),
                       Suburb = "Dandenong",
                       Postcode = "2606",
                   };
    }

    public string AddressStrings
    {
        get
        {
            return ToString();
        }
    }

    public override string ToString()
    {
        // create a blank StringBuilder
        var sb = new StringBuilder();

        // add the first address line
        sb.Append(string.Format("{0}, ", Line1));

        // add the second address line
        sb.Append(string.Format("{0}, ", Line2));

        sb.Append(string.Format("{0}, ", Suburb));
        sb.Append(string.Format("{0} {1}, ", State == null ? string.Empty : State.Description, Postcode));
        sb.Append(string.Format("{0}", Country == null ? string.Empty : Country.Description));

        // and then return it as a single (formatted) string
        return sb.ToString();
    }
}

你会注意到我已经排除了状态和国家,因为如果我不这样做,当我调用TryUpdateModel(导入程序)时 - 我得到了可怕的无参数构造函数异常。我的问题是:

如何通过这种方式获取州和国家(或一般情况下,任何下拉列表)的正确ID?

为了完整起见:

Address.ascx

<div class="formElementGroupVertical">
    <%: Html.LabelFor(m => m.Line1) %>
    <%: Html.EditorFor(m => m.Line1) %>
    <%: Html.ValidationMessageFor(m => m.Line1) %>
</div>
<div class="formElementGroupVertical">
    <%: Html.LabelFor(m => m.Line2) %>
    <%: Html.EditorFor(m => m.Line2) %>
    <%: Html.ValidationMessageFor(m => m.Line2) %>
</div>
<div class="formElementGroupVertical">
    <%: Html.LabelFor(m => m.Suburb) %>
    <%: Html.EditorFor(m => m.Suburb)%>
    <%: Html.ValidationMessageFor(m => m.Suburb)%>
</div>
<div class="formElementGroupVertical">
    <%: Html.LabelFor(m => m.State) %>
    <%: Html.EditorFor(m => m.State) %>
</div>
<div class="formElementGroupVertical">
    <%: Html.LabelFor(m => m.Postcode) %>
    <%: Html.EditorFor(m => m.Postcode)%>
    <%: Html.ValidationMessageFor(m => m.Postcode)%>
</div>
<div class="formElementGroupVertical">
    <%: Html.LabelFor(m => m.Country) %>
    <%: Html.EditorFor(m => m.Country) %>
</div>

国家:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<Web.Common.Models.Country>" %>
<%@ Import Namespace="Web.Common.Models" %>
<%@ Import Namespace="Web.Common.Service" %>
<%: Html.DropDownListFor(m => m.CodeId, new SelectList(ServiceLocatorFactory.GetCodeServiceLocator<Country>().GetAll(), "CodeId", "Description"), "Please Select")%>

状态与国家相同,除了显而易见的。

任何帮助?

2 个答案:

答案 0 :(得分:2)

简答:

CountryId没有被填充,因为DropDownlistFor是&#39; For&#39; country => country.CodeId

要获得CountryId,您实际上需要将下拉列表指向它:

Html.DropDownListFor(m => m.CountryId, new SelectList(ServiceLocatorFactory.GetCodeServiceLocator<Country>().GetAll(), "CodeId", "Description"), "Please Select")%>

答案稍长:

获取下拉列表值的最简单方法是将DropDownListFor绑定到存储id的viewmodel上的属性。然后在您的控制器中,您可以生成(例如,通过数据库)该ID中的对象,并根据您的业务需求将其附加到任何模型。

这很麻烦但很直接。目前还没有通过下拉菜单AFAIK模拟绑定完整对象的自动方式。

在您的情况下,您的viewmodel将具有:

public class AddressViewModel
{
  public int SelectedCountryId { get; set; }
}

然后以这种方式使用DropDownFor()

<%: Html.DropDownListFor(m => m.SelectedCountryId, new SelectList(ServiceLocatorFactory.GetCodeServiceLocator<Country>().GetAll(), "CodeId", "Description"), "Please Select")%>

然后在你的Action(伪代码)中:

public ViewResult Save(AddressViewModel addressVM)
{
  var address = new Address() { Country = countriesStore.ById(addressVM.SelectedCountryId) };
  address.Save();

  ...
}

这种方式也意味着您需要为视图使用视图模型而不是域模型。您的示例似乎正在使用域模型,因此您可能希望了解更改此问题。开始使用视图模型后,您还需要Automapper之类的内容,以便将视图模型映射到域模型。

答案 1 :(得分:0)

好的伙计们,我似乎对我的问题有一个临时的解决方法,并且很想知道您的反馈,以及可能使这个解决方案更“强大”的任何其他替代方案。

在我的操作中,我检查Request.Form并查找由复杂类型的PropertyName指定的下拉列表。所以语法如下:

下拉列表返回的PropertyName.PropertyName.Value字段。使用它,我可以在需要时查找存储库以获取ComplexType的实例。

我的行动被粘贴在下面以供参考:

[AcceptVerbs(HttpVerbs.Post)]
[GridAction]
public ActionResult Update(int id)
{
    // obtain the instance of the importer we wish to update
    var importer = serviceLocator.Get(i => i.ImporterId == id);

    // the address object doesn't bind the State and Country dropdowns so we must manually read these
    var stateCodeId = Request.Form["ImporterAddress.State.CodeId"];
    var countryCodeId = Request.Form["ImporterAddress.Country.CodeId"];

    //Perform model binding (fill the properties and validate the model)
    if (TryUpdateModel(importer))
    {
        // parse the Id fields of the selected values of the dropdowns that we've just read from the request objects
        importer.ImporterAddress.StateId = int.Parse(stateCodeId);
        importer.ImporterAddress.CountryId = int.Parse(countryCodeId);

        // and convert them to their specific code objects
        importer.ImporterAddress.State =
            ServiceLocatorFactory.GetCodeServiceLocator<State>().Get(s => s.CodeId == int.Parse(stateCodeId));
        importer.ImporterAddress.Country =
            ServiceLocatorFactory.GetCodeServiceLocator<Country>().Get(s => s.CodeId == int.Parse(countryCodeId));

        // now save the updated model
        serviceLocator.Update(importer);
    }

    // rebind the grid
    return PartialView(new GridModel(serviceLocator.GetAll().ToList()));
}