部分空异常中的部分

时间:2015-09-01 15:38:49

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

我有一个MVC表单,它比我的所有其他表单更复杂,使用了三个模型。

{p> Company -> Base_IP -> RequestedIP ViewModel -> Partial1 -> Partial2

我正在使用BeginCollectionItem,因为每个模型都有一个模型的属性列表。 IE - 公司有一个名为baseIps的属性,BaseIp类有一个名为requestedIps的属性,它是requestedIps,它返回null,计数在页面渲染上,但是没有提交。

post Create()中提交到数据库时,我会在' requestedIps'上获得空值。财产,这是为什么?

我已经在下面添加了违规控制器和部分代码示例,而不是整个事情,因为它是大量/多余的 - 如有问题,请告诉我。

控制器 - [HttpGet]创建()

public ActionResult Create()
        {
            var cmp = new Company
            {
               contacts = new List<Contact>
                {
                    new Contact { email = "", name = "", telephone = "" }
                }, pa_ipv4s = new List<Pa_Ipv4>
                {
                    new Pa_Ipv4 
                    { 
                        ipType = "Pa_IPv4", registedAddress = false, existingNotes = "", numberOfAddresses = 0, returnedAddressSpace = false, additionalInformation = "",
                        requestedIps = new List<IpAllocation>
                        {
                            new IpAllocation { allocationType = "Requested", cidr = "", mask = "", subnet  = "" }
                        }
                    }
                }
            };
            return View(cmp);
        }

控制器 - [HttpPost]创建()

        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Create(Company cmp) // does not contain properties assigned/added to in view render
        {
            if (ModelState.IsValid)
            {
                db.companys.Add(cmp);
                db.SaveChanges();
                return RedirectToAction("Index");
            }
            return View(cmp);
        }

创建视图

@model Company
@using (Html.BeginForm())
{
            <div id="editorRowsAsn">
                @foreach (var ip in Model.pa_ipv4s)
                {
                    @Html.Partial("Pa_IPv4View", ip)
                }
            </div>
            <br />
            <div data-role="main" class="ui-content">
                <div data-role="controlgroup" data-type="horizontal">
                    <input type="submit" class="ui-btn" value="Create" />
                </div>
            </div>
}

Pa_Ipv4查看

@model Pa_Ipv4
@using (Html.BeginCollectionItem("pa_ipv4s"))
{
    @Html.AntiForgeryToken()

    <div id="editorRowsRIpM">
        @foreach (var item in Model.requestedIps)
        {
            @Html.Partial("RequestedIpView", item)
        }
    </div>
    @Html.ActionLink("Add", "RequestedManager", null, new { id = "addItemRIpM", @class = "button" }

}

RequestedIpView

@model IpAllocation
<div class="editorRow">
    @using (Html.BeginCollectionItem("requestedIps"))
    {
        <div class="ui-grid-c ui-responsive">
            <div class="ui-block-a">
                <span>
                    @Html.TextBoxFor(m => m.subnet, new { @class = "checkFiller" })
                </span>
            </div>
            <div class="ui-block-b">
                <span>
                    @Html.TextBoxFor(m => m.cidr, new { @class = "checkFiller" })
                </span>
            </div>
            <div class="ui-block-c">
                <span>
                    @Html.TextBoxFor(m => m.mask, new { @class = "checkFiller" })
                    <span class="dltBtn">
                        <a href="#" class="deleteRow"><img src="~/Images/DeleteRed.png" style="width: 15px; height: 15px;" /></a>
                    </span>
                </span>
            </div>
        </div>
    }
</div>

3 个答案:

答案 0 :(得分:2)

您首先(外部)部分将生成与您的模型相关的正确名称属性(您的代码未显示Pa_Ipv4.cshtml视图中的任何控件,但我假设您确实有一些),例如

<input name="pa_ipv4s[xxx-xxx].someProperty ...>

然而内部部分不会因为@using (Html.BeginCollectionItem("requestedIps"))将生成

<input name="requestedIps[xxx-xxx].subnet ...>
<input name="requestedIps[xxx-xxx].cidr ...>

他们应该在哪里

<input name="pa_ipv4s[xxx-xxx].requestedIps[yyy-yyy].subnet ...>
<input name="pa_ipv4s[xxx-xxx].requestedIps[yyy-yyy].cidr ...>

通常,您可以使用其他视图数据将前缀传递给partial(请参阅this answer以获取示例),但遗憾的是,您无法访问Guid生成的BeginCollectionItem } helper因此无法正确添加name属性的前缀。

文章herehere讨论创建自己的帮助程序来处理嵌套集合。

其他选项包括使用嵌套的for循环并包含集合索引器的隐藏输入,这将允许您从集合中删除项目,并且在提交表单时仍能够绑定到模型。

for (int i = 0; i < Model.pa_ipv4s.Count; i++)
{
  for(int j = 0; j < Model.pa_ipv4s[i].requestedIps.Count; j++)
  {
    var name = String.Format("pa_ipv4s[{0}].requestedIps.Index", i);
    @Html.TextBoxFor(m => m.pa_ipv4s[i].requestedIps[j].subnet)
    @Html.TextBoxFor(m => m.pa_ipv4s[i].requestedIps[j].cidr)
    ...
    <input type="hidden" name="@name" value="@j" />
  }
}

但是,如果您还需要动态添加新项目,则需要使用javascript生成html(请参阅示例herehere

答案 1 :(得分:1)

如果查看最终标记,您可能会输入名称为

的输入
input name="subnet" 
input name="cidr" 
input name="mask" 

这是表单发布时表单集合的显示方式。不幸的是,这不会绑定到您的公司模型。

您的字段需要看起来像这样

input name="Company.pa_ipv4s[0].subnet"
input name="Company.pa_ipv4s[0].cidr"
input name="Company.pa_ipv4s[0].mask"

input name="Company.pa_ipv4s[1].subnet"
input name="Company.pa_ipv4s[1].cidr"
input name="Company.pa_ipv4s[1].mask"

答案 2 :(得分:0)

有多种方法可以“修复”这个问题,每个方法都有自己的注意事项。

一种方法是设置“编辑器”视图(通常在〜/ Views / Shared / EditorTemplates / ClassName .cshtml中),然后使用@ Html.EditorFor(x =&gt; x.SomeEnumerable) )。 在您需要能够从集合中间删除任意项目的情况下,这将无法正常工作;虽然你仍然可以通过你设置的ItemIsDeleted等额外属性来处理这些情况(例如通过javascript)。

在这里设置一个完整的示例会很冗长,但您也可以参考本教程:http://coding-in.net/asp-net-mvc-3-how-to-use-editortemplates/

首先,您将创建一个简单的模板,如

~/Views/Share/EditorTemplates/Contact.cshtml

 @model yournamespace.Contact
 <div>
     @Html.LabelFor(c => c.Name)
     @Html.TextBoxFor(c => c.Name)
     @Html.ValidationMessageFor(c => c.Name)
</div>
<div>
     @Html.LabelFor(c => c.Email)
     @Html.TextBoxFor(c => c.Email)
     @Html.ValidationMessageFor(c => c.Email)
</div>
... other simple non-enumerable properties of `Contact` ...
@Html.EditorFor(c => c.pa_ipv4s) @* uses ~/Views/Shared/EditorTemplates/pa_ipv4s.cshtml *@

在您编辑/创建Company的视图中,您可以将其调用为

@Html.EditorFor(company => company.Contacts)

(就像EditorTemplate for Company调用EditorFor pa_ipv4s一样。)

当您以这种方式使用EditorFor时,MVC将自动为您处理索引。 (你如何处理添加一个新的联系人/ IPv4 /等。这是一个更高级,但这应该让你开始。)

MVCContrib也有一些你可以使用的辅助方法,但是我记得并不是特别简单,可能会让你陷入特定的MVC版本。