Html.DropDownListFor用于复杂子对象的绑定

时间:2015-03-03 21:07:09

标签: c# asp.net-mvc razor

我有以下代码(ASP.NET MVC / C#)...为了简洁起见,简化了一些代码。

public class UserViewModel
{
   public int UserId { get; set; }
   public string Name { get; set; }
   public AddressViewModel Address { get; set; }

   public IEnumerable<SelectListItem> CitySelectList { get; set; }
}

public class AddressViewModel
{
   public int AddressId { get; set; }
   public string StreetAddress { get; set; }
   public int CityId { get; set; }
}

在剃须刀视图中,我有以下内容:

@Html.DropDownListFor(x => x.Address.CityId, Model.CitySelectList)

在加载时的下拉列表中选择正确的城市(即......原始值)以便工作正常,但是当我提交表单时,CityId属性不会使用新选择的值进行更新....它仍然包含原始城市的id。

我向UserViewModel添加了一个名为&#34; SelectedCityId&#34;的新属性。并将其绑定到像这样的下拉列表,以测试复杂的嵌套对象是问题:

@Html.DropDownListFor(x => x.SelectedCityId, Model.CitySelectList)

当我使用此方法提交表单时,SelectedCityId确实包含正确的id,因此当对象是一个简单的对象时,我可以看到模型绑定正常工作,但是当它是一个嵌套的子对象时,它不能正常工作。

仅仅为了排除故障,我检查了邮政编码字段,因为它也绑定到Address类的子属性,但是在使用类似的TextBoxFor帮助程序时它确实正确绑定:

@Html.TextBoxFor(x => x.Address.PostalCode)

所以,我的问题是,当表达式将控件绑定到嵌套的子属性时,为什么模型绑定似乎不适用于DropDownListFor帮助器?我确定它缺少一些微不足道的东西,但是我没看到它是什么,并且在这里没有找到类似的答案。

编辑: 这是cshtml文件中的整个html表单

@using (Html.BeginForm("MyProfile", "Account", FormMethod.Post, new { id = "myProfileForm" }))
        {
            @Html.HiddenFor(x => x.UserId)
            @Html.HiddenFor(x => x.AddressId)
            @Html.HiddenFor(x => x.Address.CityId)

            <article class="mysettings">
                <h1>Personal details</h1>
                <table>
                    <tr>


                <th>E-mail:</th>
                    <td>@Model.Username
                        <!--edit fields-->
                        <div class="edit_field" id="field3">
                            <label for="new_email">New Email:</label>
                            @Html.TextBoxFor(x => x.Username, new { id = "new_email" })
                            <input type="submit" value="save" class="gradient-button" id="submit3"/>
                            <a href="#">Cancel</a>
                        </div>
                        <!--//edit fields-->
                    </td>
                    <td></td>
                </tr>
                <tr>
                    <th>First name:</th>
                    <td>@Model.FirstName
                        <!--edit fields-->
                        <div class="edit_field" id="field1">
                            <label for="new_name">New First Name:</label>
                            @Html.TextBoxFor(x => x.FirstName, new { id = "new_name" })
                            <input type="submit" value="save" class="gradient-button" id="submit1"/>
                            <a href="#">Cancel</a>
                        </div>
                        <!--//edit fields-->
                    </td>
                    <td><a href="#field1" class="gradient-button edit">Edit</a></td>
                </tr>
                <tr>
                    <th>Last name:</th>
                    <td>@Model.LastName
                        <!--edit fields-->
                        <div class="edit_field" id="field2">
                            <label for="new_last_name">New Last Name:</label>
                            @Html.TextBoxFor(x => x.LastName, new { id = "new_last_name" })
                            <input type="submit" value="save" class="gradient-button" id="submit2"/>
                            <a href="#">Cancel</a>
                        </div>
                        <!--//edit fields-->
                    </td>
                    <td><a href="#field2" class="gradient-button edit">Edit</a></td>
                </tr>
                <tr>
                    <th>Display name:</th>
                    <td>@Model.DisplayName
                        <!--edit fields-->
                        <div class="edit_field" id="field_display">
                            <label for="new_display_name">New Display Name:</label>
                            @Html.TextBoxFor(x => x.DisplayName, new { id = "new_display_name" })
                            <input type="submit" value="save" class="gradient-button" id="submit_display"/>
                            <a href="#">Cancel</a>
                        </div>
                        <!--//edit fields-->
                    </td>
                    <td><a href="#field_display" class="gradient-button edit">Edit</a></td>
                </tr>
                <tr>
                    <th>Password:</th>
                    <td><input type="password" value="@Model.Membership.Password" disabled="disabled" style="border: none; background-color: white; padding: 0;" />
                        <!--edit fields-->
                        <div class="edit_field" id="field4">
                            <label for="new_password">New Password:</label>
                            @Html.PasswordFor(x => x.Membership.Password, new { id = "new_password" })
                            <input type="submit" value="save" class="gradient-button" id="submit4"/>
                            <a href="#">Cancel</a>
                        </div>
                        <!--//edit fields-->
                    </td>
                    <td></td>
                </tr>
                <tr>
                    <th>Cell phone:</th>
                    <td>@Model.CellPhone
                        <!--edit fields-->
                        <div class="edit_field" id="field_cell">
                            <label for="new_cell">New Cell Phone:</label>
                            @Html.TextBoxFor(x => x.CellPhone, new { id = "new_cell" })
                            <input type="submit" value="save" class="gradient-button" id="submit_cell"/>
                            <a href="#">Cancel</a>
                        </div>
                        <!--//edit fields-->
                    </td>
                    <td><a href="#field_cell" class="gradient-button edit">Edit</a></td>
                </tr>
                <tr>
                    <th>Home phone:</th>
                    <td>@Model.HomePhone
                        <!--edit fields-->
                        <div class="edit_field" id="field_home">
                            <label for="new_home">New Home Phone:</label>
                            @Html.TextBoxFor(x => x.HomePhone, new { id = "new_home" })
                            <input type="submit" value="save" class="gradient-button" id="submit_home"/>
                            <a href="#">Cancel</a>
                        </div>
                        <!--//edit fields-->
                    </td>
                    <td><a href="#field_home" class="gradient-button edit">Edit</a></td>
                </tr>
                <tr>
                    <th>Street Address:</th>
                    <td>@(Model.Address != null ? Model.Address.Address1 : "No address given")
                        <!--edit fields-->
                        <div class="edit_field" id="field5">
                            <label for="new_address">New Address:</label>
                            @Html.TextBoxFor(x => x.Address.Address1, new { id = "new_address" })
                            <input type="submit" value="save" class="gradient-button" id="submit5"/>
                            <a href="#">Cancel</a>
                        </div>
                        <!--//edit fields-->
                    </td>
                    <td><a href="#field5" class="gradient-button edit">Edit</a></td>
                </tr>

                <tr>
                    <th>Town / City:</th>
                    <td>@(Model.Address != null ? Model.Address.City.Name : "No address given")
                        <!--edit fields-->
                        <div class="edit_field" id="field6">
                            <label for="new_city">New City:</label>
                            @Html.DropDownListFor(x => x.Address.CityId, Model.CitySelectList, new { @class = "f-item" })
                            <input type="submit" value="save" class="gradient-button" id="submit6"/>
                            <a href="#">Cancel</a>
                        </div>
                        <!--//edit fields-->
                    </td>
                    <td><a href="#field6" class="gradient-button edit">Edit</a></td>
                </tr>

                <tr>
                    <th>Postal Code:</th>
                    <td>@(Model.Address != null ? Model.Address.PostalCode : "No address given")
                        <!--edit fields-->
                        <div class="edit_field" id="field7">
                            <label for="new_zip">New Postal Code:</label>
                            @Html.TextBoxFor(x => x.Address.PostalCode, new { id = "new_zip" })
                            <input type="submit" value="save" class="gradient-button" id="submit7"/>
                            <a href="#">Cancel</a>
                        </div>
                        <!--//edit fields-->
                    </td>
                    <td><a href="#field7" class="gradient-button edit">Edit</a></td>
                </tr>

                <tr>
                    <th>First Admin Division (State):</th>
                    <td>@(Model.Address != null ? Model.Address.City.FirstAdminDivision.Name : "No address given")
                        <!--edit fields-->
                        <div class="edit_field" id="field8">
                            <label for="new_state">New First Admin Division (State):</label>
                            <input type="text" id="new_state"/>
                            <input type="submit" value="save" class="gradient-button" id="submit8"/>
                            <a href="#">Cancel</a>
                        </div>
                        <!--//edit fields-->
                    </td>
                    <td><a href="#field8" class="gradient-button edit">Edit</a></td>
                </tr>

                <tr>
                    <th>Country:</th>
                    <td>@(Model.Address != null ? Model.Address.City.FirstAdminDivision.Country.Name : "No address given")
                        <!--edit fields-->
                        <div class="edit_field" id="field9">
                            <label for="new_country">New Country:</label>
                            <input type="text" id="new_country"/>
                            <input type="submit" value="save" class="gradient-button" id="submit9"/>
                            <a href="#">Cancel</a>
                        </div>
                        <!--//edit fields-->
                    </td>
                    <td><a href="#field8" class="gradient-button edit">Edit</a></td>
                </tr>
            </table>

        </article>
    }

两个控制器动作:

public ActionResult MyProfile()
        {
            IAccountService accountSvc = GetService<IAccountService>();
            UserProfileServiceRequest request = new UserProfileServiceRequest();

            int userId = int.Parse(SessionMgr.GetInstance().GetSessionValue(SessionTypes.UserId).ToString());

            if (userId == 0)
            {
                return RedirectToAction("Login", "Account");
            }
            else
            {
                request.UserId = userId;
                UserProfileServiceResponse response = accountSvc.GetUserProfile(request);
                UserProfileViewModel model = AutoMapper.Mapper.Map<UserProfileViewModel>(response.UserProfile);

                ICommonService commonSvc = GetService<ICommonService>();
                GetCitiesServiceRequest citiesRequest = new GetCitiesServiceRequest { FirstAdminDivisionId = model.Address.City.FirstAdminDivision.FirstAdminDivisionId };
                KeyValuePairServiceResponse citiesResponse = commonSvc.GetCitiesForSelect(citiesRequest);
                model.CitySelectList = citiesResponse.KeyValuePairs.ToSelectList();

                List<RoleViewModel> roles = (List<RoleViewModel>)SessionMgr.GetInstance().GetSessionValue(SessionTypes.Roles);
                RoleViewModel consumerRole = roles.FirstOrDefault(x => x.Meaning.Equals(DataEnumerations.GetRoleMeaning(DataEnumerations.Role.Consumer)));

                if (consumerRole != null)
                {
                    GetConsumerTripsServiceRequest consumerTripsRequest = new GetConsumerTripsServiceRequest() { UserId = userId };
                    GetConsumerTripsServiceResponse consumerTripsResponse = commonSvc.GetConsumerTrips(consumerTripsRequest);
                    model.ConsumerTrips = AutoMapper.Mapper.Map<List<TripViewModel>>(consumerTripsResponse.Trips);

                    GetUserReviewsServiceRequest userReviewsRequest = new GetUserReviewsServiceRequest() { UserId = userId };
                    GetUserReviewsServiceResponse userReviewsResponse = commonSvc.GetUserReviews(userReviewsRequest);
                    model.UserReviews = AutoMapper.Mapper.Map<List<UserReviewViewModel>>(userReviewsResponse.UserReviews);
                }

                return View(model);
            }
        }

[HttpPost]
        public ActionResult MyProfile(UserProfileViewModel model)
        {
            UpdateUserProfileServiceRequest request = AutoMapper.Mapper.Map<UpdateUserProfileServiceRequest>(model);

            IAccountService accountSvc = GetService<IAccountService>();
            accountSvc.UpdateUserProfile(request);

            return RedirectToAction("MyProfile", "Account");

        }

整个AddressViewModel类:

public class AddressViewModel
    {
        public Int32 AddressId { get; set; }
        public String Address1 { get; set; }
        public String Address2 { get; set; }
        public Int32 CityId { get; set; }
        public String PostalCode { get; set; }

        public CityViewModel City { get; set; }

    }

整个UserProfileViewModel:

public class UserProfileViewModel : BaseViewModel
    {
        public int UserId { get; set; }
        public string Username { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string DisplayName { get; set; }
        public string CellPhone { get; set; }
        public string HomePhone { get; set; }
        public int AddressId { get; set; }

        public AddressViewModel Address { get; set; }
        public MembershipViewModel Membership { get; set; }

        public List<TripViewModel> ConsumerTrips { get; set; }

        public List<UserReviewViewModel> UserReviews { get; set; }

        public IEnumerable<SelectListItem> CitySelectList { get; set; }
        public int SelectedCityId { get; set; }
    }

1 个答案:

答案 0 :(得分:2)

除了下拉列表

之外,您的表单还包含该属性的隐藏输入
@Html.HiddenFor(x => x.Address.CityId)
....
@Html.DropDownListFor(x => x.Address.CityId, Model.CitySelectList, ..)

实际上,您传回的是同一属性的2个值。 DefaultModelBinder读取第一个(来自隐藏输入,即原始值)并设置Address.CityId的值。

将忽略同一属性的任何后续值(来自下拉列表)

删除隐藏的输入,您的模型将与所选值正确绑定。