ASP.NET MVC: كيفية الحفاظ على حالة TextBox عندما تكون ViewModel هي مجموعة / قائمة / iEnumerable

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

سؤال

أنا أستخدم ASP.NET MVC 2 بيتا. يمكنني إنشاء معالج مثل سير العمل باستخدام تقنية Steven Sanderson (في إطار كتابه Pro ASP.NET MVC) باستثناء استخدام الجلسة بدلا من حقول النماذج المخفية للحفاظ على البيانات عبر الطلبات. يمكنني العودة إلى ذهابا وإيابا بين الصفحات والحفاظ على القيم في مربع نص دون أي مشكلة عندما لا يكون نموذجي مجموعة. مثال على ذلك سيكون نموذج شخص بسيط:

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

لكنني غير قادر على الحصول على هذا للعمل عندما أترك iEnumerable. في رأيي، أحاول تشغيل النموذج وإنشاء مربع نص عن الاسم والبريد الإلكتروني لكل شخص في القائمة. يمكنني توليد النموذج غرامة وأستطيع إرسال النموذج مع قيمي وانتقل إلى Step2. ولكن عندما أقوم بالنقر فوق الزر "رجوع" في Step2، فهذا يعيدني إلى Step1 بنموذج فارغ. لا شيء من الحقول التي كنت قديمة سابقا هناك. يجب أن يكون هناك شيء أفتقده. يمكن لشخص مساعدتي؟

هنا وجهة نظري:

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

وهنا جهاز تحكم الخاص بي:

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);
    }
}
هل كانت مفيدة؟

المحلول

بقدر ما أستطيع أن أقول، هذا غير مدعوم في ASP.NET MVC 2 بيتا، ولا هو مدعوم في ASP.NET MVC 2 RC. لقد حفرت من خلال التعليمات البرمجية المصدر MVC ويبدو أن القواميس مدعومة ولكن لا توجد نماذج iEnumerable <> (أو تحتوي على كائنات iEnumerable المتداخل) وواقعها مثل Ilist <>>.

المشكلة في فئة ViewDatAdAdiAdiActary. خاصة، توفر طريقة GetPropertyValue طريقة لاسترداد قيم الممتلكات من خصائص القاموس (عن طريق استدعاء GetindexedPropertyValue) أو الخصائص البسيطة باستخدام طريقة PropertyDescriptor.getValue لسحب القيمة.

لإصلاح ذلك، قمت بإنشاء طريقة GetCollectionPropertyValue التي تعالج النماذج التي تعد مجموعات (وحتى النماذج التي تحتوي على مجموعات متداخلة). أنا جعل الرمز هنا للإشارة. ملاحظة: لا أقدم أي مطالبات حول الأناقة - في الواقع، فإن جميع تحليل السلسلة قبيحة للغاية، ولكن يبدو أنها تعمل. هنا الطريقة:

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

وهنا طريقة GetPropertyValue المعدلة التي تستدعي الطريقة الجديدة:

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

مرة أخرى، هذا في ملف ViewDatadaDictionary.cs في ASP.NET MVC 2 RC. هل يجب علي إنشاء مشكلة جديدة لتتبع ذلك على موقع Codeplex MVC؟

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top