2011-01-18 2 views
6

Мы обнаружили странное поведение в DropDownListFor (выпуск ASP.NET MVC3). Он выбирает значение свойства ViewBag вместо значения свойства модели в раскрывающемся списке.Значение свойства ViewBag в DropDownListFor вместо значения свойства модели

Модель:

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

Index контроллер действия:

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

ответ

5

Я не уверен, почему это решение было принято, но это произошло потому, что MVC Framework попытался использовать значение, указанное в представлении ViewData, перед использованием параметра, предоставленного параметром. Вот почему ViewBag.Country переопределяет значение параметра Model.Country.

Вот как это было written в рамках MVC в методе SelectInternalчастного.

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.

4

Следующая строка из метода действий является то, что сбивает с толку код:

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

Это потому, что HTML-хелперы заглянуть в несколько разных мест, чтобы подобрать значения для созданных элементов управления. В этом случае ViewData["Country"] сталкивается с ModelState["Country"] Переименуйте это свойство на что-то еще, и все должно работать.

+0

Вы предлагаете всегда использовать префикс для имени свойства ViewBag, чтобы избежать столкновения имен? Ex. ViewBag.ViewBag_Country –

+0

Святое дерьмо, вы правы. Спасибо за это ... спасли мой день ... :) – dizzwave

Смежные вопросы