具有多个实体的模型绑定

本文关键字:模型 绑定 实体 | 更新日期: 2023-09-27 18:32:51

我正在开发一个管理面板,该面板将允许用户添加一个或多个其他用户。有一个文本区域,管理员可以在其中输入一个或多个要添加到应用程序的用户 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作为参数。如何调整此代码以处理一次添加的多个用户?这可能吗?

具有多个实体的模型绑定

你走在正确的轨道上,杰夫。 你只需要稍微扩展一下你所拥有的东西。

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

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

请注意,name item.CanWrite 。 这里的问题是,由于您循环访问了模型中的所有用户,因此您实际上为所有输入字段指定了相同的名称。 这会阻止 MVC 对绑定到 POST 数据进行建模,因为它无法区分每个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. 将重复的逻辑放在视图中。

您的模板现在应如下所示(不包括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" />
}

如果您想了解有关绑定到集合的更多信息,请参阅我为另一个问题提供的答案。

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

尝试这样的事情:

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
}