ASP.NET MVC: Wie TextBox Staat zu erhalten, wenn Ihr Ansichtsmodell ist eine Sammlung / Liste / IEnumerable

StackOverflow https://stackoverflow.com/questions/1878087

Frage

Ich bin mit ASP.NET MVC 2 Beta. Ich kann einen Assistenten wie Workflow mit Steven Sanderson Technik (in seinem Buch Pro ASP.NET MVC Framework) außer der Verwendung von Session statt versteckten Formularfelder erstellen, um die Daten über Anfragen zu erhalten. Ich kann hin und her zwischen den Seiten gehen und die Werte in einem Text ohne Problem zu halten, wenn mein Modell nicht eine Sammlung ist. Ein Beispiel wäre eine einfache Person Modell:

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

Aber ich bin nicht in der Lage, dies zu erhalten zu arbeiten, wenn ich um eine IEnumerable passieren. Meiner Ansicht nach versuche ich durch das Modell und erzeugen einen Text für Namen und E-Mail für jede Person in der Liste auszuführen. Ich kann die Form fein erzeugen, und ich kann das Formular mit meinen Werten einreichen und gehen Sie zu Schritt 2. Aber wenn ich auf die Schaltfläche Zurück in Schritt 2 klicken nimmt es mich mit einem leeren Formular Schritt 1 zurück. Keines der Felder, die ich vorher gibt es bevölkert. Es muss etwas sein, ich bin fehlt. Kann mir jemand helfen?

Hier ist meine Ansicht:

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

Und hier ist mein 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);
    }
}
War es hilfreich?

Lösung

Soweit ich sagen kann, ist dies nicht in ASP.NET MVC 2 Beta unterstützt, noch ist es unterstützt in ASP.NET MVC 2 RC. Ich grub durch den Quellcode MVC und es sieht aus wie Wörterbücher unterstützt werden, aber nicht Modelle, die IEnumerable sind <> (oder die IEnumerable-Objekte enthalten verschachtelte) und die Erben wie IList <>.

Das Problem ist in der Viewdatadictionary Klasse. Insbesondere stellt die GetPropertyValue Methode nur eine Art und Weise Eigenschaftswerte von Wörterbuch-Eigenschaften abzurufen (durch GetIndexedPropertyValue Aufruf) oder einfache Eigenschaften der PropertyDescriptor.GetValue Methode unter Verwendung des Wertes zu ziehen.

Um dies zu beheben, habe ich eine GetCollectionPropertyValue Methode, die Modelle verarbeitet, die Sammlungen (und sogar Modelle, die Sammlungen enthalten verschachtelt) sind. Ich bin Einfügen des Code hier als Referenz. Hinweis: Ich mache keine Ansprüche über Eleganz - in der Tat alle die Zeichenfolgenanalyse ziemlich hässlich ist, aber es scheint zu funktionieren. Hier ist die Methode:

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

Und hier ist das modifizierte GetPropertyValue Verfahren, das die neue Methode aufruft:

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

Auch dies ist in der ViewDataDictionary.cs Datei in ASP.NET MVC 2 RC. Sollte ich ein neues Problem schaffen dies auf dem MVC Codeplex-Website zu verfolgen?

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top