C# 如何从ViewModels列表发布ViewModel

C# 如何从ViewModels列表发布ViewModel,c#,asp.net-mvc,razor,viewmodel,model-binding,C#,Asp.net Mvc,Razor,Viewmodel,Model Binding,我当前有一个视图,我将ViewModels列表传递给该视图。然后,我想选择其中一个ViewModels并将其发布回控制器。我目前有一个foreach循环遍历每个ViewModel并显示它们的数据。对于每个ViewModel,都有一个submit按钮,但它似乎不像我希望的那样工作 代码如图所示: @model IEnumerable<RoboticsScheduler.Models.UserVM> ... @using (Html.BeginForm("UserApproved", "

我当前有一个视图,我将ViewModels列表传递给该视图。然后,我想选择其中一个ViewModels并将其发布回控制器。我目前有一个foreach循环遍历每个ViewModel并显示它们的数据。对于每个ViewModel,都有一个submit按钮,但它似乎不像我希望的那样工作

代码如图所示:

@model IEnumerable<RoboticsScheduler.Models.UserVM>
...
@using (Html.BeginForm("UserApproved", "Approve"))
{
...
    @foreach (var item in Model)
    {
    ...
    @*Modified model data*@
    ...
    <div class="form-group">
        <div class="col-md-offset-2 col-md-10">
            <input type="submit" value="Approve User" class="btn btn-default" />
        </div>
    </div>
    }
}
然后在我的接受控制器中,我将UserVM作为参数,但我在控制器中不断得到空的UserVM

控制器中的代码:

public async Tas<ActionResult> UserApproved(UserVM uvm)
{
...
}
如果有人对我如何完成这样的事情提出建议,那就太好了

编辑

显示更深入的视图,并将BeginForm移动到循环中:

...
@foreach (var item in Model)
{
    using (Html.BeginForm("Index", "Approved", FormMethod.Post))
    {
        <tr>
            <td>
                @Html.HiddenFor(modelItem => item.Id)
                @Html.DisplayFor(modelItem => item.Email)
            </td>
            <td>
                @item.EmailConfirmed.ToString()
            </td>
            <td>
                <span class="col-md-10">
                    @{List<string> roles = ViewBag.Roles;
                    int i = 0;
                    }
                    @foreach (string role in roles)
                    {
                        @Html.EditorFor(modelItem => item.Roles[i])
                        @Html.Label(role, new { @class = "control-label" })
                        <br />
                    }
                </span>
            </td>
            <td>
                <div class="form-group">
                    <div class="col-md-offset-2 col-md-10">
                        <input type="submit" value="Approve User" class="btn btn-default" />
                    </div>
                </div>
            </td>
        </tr>
    }
}

解决方案是了解在发布表单时MVC如何将值绑定回控制器。详细解释

解决方案: 您的视图模型是@model IEnumerable,因此表单提交变得棘手,因为您没有处理单个对象。您已经获得了正确的for循环,但是您需要了解它发生了什么

@foreach (string role in roles)
 {
    @Html.EditorFor(modelItem => item.Roles[i])
    @Html.Label(role, new { @class = "control-label" })
    <br />
 }
在这里,我隐式地为输入标记命名。我还假设您的ViewModel UserVM有一个名为RoleType的属性。像下面这样

public class UserVM
{
  public string RoleType {get;set;}
  public int UserId {get;set;} // you will see why this in my below explanation
} 
现在,当您提交表单时,MVC将看到控制器操作的输入参数类型,即它的UserVM对象,现在它在该对象中查找与表单中的输入元素同名的属性。在我们的例子中,我们有一个名为RoleType的输入标记,并且在对象内部有一个名为RoleType的属性,因此MVC很乐意绑定该值

建议: 在阅读了您的评论之后,我了解到您希望显示一个角色下拉列表,用户希望从中选择一个角色。所以你可以试试这个

更改从Viewbag提取数据的方式的逻辑

@{
   List<string> roles = ViewBag.Roles;
   int i = 0;
   var RolesList = roles.select(c => new SelectListItem {Text=c,Value=c}).ToList();
 }
您仍然可以使用相同的控制器语法。这是为了让您更加清楚MVC所做的模型绑定。您可以尝试并在action方法中检查对象,以查看填充到对象各自属性的值

