MVC模型没有绑定到POST中的集合

时间:2014-07-02 20:12:39

标签: asp.net-mvc model-binding

  • Visual Studio 2012
  • MVC 5.2.0
  • jQuery 2.1.1
  • jQuery UI Combined 1.10.4
  • Json.Net 6.0.3
  • Kendo UI MVC 2014.1.528

我已经阅读了很多类似的帖子。我试图运用我所见过的无济于事的方式。为了你的怜悯和帮助,我把自己扔在键盘的脚下。

我有一个类似调查的网站,所以为了简单起见,我编写了一个示例项目来复制问题。我也可以上传。

我有一个带有地址的子对象的模型 - 没有问题 - 它会绑定。

该模型还有一个问题集。它永远不会在帖子上绑定,这就是问题所在。

让我们从模特开始:

调查本身:

public class Survey
{
    [ScaffoldColumn(false)]
    public int Id { get; set; }
    public string Name { get; set; }
    [DisplayName("Phone #")]
    [StringLength(15)]
    [DataType(DataType.PhoneNumber)]
    public string ContactMethodPhone { get; set; }
    [DisplayName("Email")]
    [StringLength(120)]
    [EmailAddress]
    public string ContactMethodEmail { get; set; }
    public virtual Address Address { get; set; }

    public virtual List<Question> Questions { get; set; }
}

和地址:

public class Address
{
    [ScaffoldColumn(false)]
    public int AddressId { get; set; }

    [DisplayName("Address line 1")]
    [Required]
    [StringLength(200)]
    public string Street { get; set; }
    [DisplayName("Address line 2")]
    [StringLength(200)]
    public string Street2 { get; set; }
    [DisplayName(" ")]
    [StringLength(50)]
    public string ApartmentNum { get; set; }
    [DisplayName("Type")]
    [StringLength(50)]
    public string Tenement { get; set; }
    [DisplayName("City")]
    [Required]
    [StringLength(200)]
    public string City { get; set; }
    [DisplayName("Province/State")]
    [StringLength(20)]
    public string State { get; set; }
    [Required]
    [DisplayName("Postal/Zip Code")]
    [StringLength(10)]
    public string MailCode { get; set; }
    [DisplayName("Country")]
    [StringLength(30)]
    public string Country { get; set; }


    [NotMapped]
    public List<SelectListItem> Cities { get; set; }
    [NotMapped]
    public List<SelectListItem> Provinces { get; set; }
    [NotMapped]
    public List<SelectListItem> Countries { get; set; }
    [NotMapped]
    public List<SelectListItem> MailCodes { get; set; }
    [NotMapped]
    public List<SelectListItem> Tenements { get; set; }
}

和问题:

 public class Question
{
    [ScaffoldColumn(false)]
    public int Id { get; set; }
    [ScaffoldColumn(false)]
    public int ImageID { get; set; }                        // Question Image
    [DisplayName("Question: ")]
    public string InformationIntakeGroupValue { get; set; } // Question: ie Did you graguate high school 
    public int ImageID_Topic { get; set; } // Topic Image
    [DisplayName("Topic: ")]
    public string InformationIntakeTopicValue { get; set; } // Topic: ie Education
    [ScaffoldColumn(false)]
    public string InformationIntakeTypeCode { get; set; }   // Type of question (date, bool, text)
    // below not filled by select;
    // the key from the EntityAffilliateIntake record insert
    public int PersonId { get; set; }                       // Person anwering question
    // this is the user response area
    [DisplayName("Answer: ")]
    public string InformationIntakeValue { get; set; }      
    [DisplayName("Choice: ")]
    public string InformationIntakeValueBool { get; set; }
    [DisplayName("Date: ")]
    public DateTime InformationIntakeValueDate { get; set; }

    [ForeignKey("Survey")]
    public int SurveyId { get; set; }
    public virtual Survey Survey { get; set; }

}

(注意:我还试过没有外键的模型 - 但可能没有正确定义)

