2011-01-18 19 views
6

Encontramos un comportamiento extraño en DropDownListFor (versión ASP.NET MVC3). Selecciona el valor de la propiedad ViewBag en lugar del valor de la propiedad Model en el menú desplegable.Valor de propiedad ViewBag en DropDownListFor en lugar del valor de propiedad del modelo

Modelo:

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

Índice de controlador de acción:

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); 

Vista:

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

cuadro de texto se mostrará con el valor "Rusia" y desplegable con el valor "Danmark" seleccionado en lugar de "Rusia".

No se encontró documentación sobre este comportamiento. ¿Es este comportamiento normal? ¿Y por qué es normal? Porque es muy difícil controlar los nombres de las propiedades ViewBag y Model.

This sample MVC3 project sources

Respuesta

5

No estoy seguro de por qué se tomó esta decisión, pero sucedió porque el marco MVC intentó usar el valor proporcionado por ViewData antes de usar el valor proporcionado por el parámetro. Es por eso que ViewBag.Country anula el valor proporcionado por el parámetro Model.Country.

Eso era lo que estaba en written framework MVC en el método privadaSelectInternal.

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; 
} 

Este código defaultValue = htmlHelper.ViewData.Eval(fullName); trataron de obtener el valor de ViewData y si se puede obtener el valor, se anulará el parámetro suministrado selectList con la nueva lista.

Espero que te pueda ayudar. Gracias.

side-node: ViewBag es simplemente una clase contenedora dinámica de ViewData.

4

La siguiente línea de su método de acción es lo que se confunde el código:

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

Eso es porque los ayudantes HTML miran en unos pocos lugares diferentes para recoger los valores de los controles generados. En este caso, ViewData["Country"] está en conflicto con ModelState["Country"] Cambie el nombre de esa propiedad a otra cosa y todo debería funcionar.

+0

¿Sugiere siempre utilizar el prefijo para el nombre de la propiedad ViewBag para evitar el conflicto de nombres? Ex. ViewBag.ViewBag_Country –

+0

Santa mierda, tienes razón. Gracias por esto ... me salvó el día ... :) – dizzwave

Cuestiones relacionadas