Domanda

Sto usando ASP.NET MVC 2 Beta. Posso creare una procedura guidata come flusso di lavoro usando la tecnica di Steven Sanderson (nel suo libro Pro ASP.NET MVC Framework), ad eccezione utilizzando Sessione invece dei campi modulo nascosti per conservare i dati tra le richieste. Posso andare avanti e indietro tra le pagine e mantenere i valori in un controllo TextBox senza alcun problema quando il mio modello non è una collezione. Un esempio potrebbe essere un modello persona semplice:

public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
    public string Email { get; set; }
}

Ma io sono in grado di ottenere questo al lavoro quando mi passa intorno ad un IEnumerable. A mio avviso che sto cercando di correre attraverso il modello e generare una casella di testo per nome ed e-mail per ogni persona nella lista. Posso generare la forma fine e posso inviare il modulo con i miei valori e andare a Step2. Ma quando si fa clic sul pulsante Indietro nel Step2 che mi riporta a Step1 con una forma vuota. Nessuno dei campi che ho già popolate sono lì. Ci deve essere qualcosa che mi manca. Qualcuno può darmi una mano?

Ecco la mia vista:

<% using (Html.BeginForm()) { %>
<% int index = 0;
   foreach (var person in Model) { %>
       <fieldset>
            <%= Html.Hidden("persons.index", index.ToString())%>
            <div>Name: <%= Html.TextBox("persons[" + index.ToString() + "].Name")%></div>
            <div>Email: <%= Html.TextBox("persons[" + index.ToString() + "].Email")%></div>
       </fieldset>
       <% index++;
   } %>  
   <p><input type="submit" name="btnNext" value="Next >>" /></p>
<% } %>

E qui è il mio controller:

public class PersonListController : Controller
{
    public IEnumerable<Person> persons;

    protected override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        persons = (Session["persons"]
            ?? TempData["persons"]
            ?? new List<Person>()) as List<Person>;
        // I've tried this with and without the prefix.
        TryUpdateModel(persons, "persons"); 
    }

    protected override void OnResultExecuted(ResultExecutedContext filterContext)
    {
        Session["persons"] = persons;

        if (filterContext.Result is RedirectToRouteResult)
            TempData["persons"] = persons;
    }

    public ActionResult Step1(string btnBack, string btnNext)
    {
        if (btnNext != null)
            return RedirectToAction("Step2");

        // Setup some fake data
        var personsList = new List<Person> 
            { 
                new Person { Name = "Jared", Email = "test@email.com", },
                new Person { Name = "John", Email = "test2@email.com" } 
            };

        // Populate the model with fake data the first time
        // the action method is called only. This is to simulate
        // pulling some data in from a DB.
        if (persons == null || persons.Count() == 0)
            persons = personsList;

        return View(persons);
    }

    // Step2 is just a page that provides a back button to Step1
    public ActionResult Step2(string btnBack, string btnNext)
    {
        if (btnBack != null)
            return RedirectToAction("Step1");

        return View(persons);
    }
}
È stato utile?

Soluzione

Per quanto posso dire, questo non è supportato in ASP.NET MVC 2 Beta, né è supportato in ASP.NET MVC 2 RC. Ho scavato attraverso il codice sorgente di MVC e sembra modelli come dizionari sono supportati, ma non che sono IEnumerable <> (o che contengono nidificato oggetti IEnumerable) ed è eredi come IList <>.

Il problema è nella classe ViewDataDictionary. In particolare, il metodo GetPropertyValue fornisce solo un modo per recuperare i valori delle proprietà da proprietà del dizionario (chiamando GetIndexedPropertyValue) o in semplici utilizzando il metodo PropertyDescriptor.GetValue di tirare fuori il valore.