控制器:

   // GET: /Inquiry/Create
    public ActionResult Create()
    {
        var geoIpData = Strings._download_serialized_json_data<GeoData>(StringConstants.UrlForGeoService);

        SurveyModel = new Survey
        {
            Id=1,
            Address = new AddressController().GetAddressModel(geoIpData.country, geoIpData.regionName, geoIpData.city, ""),
            Questions = new QuestionController().GetQuestions(1).ToList()
        };

        return View(SurveyModel);
    }

    // POST: /Inquiry/Create
    // To protect from overposting attacks, please enable the specific properties you want to bind to, for 
    // more details see http://go.microsoft.com/fwlink/?LinkId=317598.
    [HttpPost]
    public ActionResult Create([Bind(Include = "Id, Name, ContactMethodPhone, ContactMethodEmail, Address, Questions")] Survey survey)
    {
        if (ModelState.IsValid)
        {
            int i = 0;
        }
        if (survey.Address.Cities == null)
        {
            survey.Address.Cities = SurveyModel.Address.Cities;
            survey.Address.Countries = SurveyModel.Address.Countries;
            survey.Address.MailCodes = SurveyModel.Address.MailCodes;
            survey.Address.Provinces = SurveyModel.Address.Provinces;
            survey.Address.Tenements = SurveyModel.Address.Tenements;
        }
        if (survey.Questions == null)
        {
            survey.Questions = SurveyModel.Questions;
        }
        return View(survey);
    }

观点:

@model BindCollection.Models.Survey

@{
    ViewBag.Title = "Create";
}

<h2>Create</h2>