//Person
@model MvcApplication1.Models.Person
@{
     ViewBag.Title = "Index";    
 }

<h2>Person</h2>

@using (Html.BeginForm("Index", "Home", FormMethod.Post))
{
    <div>
        @Html.TextBoxFor(model => model.Id, new { id = "Id"})
    </div>

    <div>
        @Html.TextBoxFor(model => model.Nome, new { id = "Name" })
    </div>

     if (Model != null && Model.Phone != null)
     {
         foreach (var phone in Model.Phone)
         {
             Html.RenderPartial("_Phone", phone);
         }
     }    

    <input type="submit" value="Save" />
}


//phone
@model MvcApplication1.Models.Phone

@using (Html.BeginCollectionItem("Phone"))
{
     <div>
        <div>
            @Html.EditorFor(model => model.PhoneNumber)
        </div>
    </div>
}
使用Html.BeginCollectionItem

您需要显示视图-如何为每个UserVM生成控件?如果因为你的观点是错误的而没有约束力,那么这个观点就不好。除非您在没有任何html助手的情况下手动创建html,否则foreach循环无法生成正确的名称属性。您需要显示视图。@Jon,唯一重要的部分是如何生成控件,只需查看模型的一个或两个属性即可。是的,但答案取决于您在这里真正想做什么。在一个窗体中为集合中的每个项生成控件,这表明您实际上想要回发整个集合。这是正确的吗?没有区别-看看为隐藏输入和单选按钮生成的html。它们的名称属性与您的模型没有任何关系,因此在您发布文章时将永远不会绑定到您的模型。这是反MVC的,会产生各种问题。它不会提供真正的双向模型绑定,如果需要返回视图,它将失败,客户端验证将不可能,它将创建无效的html等。新的{name=UserId}将不会更改name属性,因此它无论如何都不会绑定不确定您在该语句中的意思如果需要返回视图,它将失败,我确实同意。只有使用EditorFor和数据转换才能实现此功能。但是我不同意新的{name=UserId}将不起作用的事实,因为我在大多数情况下都能使它起作用。如果我错了,请纠正我。我也拒绝接受这是反MVC的说法,因为我所做的只是更改name属性,这对我来说很好。好的,这里有一个场景。我们可以在razor视图中编写纯HTML语法,对吗?我们可以让它与@Html.EditorFor.完全一样工作。。最终呈现的DOM是纯HTML。因此,更改name属性或编写纯HTML都是一样的。这一切都取决于开发人员的舒适区。您将一个对象发布到控制器,但是如果由于服务器端验证错误而需要返回视图,那么它将失败,因为视图需要一个集合,它将抛出一个异常!它不会更改name属性,请检查源代码以了解原因。有一个解决方法可以做到这一点,你几乎已经做到了,但是对于不了解MVC和模型绑定内部工作原理的初学者来说,这太危险了。
@foreach (var item in Model)
   {
    using (Html.BeginForm("Index", "Approved", FormMethod.Post))
    {
        @Html.HiddenFor(modelItem => item.Id, new {Name = "UserId"}) 
        @Html.DropDownList("RoleType",RolesList)
        @Html.Label(role, new { @class = "control-label" })
        <br />
    }
   }
yourlocalhost/Approved/Index?RoleType="Admin"&UserId="23"
//Person
@model MvcApplication1.Models.Person
@{
     ViewBag.Title = "Index";    
 }

<h2>Person</h2>

@using (Html.BeginForm("Index", "Home", FormMethod.Post))
{
    <div>
        @Html.TextBoxFor(model => model.Id, new { id = "Id"})
    </div>

    <div>
        @Html.TextBoxFor(model => model.Nome, new { id = "Name" })
    </div>

     if (Model != null && Model.Phone != null)
     {
         foreach (var phone in Model.Phone)
         {
             Html.RenderPartial("_Phone", phone);
         }
     }    

    <input type="submit" value="Save" />
}


//phone
@model MvcApplication1.Models.Phone

@using (Html.BeginCollectionItem("Phone"))
{
     <div>
        <div>
            @Html.EditorFor(model => model.PhoneNumber)
        </div>
    </div>
}