Per risolvere questo problema, ho creato un metodo GetCollectionPropertyValue che gestisce I modelli che sono collezioni (e anche i modelli che contengono collezioni nidificato). Sto incollando il codice qui per riferimento. Nota: Non faccio alcun reclamo circa l'eleganza - in realtà tutto il parsing della stringa è abbastanza brutto, ma sembra funzionare. Ecco il metodo:

// Can be used to pull out values from Models with collections and nested collections.
        // E.g. Persons[0].Phones[3].AreaCode
        private static ViewDataInfo GetCollectionPropertyValue(object indexableObject, string key)
        {
            Type enumerableType = TypeHelpers.ExtractGenericInterface(indexableObject.GetType(), typeof(IEnumerable<>));
            if (enumerableType != null)
            {
                IList listOfModelElements = (IList)indexableObject;

                int firstOpenBracketPosition = key.IndexOf('[');
                int firstCloseBracketPosition = key.IndexOf(']');

                string firstIndexString = key.Substring(firstOpenBracketPosition + 1, firstCloseBracketPosition - firstOpenBracketPosition - 1);
                int firstIndex = 0;
                bool canParse = int.TryParse(firstIndexString, out firstIndex);

                object element = null;
                // if the index was numeric we should be able to grab the element from the list
                if (canParse)
                    element = listOfModelElements[firstIndex];

                if (element != null)
                {
                    int firstDotPosition = key.IndexOf('.');
                    int nextOpenBracketPosition = key.IndexOf('[', firstCloseBracketPosition);

                    PropertyDescriptor descriptor = TypeDescriptor.GetProperties(element).Find(key.Substring(firstDotPosition + 1), true);

                    // If the Model has nested collections, we need to keep digging recursively
                    if (nextOpenBracketPosition >= 0)
                    {
                        string nextObjectName = key.Substring(firstDotPosition+1, nextOpenBracketPosition-firstDotPosition-1);
                        string nextKey = key.Substring(firstDotPosition + 1);

                        PropertyInfo property = element.GetType().GetProperty(nextObjectName);
                        object nestedCollection = property.GetValue(element,null);
                        // Recursively pull out the nested value
                        return GetCollectionPropertyValue(nestedCollection, nextKey);
                    }
                    else
                    {
                        return new ViewDataInfo(() => descriptor.GetValue(element))
                        {
                            Container = indexableObject,
                            PropertyDescriptor = descriptor
                        };
                    }
                }
            }

            return null;
        }

E qui è il metodo GetPropertyValue modificato che chiama il nuovo metodo:

private static ViewDataInfo GetPropertyValue(object container, string propertyName) {
            // This method handles one "segment" of a complex property expression

            // First, we try to evaluate the property based on its indexer
            ViewDataInfo value = GetIndexedPropertyValue(container, propertyName);
            if (value != null) {
                return value;
            }

            // If the indexer didn't return anything useful, continue...

            // If the container is a ViewDataDictionary then treat its Model property
            // as the container instead of the ViewDataDictionary itself.
            ViewDataDictionary vdd = container as ViewDataDictionary;
            if (vdd != null) {
                container = vdd.Model;
            }

            // Second, we try to evaluate the property based on the assumption
            // that it is a collection of some sort (e.g. IList<>, IEnumerable<>)
            value = GetCollectionPropertyValue(container, propertyName);
            if (value != null)
            {
                return value;
            }

            // If the container is null, we're out of options
            if (container == null) {
                return null;
            }

            // Third, we try to use PropertyDescriptors and treat the expression as a property name
            PropertyDescriptor descriptor = TypeDescriptor.GetProperties(container).Find(propertyName, true);


            if (descriptor == null) {
                return null;
            }

            return new ViewDataInfo(() => descriptor.GetValue(container)) {
                Container = container,
                PropertyDescriptor = descriptor
            };
        }

Ancora una volta, questo è nel file ViewDataDictionary.cs in ASP.NET MVC 2 RC. Devo creare una nuova emissione per monitorare questo sul sito CodePlex MVC?

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top