@using (Html.BeginForm())
{
    @Html.AntiForgeryToken()

// I use <div class="container">, <fieldset> and <div class="row">
// instead of <div class="form-horizontal"> and <div class="form-group">

<div class="container">
    <fieldset>
        <legend></legend>
        <h4>Survey</h4>
        <hr />
        @Html.ValidationSummary(true)
        @Html.HiddenFor(model => model.Id)

        <div class="row">
            @Html.LabelFor(model => model.Name, new { @class = "control-label col-md-2 col-md-2 col-lg-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.Name)
                @Html.ValidationMessageFor(model => model.Name)
            </div>
        </div>

        <div class="row">
            <div class="col-sm-12 col-md-12 col-lg-12">
                Your address information:<br />
                <br />
            </div>
        </div>
        @* no problem here with address *@
        @{ var vddAddress = new ViewDataDictionary(ViewData) { TemplateInfo = new TemplateInfo() { HtmlFieldPrefix = "Address" } };}
        @Html.Partial("_AddressPartial", Model.Address, @vddAddress)

        <hr />
        <div class="row">
            <div class="col-sm-12 col-md-12 col-lg-12">
                How we can contact you? :<br />
                <br />
            </div>
        </div>
        <div class="row">
            @Html.LabelFor(model => model.ContactMethodPhone, new { @class = "control-label col-sm-2 col-md-2 col-lg-2" })
            <div class="col-sm-10 col-md-10 col-lg-10">
                @Html.EditorFor(model => model.ContactMethodPhone)
                @Html.ValidationMessageFor(model => model.ContactMethodPhone)
            </div>
        </div>

        <div class="row">
            @Html.LabelFor(model => model.ContactMethodEmail, new { @class = "control-label col-sm-2 col-md-2 col-lg-2" })
            <div class="col-sm-10 col-md-10 col-lg-10">
                @Html.EditorFor(model => model.ContactMethodEmail)
                @Html.ValidationMessageFor(model => model.ContactMethodEmail)
            </div>
        </div>
        <hr />
        <div class="row">
            <div class="col-sm-12 col-md-12 col-lg-12">
                Some Questions<br />
                <br />
            </div>
        </div>
        @*Here is the evil one! Beware!*@
        @{ var vddQuestions = new ViewDataDictionary(ViewData) { TemplateInfo = new TemplateInfo() { HtmlFieldPrefix = "Questions" } };}
        @Html.Partial("_QuestionsPartial", Model.Questions, @vddQuestions)

        <hr />
        <div class="form-group">
            <div class="col-md-offset-2 col-md-10">
                <input type="submit" value="Save" class="btn btn-default" />
            </div>
        </div>
    </fieldset>
</div>

}

地址部分视图无关紧要,因为那里没有问题。

以下是问题的部分视图:

@model IEnumerable<BindCollection.Models.Question>
@using BindCollection
@using Kendo.Mvc.UI

@{
    string CurrentTopic = string.Empty;
    bool FirstTime = true;
}

@foreach (var item in Model)
{
    if (CurrentTopic != item.InformationIntakeTopicValue)
    {
        CurrentTopic = item.InformationIntakeTopicValue;
        if (!FirstTime)
        {
            FirstTime = false;
            item.InformationIntakeTopicValue = string.Empty;
        }
    }
    else
    {
        item.InformationIntakeTopicValue = string.Empty;
    }
    @Html.EditorFor(m=>item, "Question")
 <br />
 <br />

}

当然,我为一个问题做了一个EditorTemplate,你可以在上面看到几行......

@model BindCollection.Models.Question

@Html.HiddenFor(m=>m.Id)       

   @{if (!string.IsNullOrWhiteSpace(Model.InformationIntakeTopicValue))
    {
        <hr />
        <div class="row">
            <div class="col-sm-12 col-md-12 col-lg-12">
                <br />
                <br />
                <h4>
                    @Html.DisplayFor(m => m.InformationIntakeTopicValue, new { @class = "control-label col-sm-10 col-md-10 col-lg-10" })
                </h4>
                <br />
            </div>
        </div>
    }}

    @*type of value to ask for is denoted by item.InformationIntakeTypeCode*@

    <div class="row">
        <div class="control-label col-sm-9 col-md-9 col-lg-9">
            @Html.DisplayFor(m => m.InformationIntakeGroupValue, null)
        </div>

    </div>
    <div class="row">
            <div class="col-sm-12 col-md-12 col-lg-12">
                @{

                    var outputBool = false;
                    var outputDate = false;
                    var outputText = false;

                   if(Model.InformationIntakeTypeCode.ToLower().Contains("date") )
                   {
                       outputDate = true;
                   }
                   else if (Model.InformationIntakeTypeCode.ToLower().Contains("bool"))
                   {
                       outputBool = true;
                   }
                   else if (Model.InformationIntakeTypeCode.ToLower().Contains("string"))
                   {
                       outputText = true;
                   }

                }
                @if(outputBool)
                {
                    @(Html.Kendo().DropDownListFor(m => m.InformationIntakeValueBool)
                    .HtmlAttributes(new { style ="width: 100px;", id="InformationIntakeValueBool"+Model.Id.ToString() })
                    .DataTextField("Text")
                    .DataValueField("Value")
                    .BindTo(new List<SelectListItem>() {
                        new SelectListItem() {
                            Text = "Please Select...",
                            Value = "0"
                        },
                        new SelectListItem() {
                            Text = "Yes",
                            Value = "true"
                        },
                        new SelectListItem() {
                            Text = "No",
                            Value = "false"
                        }
                    }).Value("0")
                    )
                }
                @if(outputDate)
                {
                    @(Html.Kendo().DatePickerFor(m => m.InformationIntakeValueDate)
                            .HtmlAttributes(new { style = "width: 100px;", id="InformationIntakeValueDate"+Model.Id.ToString() })
                    )
                }
                @if (outputText)
                {
                    @Html.Kendo().AutoCompleteFor(m => m.InformationIntakeValue).HtmlAttributes(new { style = "width: 80%;", id="InformationIntakeValue"+Model.Id.ToString()})
                }
            </div>
    </div>

所以......当我发布POST时,会发生奇怪的事情。传递所有表单值,但问题看起来很奇怪:

Id  1
Name    sally
Address.Street  123 Avenue Steet
Address.Street2 Building C
Address.Tenement    Suite
Address.ApartmentNum    111
Address.City_input  Sarnia
Address.City    Sarnia
Address.State_input Ontario
Address.State   Ontario
Address.Country_input   Canada
Address.Country Canada
Address.MailCode_input  N6B 2K0
Address.MailCode    N6B 2K0
ContactMethodPhone  555-555-5555
ContactMethodEmail  r@r.com
Questions.item.Id   1
Questions.item.InformationIntakeValueBool   true
Questions.item.Id   2
Questions.item.InformationIntakeValueDate   2/4/2014
Questions.item.Id   3
Questions.item.InformationIntakeValue   Speckled
Questions.item.Id   4
Questions.item.InformationIntakeValueBool   true
Questions.item.Id   5
Questions.item.InformationIntakeValue   Lightly Toasted
Questions.item.Id   7
Questions.item.InformationIntakeValueBool   true
Questions.item.Id   8
Questions.item.InformationIntakeValue   Nothing!
Questions.item.Id   6
Questions.item.InformationIntakeValueBool   true
Questions.item.Id   9
Questions.item.InformationIntakeValueDate   6/29/2014

我认为,正如我在其他帖子中看到的那样,问题项应如下所示:

Questions[0].Id 1
Questions[0].InformationIntakeValueBool true
Questions[1].Id 2
Questions[1].InformationIntakeValueDate 2/4/2014
Questions[2].Id 3
Questions[2].InformationIntakeValue Speckled

所以我不确定为什么我看起来像这样。

在服务器端,请求仅显示每个变量:

Request.Form.Results View   Expanding the Results View will enumerate the IEnumerable   
    [0] "__RequestVerificationToken"    object {string}
    [1] "Id"    object {string}
    [2] "Name"  object {string}
    [3] "Address.Street"    object {string}
    [4] "Address.Street2"   object {string}
    [5] "Address.Tenement"  object {string}
    [6] "Address.ApartmentNum"  object {string}
    [7] "Address.City_input"    object {string}
    [8] "Address.City"  object {string}
    [9] "Address.State_input"   object {string}
    [10]    "Address.State" object {string}
    [11]    "Address.Country_input" object {string}
    [12]    "Address.Country"   object {string}
    [13]    "Address.MailCode_input"    object {string}
    [14]    "Address.MailCode"  object {string}
    [15]    "ContactMethodPhone"    object {string}
    [16]    "ContactMethodEmail"    object {string}
    [17]    "Questions.item.Id" object {string}
    [18]    "Questions.item.InformationIntakeValueBool" object {string}
    [19]    "Questions.item.InformationIntakeValueDate" object {string}
    [20]    "Questions.item.InformationIntakeValue" object {string}

查看最近4项?其他记录怎么样?

我猜测我发送到问题部分视图的ViewDataDictionary有些奇怪。

任何帮助将不胜感激。这应该简单......

1 个答案:

答案 0 :(得分:1)

部分视图或foreach语句都不包含正确绑定集合所需的信息。我总是使用EditorTemplates来做所有事情。特别是因为EditorTemplates会自动遍历集合。

但是,如果您已经绑定并决定使用循环,则使用for循环,然后索引模型。在你的情况下:

@for(int i = 0; i < Model.Count; i++)
{
    if (CurrentTopic != Model[i].InformationIntakeTopicValue)
    ...
    @Html.EditorFor(m => Model[i]) // don't have to specify the template name
                                   // since it's the same name as the type
}

但是,我只是这样做:

在您看来,请执行以下操作:

@*Here is the evil one! Beware!*@

@Html.EditorFor(m => m.Questions)

然后像往常一样使用Question.cshtml,它将自动由EditorFor

迭代

但是,在Question.cshtml的顶部(在模型声明之后)添加以下代码,这就是实现您正在尝试的所有必要条件。您根本不需要局部视图。

@{
  if (ViewBag.CurrentTopic != Model.InformationIntakeTopicValue)
  {
      ViewBag.CurrentTopic = Model.InformationIntakeTopicValue;
  }
  else
  {
      Model.InformationIntakeTopicValue = string.Empty;
  }
}