ASP.NET MVC : 뷰 모델이 수집/목록/ienumerable 일 때 텍스트 상자 상태를 유지하는 방법
-
18-09-2019 - |
문제
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을 지나갈 때 이것을 작동시킬 수 없습니다. 내 생각에 나는 모델을 실행하고 목록의 각 사람에 대한 이름과 이메일에 대한 텍스트 상자를 생성하려고합니다. 양식을 잘 생성 할 수 있으며 값으로 양식을 제출하고 2 단계로 이동할 수 있습니다. 그러나 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 <>와 같은 상속자입니다.
문제는 ViewDataDictionary 클래스에 있습니다. 특히 GetPropertyValue 메소드는 PropertyDescriptor.getValue 메소드를 사용하여 값을 끌어내어 사전 속성 (getIndexEdPropertyValue를 호출 함) 또는 간단한 속성에서 속성 값을 검색하는 방법 만 제공합니다.
이 문제를 해결하기 위해 컬렉션 인 모델 (및 중첩 된 컬렉션이 포함 된 모델)을 처리하는 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
};
}
다시 말하지만, 이것은 ASP.NET MVC 2 RC의 ViewDatAdictionary.cs 파일에 있습니다. MVC CodePlex 사이트에서이를 추적 할 새 문제를 만들어야합니까?