ASP.NET MVC:如何维护国家的TextBox当你的视图模型是一家集/列表/ IEnumerable的
-
18-09-2019 - |
题
我使用ASP.NET MVC 2贝塔。但是使用而不是隐藏的表单字段会话保持跨请求的数据,我可以创建一个使用史蒂芬·桑德森的技术像工作流程向导(在他的书临ASP.NET MVC框架)。我可以去来回页之间,并在TextBox保持值没有任何问题,当我的模型不是一个集合。一个例子是一个简单的Person模型:
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
public string Email { get; set; }
}
但我无法得到这个,当我绕过一个IEnumerable工作。在我看来,我试图通过示范运行,并为姓名和电子邮件文本框列表中的每个人。我可以生成的形式很好,我可以用我的价值观提交表单,然后转到步骤2。但是,当我点击第二步后退按钮,它把我带回到第一步一个空的形式。我以前填充字段均存在。一定有什么东西我失踪。有人可以帮我吗?
下面是我的看法:
<% 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 Beta版的支持,也不是在ASP.NET MVC 2 RC的支持。我通过MVC源代码,挖它看起来是的IEnumerable <>(或包含嵌套的IEnumerable对象)像字典,但不支持模型,它的传承人喜欢的IList <>。
的问题是在的ViewDataDictionary类。特别地,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
};
}
再次,这是在ASP.NET MVC 2 RC的ViewDataDictionary.cs文件。我应该创建一个新的问题跟踪此对MVC的CodePlex网站?