2011-01-18 12 views
6

Abbiamo rilevato un comportamento strano in DropDownListFor (versione MVC3 di ASP.NET). Seleziona il valore della proprietà ViewBag invece del valore della proprietà Model nel menu a discesa.Valore della proprietà ViewBag in DropDownListFor invece del valore della proprietà Model

Modello:

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

Indice Action Controller:

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), "...") 

Sarà casella di testo mostrerà con valore "Russia" e discesa con valore "Danmark" selezionato al posto di "Russia".

Non ho trovato alcuna documentazione su questo comportamento. Questo comportamento è normale? E perché è normale? Perché è molto difficile controllare i nomi delle proprietà di ViewBag e Model.

This sample MVC3 project sources

risposta

5

Non sono sicuro del motivo per cui è stata presa questa decisione, ma è successo perché il framework MVC ha tentato di utilizzare il valore fornito da ViewData prima di utilizzare il valore fornito dal parametro. Ecco perché ViewBag.Country sostituisce il valore fornito dal parametro Model.Country.

Quello era come era written nel framework MVC nel metodo SelectInternalprivato.

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

Questo codice defaultValue = htmlHelper.ViewData.Eval(fullName); cercato di ottenere il valore da ViewData e se si può ottenere il valore, avrà la precedenza il parametro fornito selectList con nuova lista.

Spero che possa essere d'aiuto. Grazie.

nodo laterale: ViewBag è solo una classe wrapper dinamica di ViewData.

4

la riga seguente dal tuo metodo di azione è ciò che sta confondendo il codice:

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

Questo perché gli aiutanti html guardare in un paio di luoghi diversi per raccogliere i valori per i controlli generati. In questo caso ViewData["Country"] si scontrano con ModelState["Country"] Rinominare quella proprietà in qualcos'altro e tutto dovrebbe funzionare.

+0

Suggerite di utilizzare sempre il prefisso per il nome della proprietà ViewBag per evitare conflitti tra i nomi? Ex. ViewBag.ViewBag_Country –

+0

holy crap, hai ragione. Grazie per questo ... ho salvato la mia giornata ... :) – dizzwave

Problemi correlati