在PostBack期间模型值丢失

时间:2014-09-10 16:48:18

标签: c# asp.net forms asp.net-mvc-4

我试图将我的模型传回控制器。我已经将模型的所有字段都包含在表单中,但是一旦它返回到控制器,Employees和EmployeesInMultipleCompanies,它们都是IList类型,它就会丢失模型的两个部分。我已经验证了字段在传递到视图时存在,它们只是没有将其返回到控制器。

.cshtml

@using (Html.BeginForm("PostEmail","Import",FormMethod.Post))
{
    @Html.AntiForgeryToken()
    @Html.ValidationSummary(true)

    <fieldset>
        <legend>EmailViewModel</legend>
        <p>
            <input type="email" name="EmailAddresses" value=" " required="required" />
            <span class="btn_orange"><a href="#" class="remove_field" >x</a></span>
        </p>
        <p>
            <span class="btn_orange"><a class="add_email_button" href="#">Add Another Email</a></span>
        </p>
        @Html.HiddenFor(m=> Model.Employees)
        @Html.HiddenFor(m=> Model.CompanyId)
        @Html.HiddenFor(m=> Model.CompanyName)
        @Html.HiddenFor(m=> Model.PayFrequency)
        @Html.HiddenFor(m=> Model.FirstPayPeriodBeginDate)
        @Html.HiddenFor(m=> Model.LastPayPeriodEndDate)
        @Html.HiddenFor(m=> Model.NumberPayPeriods)
        @Html.HiddenFor(m=> Model.ValidEmployees)
        @Html.HiddenFor(m=> Model.InvalidEmployees)
        @Html.HiddenFor(m=> Model.EmployeesInMultipleCompanies)
        @Html.HiddenFor(m=> Model.TotalEmployeeCount)
        <p>
            <input type="submit" value="Send Email" />
        </p>
    </fieldset>
}

<div>
    @Html.ActionLink("Cancel", "Continue", "Import")
</div>

<script type="text/javascript">
    $(document).ready(function () {
        $('body').on('click', '.remove_field', function () {
            $(this).closest('p').remove();
        });
        $('.add_email_button').closest('p').click(function () {
            var html = '<p><input type="email" required="required" name="EmailAddresses" /><span class="btn_orange"><a href="#" class="remove_field">x</a></span></p>';
            $(html).insertBefore($(this));
        });

        $(body).on('click', 'submit', function() {
            $('email').attr('required', true);
        });
    });
</script>

controller .cs

public ActionResult SendEmail(ImportViewModel model)
        {
            var editedEmployees = model.EmployeesInMultipleCompanies;
            var importModel = TempData["ImportModel"] as ImportViewModel;

            //for each employee who is in multiple companies, set user-chosen company id and the COHD related to that company
            var importService = new ImportService();
            importService.UpdateEmployeesInMultipleCompanies(editedEmployees, importModel.Employees);
            TempData["ImportModel"] = importModel;

            return this.RazorView("SendEmail", importModel);
        }


        [HttpPost]
        public ActionResult PostEmail(ImportViewModel model)
        {
            IEmailer emailer = new Emailer();
            emailer.SendEmail(model.EmailAddresses, model.Employees);
            var employees =
                model.Employees.Where(e => !string.IsNullOrWhiteSpace(e.ValidationErrorOrException)).ToList();
            var emailViewModel = new EmailViewModel(employees);
            return this.RazorView("Continue", emailViewModel);
        }

3 个答案:

答案 0 :(得分:2)

将其替换为以下内容:

@foreach(var i = 0; i < Model.Employees.Count; i++)
{
  Html.Hiddenfor(m => m.Employees[i])
}

应该对EmployeesInMultipleCompanies

进行同样的操作

答案 1 :(得分:1)

插入隐藏字段的值将序列化为字符串以执行此操作。

IList<T>不可序列化,因为它是一个接口,因此无法存储在隐藏字段中。

有关详细信息,请参阅this问题

如何将IList<T>转换为常规列表?

答案 2 :(得分:1)

问题不在于控制器,而在于客户端上存储值的方式。 DefaultModelBinder非常聪明,但有时也不那么聪明。只要属性匹配,它就可以处理某些情况下的接口,复杂类型的集合以及许多其他事物。有关该行为的详细信息,请参阅此文章:http://msdn.microsoft.com/en-us/magazine/hh781022.aspx

如果您调试控制器并检查Request.Form中的值,您将看到您的数据存储为单个值。如果模型粘合剂可以说话,他们会说:

Request.Form [&#34; EmployeesInMultipleCompanies&#34;]与我模型的EmployeesInMultipleCompanies属性无关,因为我的模型元数据(ModelMetaDataProvider)告诉我我应该寻找一个集合类型(IList未知的泛型类型),这显然是一个字符串值,所以我不会绑定这个值。

要解决此问题,您必须为DefaultModelBinder提供足够的提示,以便它完成工作。这样做的一种方法是以这样的方式呈现隐藏字段,使得它们具有与它们相关联的索引器。这是模型绑定器需要以您期望的方式运行的线索。

如果您同时渲染:

@for(var i = 0; i < Model.Employees.Count; i++)
{
  Html.Hiddenfor(m => m.Employees[i])
}
@for(var i = 0; i < Model.EmployeesInMultipleCompanies.Count; i++)
{
  Html.Hiddenfor(m => m.EmployeesInMultipleCompanies[i])
}

然后再次调试您的调试器,您现在会在Request.Form

中看到这一点
Request.Form["EmployeesInMultipleCompanies[0]"]
Request.Form["EmployeesInMultipleCompanies[1]"]

等...

现在模型绑定器可以正常工作。

或者,您可以创建一个自定义模型绑定器,它知道如何从单个输入中读取字段。


我在这里使用通用IList进行此类ModelBinding时创建了一个Gist来演示不同类型的可能结果:https://gist.github.com/xDaevax/bd35b5d88fed03709d30