模型与多个实体绑定

时间:2013-09-25 17:16:20

标签: c# asp.net-mvc

我正在开发一个允许用户添加一个或多个其他用户的管理面板。管理员可以输入一个或多个要添加到应用程序的用户ID的文本区域,这对应于在雇佣过程中分配的用户ID。提交时,应用程序从包含所有员工的数据库中提取姓名,电子邮件等,并将其显示在屏幕上以进行验证。屏幕上还包含一些用于指定某些权限的复选框,例如CanWriteIsAdmin

查看

using (Html.BeginForm())
        {
            <table>
                <tr>
                    <th>
                    </th>
                    <th>
                        @Html.DisplayNameFor(model => model.User.First().ID)
                    </th>
                    <th>
                        @Html.DisplayNameFor(model => model.User.First().Name)
                    </th>
                    <th>
                        @Html.DisplayNameFor(model => model.User.First().Email)
                    </th>
                    <th>
                        @Html.DisplayNameFor(model => model.User.First().CanWrite)
                    </th>
                    <th>
                        @Html.DisplayNameFor(model => model.User.First().IsAdmin)
                    </th>
                </tr>

                @foreach (var item in Model.User)
                {
                    <tr>
                        <td>
                            <input type="checkbox" name="id" value="@item.ID" checked=checked/>
                        </td>
                        <td>
                            @Html.DisplayFor(modelItem => item.ID)
                        </td>
                        <td>
                            @Html.DisplayFor(modelItem => item.Name)
                        </td>
                        <td>
                            @Html.DisplayFor(modelItem => item.Email)
                        </td>
                        <td>
                            @Html.CheckBoxFor(modelItem => item.CanWrite)
                        </td>
                        <td>
                            @Html.CheckBoxFor(modelItem => item.IsAdmin)
                        </td>
                    </tr>
                }

            </table>
            <input type="submit" />
        }

注意:名称为ID的复选框的原因是,为了能够在获取名称和其他信息后不添加用户,例如,您不想添加的用户在意外的ID列表中。

模型

public class User
{
    public int ID { set; get; }
    public string Name { set; get; }
    public bool IsAdmin { set; get; }
    public bool CanWrite { set; get; }
    public string Email{ set; get; }

}

控制器

[HttpPost]
public ActionResult Create(IEnumerable<User> model)
{
    //With this code, model shows up as null
}

对于单个用户,我知道我可以在控制器操作中使用User model作为参数。如何调整此代码以便同时添加多个用户?这甚至可能吗?

3 个答案:

答案 0 :(得分:1)

我只是开始使用ASP.NET MVC,但我想你只需要创建一个视图模型类,它具有类型为IEnumerable<User>的属性,用于存储用户集合。然后将其传递给您的视图并返回到您的POST处理程序。

答案 1 :(得分:1)

尝试这样的事情:

<强>模型

public class Employee
{
    public IEnumerable<User> Employees { set; get; }
}

public class User
{
    public int ID { set; get; }
    public string Name { set; get; }
    public bool IsAdmin { set; get; }
    public bool CanWrite { set; get; }
    public string Email{ set; get; }
}

查看

@model Employee

@using( Html.BeginForm()
{
    // build HTML table, not much different from yours
}

<强>控制器

[HttpPost]
public ActionResult Create(Employee model)
{
    // model will have an IEnumerable property with the User info
}

答案 2 :(得分:1)

杰克,你走在正确的轨道上。你只需要扩展你所拥有的东西。

首先,您的模型在您的操作中null的原因是因为输入字段在表单上的命名方式。您的字段将按照以下方式呈现:

<input id="item_CanWrite" name="item.CanWrite" type="checkbox" value="true" />
<input name="item.CanWrite" type="hidden" value="false" />

请注意nameitem.CanWrite。这里的问题是因为你循环遍历模型中的所有用户,所以你实际上给所有输入字段赋予了相同的名称。这可以防止MVC模拟绑定到POSTed数据,因为它无法区分每个User

为了解决此问题,您需要将调用更改为Html.DisplayFor,以便允许MVC将每个User作为单独的实体进行处理,从而允许每个User设置要按顺序命名的字段。这允许模型绑定器正确绑定到这些字段。像这样:

@for (int i = 0; i < Model.Count(); i++)
{
    <tr>
        <td>
            <input type="checkbox" name="id" value="@Model[i].ID" checked=checked/>
        </td>
        <td>
            @Html.DisplayFor(m => m[i].ID)
        </td>
        <td>
            @Html.DisplayFor(m => m[i].Name)
        </td>
        <td>
            @Html.DisplayFor(m => m[i].Email)
        </td>
        <td>
            @Html.CheckBoxFor(m => m[i].CanWrite)
        </td>
        <td>
            @Html.CheckBoxFor(m => m[i].IsAdmin)
        </td>
    </tr>
}

如果你现在检查这些字段是如何呈现为HTML的,你会发现它们看起来像这样:

<input checked="checked" data-val="true" data-val-required="The CanWrite field is required." name="[0].CanWrite" type="checkbox" value="true" />
<input name="[0].CanWrite" type="hidden" value="false" />
<input checked="checked" data-val="true" data-val-required="The CanWrite field is required." name="[1].CanWrite" type="checkbox" value="true" />
<input name="[1].CanWrite" type="hidden" value="false" />

如您所见,每个User都有自己独立的索引。我使用了一个简单的测试设置和以下控制器方法,它在我的测试模型中选取了正确数量的记录:

[HttpPost]
public ActionResult Index(IEnumerable<User> model)
{
    // Process your data.

    return View(model);
}

但是,我想在此提出一个进一步的改进,以便删除您视图中的逻辑。为此,我们可以使用DisplayTemplates代替循环:

  1. 在视图的当前文件夹中创建一个DisplayTemplates文件夹(例如,如果您的视图为Home\Index.cshtml,请创建文件夹Home\DisplayTemplates)。
  2. 在该目录中创建一个强类型视图,其名称与您的模型匹配(即在这种情况下视图将被称为User.cshtml)。
  3. 将重复的逻辑放在视图中。
  4. 您的模板现在看起来应该是这样的(除了User模型所在的位置之外的名称空间):

    @model User
    
    <tr>
        <td>
            <input type="checkbox" name="id" value="@Model.ID" checked=checked/>
        </td>
        <td>
            @Html.DisplayFor(m => m.ID)
        </td>
        <td>
            @Html.DisplayFor(m => m.Name)
        </td>
        <td>
            @Html.DisplayFor(m => m.Email)
        </td>
        <td>
            @Html.CheckBoxFor(m => m.CanWrite)
        </td>
        <td>
            @Html.CheckBoxFor(m => m.IsAdmin)
        </td>
    </tr>
    

    现在,在原始视图中,您可以用以下内容替换所有循环:

    @using (Html.BeginForm())
    {
        <table>
            @Html.DisplayForModel()
        </table>
        <input type="submit" value="Submit" />
    }
    

    如果您想了解有关绑定收藏品的更多信息,请参阅我提供给其他问题的an answer