【问题标题】:Mixing model binding with form posting in MVC 5?在 MVC 5 中将模型绑定与表单发布混合?
【发布时间】:2016-11-29 14:59:36
【问题描述】:

是否可以启用模型绑定以及表单中的已发布数据?

我有一个集合属性,我想在 foreach 循环中进行迭代以保存集合中的每个选定项:

    <div class="form-group">
        @Html.LabelFor(m => m.Users, new { @class = "control-label col-md-2" })
        <div class="col-md-10">
            @{
                List<ApplicationUser> allUsers = ViewBag.AllUsers;
                bool assigned;
            }
            <div>
                @foreach (var user in allUsers)
                {
//here I want to render all users, and only the users who are in the task, have a checked checkbox

                    assigned = Model.Users.Select(u => u.Id).Contains(user.Id);
                    <input type="checkbox" name="asndUsers" value="@user.Id" id="@user.Id" @Html.Raw(assigned ? "checked" : "") /> <label style="font-weight: normal;" for="@user.Id">@user.UserName</label><br />
                }
            </div>
        </div>
    </div>
//fields updated with model binding:
    <div class="form-group">
        @Html.LabelFor(m => m.Status, new { @class = "control-label col-md-2" })
        <div class="col-md-10">
            @Html.EditorFor(m => m.Status, new { htmlAttributes = new { @class = "form-control" } })
            @Html.ValidationMessageFor(m => m.Status)
        </div>
    </div>

这是 Edit action post 方法:

[HttpPost, ValidateAntiForgeryToken]
public ActionResult Edit([Bind(Include = "Id,Title,Description,DueDate,Status")] UserTask task, string[] asndUsers)
{
    if (ModelState.IsValid)
    {
        task.Users = new List<ApplicationUser>();
        foreach (var item in asndUsers)
        {
            var user = context.Users.Find(item);
            task.Users.Add(user);
        }
        context.Entry(task).State = EntityState.Modified;
        context.SaveChanges();
        return RedirectToAction("Index");
    }
    return View(task);
}

当我调试时它可以工作,我看到新发布的数据已与绑定数据合并。 但是当请求重定向到Index视图时,编辑一个项目后没有变化,这是Index操作方法:

public ActionResult Index(int? taskId)
{
    var viewModel = new TasksUsers();
    viewModel.Tasks = context.Tasks.Include(x => x.Users);
    if (taskId != null)
    {
        viewModel.Users = viewModel.Tasks.Where(t => t.Id == taskId).Single().Users;
        ViewBag.Row = taskId;
    }
    return View(viewModel);
}

【问题讨论】:

    标签: asp.net-mvc model-binding


    【解决方案1】:

    原答案

    更新相关实体的正确方法是先加载它们, 所以我使用急切加载将传入的task 参数重新定义为 如下:

    task = context.Tasks.Include(t => t.Users).Single(s => s.Id == task.Id); Note that `Find` can't be used with `Include` so I used
    

    Single.

    这解决了更新用户实体的问题

    这是错误的, 正确的方法是使用显式绑定而不是隐式绑定 (TryUpdateModel())

    【讨论】:

    • 感谢分享!
    【解决方案2】:

    回发的任务不再由 DbContext 跟踪。尝试在 Edit 操作中将任务 Attach 发送到 DbSet:

        context.Tasks.Attach(task);
        if (task.Users == null) {
            task.Users = new List<ApplicationUser>();
        }
        foreach (var item in asndUsers) {
            var user = context.Users.Find(item);
            task.Users.Add(user);
        }
        // may be important because Attach() sets the State to 'Unchanged'?
        context.Entry(task).State = EntityState.Modified; 
        context.SaveChanges();
    

    附带说明,您可以在调用RedirectToAction 时传递参数。 (仅当您想将已编辑任务的 id 传递给 Index 操作时才这样做):

    return RedirectToAction("Index", new { taskId = existingTask.Id });
    //                                     ^^^^^^
    //                                     must match parameter name of Index action
    

    【讨论】:

    • 将其放入 POST Edit 操作中。我编辑了代码以使用法更清晰。
    • 再次编辑,使用Attach() 否则您将丢失通过模型绑定发送的其他值(即Status)!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-04-24
    • 1970-01-01
    • 2016-09-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多