DropDownListFor中的ViewBag属性值而不是Model属性值

时间:2011-01-18 12:39:57

标签: asp.net-mvc-3

我们在DropDownListFor(ASP.NET MVC3版本)中发现了奇怪的行为。它在下拉列表中选择ViewBag属性值而不是Model属性值。

型号:

public class Country {
    public string Name { get; set; }
}
public class User {
    public Country Country { get; set; }
}

控制器索引操作:

ViewBag.CountryList = new List<Country> {  /* Dropdown collection */
   new Country() { Name = "Danmark" }, 
   new Country() { Name = "Russia" } }; 

var user = new User();
user.Country = new Country(){Name = "Russia"}; /* User value */
ViewBag.Country = new Country() { Name = "Danmark" };  /* It affects user */
return View(user); 

查看:

@Html.EditorFor(user => user.Country.Name)      
@Html.DropDownListFor(user => user.Country.Name,
    new SelectList(ViewBag.CountryList, "Name", "Name", Model.Country), "...")

它将显示带有“俄罗斯”值的文本框,并选择“Danmark”值而不是“俄罗斯”。

我没有找到有关此行为的任何文档。这种行为是否正常?为什么这是正常的?因为很难控制ViewBag和Model属性名称。

This sample MVC3 project sources

2 个答案:

答案 0 :(得分:5)

我不太确定为什么做出这个决定,但是之所以发生这种情况是因为MVC框架在使用参数提供的值之前尝试使用ViewData提供的值。这就是ViewBag.Country覆盖参数提供的值Model.Country的原因。

这就是私有方法SelectInternal中MVC框架中的written

object defaultValue = (allowMultiple) ? htmlHelper.GetModelStateValue(fullName, typeof(string[])) : htmlHelper.GetModelStateValue(fullName, typeof(string));

// If we haven't already used ViewData to get the entire list of items then we need to
// use the ViewData-supplied value before using the parameter-supplied value.
if (!usedViewData) {
    if (defaultValue == null) {
        defaultValue = htmlHelper.ViewData.Eval(fullName);
    }
}

if (defaultValue != null) {
    IEnumerable defaultValues = (allowMultiple) ? defaultValue as IEnumerable : new[] { defaultValue };
    IEnumerable<string> values = from object value in defaultValues select Convert.ToString(value, CultureInfo.CurrentCulture);
    HashSet<string> selectedValues = new HashSet<string>(values, StringComparer.OrdinalIgnoreCase);
    List<SelectListItem> newSelectList = new List<SelectListItem>();

    foreach (SelectListItem item in selectList) {
        item.Selected = (item.Value != null) ? selectedValues.Contains(item.Value) : selectedValues.Contains(item.Text);
        newSelectList.Add(item);
    }
    selectList = newSelectList;
}

此代码defaultValue = htmlHelper.ViewData.Eval(fullName);尝试从ViewData获取值,如果它可以获取值,它将使用新列表覆盖提供的参数selectList

希望它可以提供帮助。谢谢。

side-node :ViewBag只是​​ViewData的动态包装类。

答案 1 :(得分:4)

您的操作方法中的以下行是令代码混淆的内容:

ViewBag.Country = new Country() { Name = "Danmark" };  /* It affects user */

那是因为html帮助器会查看几个不同的位置来为生成的控件选取值。在这种情况下,ViewData["Country"]ModelState["Country"]发生冲突,将该属性重命名为其他内容,一切都应该有效。