使用ASP.NET和MVC 3,如何创建隐藏字段,以便具有数组的List作为列表中每个项的值正确绑定?

我有一个查询语句列表,当需要将另一个语句添加到最后时,需要将其发送回MVC控制器。 对于我正在尝试立即创建的测试,页面以filter列表开头。 在执行此示例时,页面将为filter创建字段,如下所示:

   

但是当我看到表单实际发回给控制器时,列表会返回:

 PropertyName = "State" Operator = "=" Value= "new string[1]" // The value comes back in the first index of the array 

我必须将Value参数转换为数组并获取第一个索引才能获得该值。 这没关系,但并不理想。 当FilterField包含Value属性的整数或字符串数​​组时,会出现主要问题。 当发生这种情况时,HTML会出现:

    

然后,该值包含对象类型,根本没有值。 因此,模型绑定器会变得混乱,一切都会中断。 有没有更简单的方法将这个值列表绑定到View?

FilterField.cs:

 public class FilterField { public string PropertyName { get; set; } public string Operator { get; set; } public object Value { get; set; } public FilterOutput GetOutput() { return new FilterOutput(PropertyName, Operator, Value); } } 

FilterOutput.cs

 public class FilterOutput { private List _properties = new List(); private string _propertyName; private string _op; private object _value; public string Sql { get; set; } public QueryProperty[] Properties { get; set; } public FilterOutput(string propertyName, string op, object value) { var sql = "[{0}] {1} {2}"; Sql = string.Format(sql, propertyName, op, FormatParameter(propertyName, op, value)); Properties = _properties.ToArray(); } private string FormatParameter(string propertyName, string op, object value) { _properties.Clear(); var sb = new StringBuilder(); switch (op.ToUpper()) { case "IN": { var values = value as Array; sb.Append("{"); var inCount = 0; foreach (var v in values) { var pName = propertyName + inCount; if (inCount == 0) sb.Append("@" + pName); else sb.Append(",@" + pName); _properties.Add(new QueryProperty { Name = pName, Value = v }); inCount++; } sb.Append("}"); } break; case "LIKE": if (value.ToString().Contains("_")) sb.Append("@" + propertyName); else sb.Append("'%' + @" + propertyName + " + '%'"); _properties.Add(new QueryProperty { Name = propertyName, Value = value }); break; case "BETWEEN": var range = value as Array; var betweenCount = 0; foreach (var r in range) { if (betweenCount > 0) sb.Append(" AND "); sb.Append("@" + propertyName + betweenCount); _properties.Add(new QueryProperty { Name = propertyName + betweenCount, Value = r }); betweenCount++; } break; default: sb.Append("@" + propertyName); _properties.Add(new QueryProperty { Name = propertyName, Value = value }); break; } return sb.ToString(); } public string ConvertToSql() { var filterOutput = this; var output = filterOutput.Properties.Aggregate(filterOutput.Sql, (current, p) => current.Replace("@" + p.Name, FormatObjectToString(p.Value))); output = output .Replace("[", "t.").Replace("]", "") // Convert [text] to t.text .Replace("{", "(").Replace("}", ")") // Convert {'text1','text2'} to ('text1','text2') .Replace("'%' + '", "'%").Replace("' + '%'", "%'"); // Convert '%' + text + '%' to '%text%' return " AND " + output; } public override string ToString() { var filterOutput = this; return filterOutput.Properties.Aggregate(filterOutput.Sql, (current, p) => current.Replace("@" + p.Name, FormatObjectToString(p.Value)).Replace("'%' + '", "'%").Replace("' + '%'", "%'")); } private string FormatObjectToString(object value) { if (value is int) return value.ToString(); return String.Format("'{0}'", value); } } 

HomeController.cs

 public ActionResult TestQuery(DateTime date) { var builder = new QueryBuilder(_repo, "INFO", date); builder.AddFilters( new FilterField { PropertyName = "State", Operator = "=", Value = "CA" }, new FilterField { PropertyName = "Schedule_Num", Operator = "IN", Value = new[] {2, 6} }); var result = builder.Build(); return View(result); } [HttpPost] public ActionResult TestPost(QueryResult result) { var builder = new QueryBuilder(_repo, "INFO", date); foreach (var f in result.Filters) { builder.AddFilters(new FilterField { PropertyName = f.PropertyName, Operator = f.Operator, Value = ((Array)f.Value).GetValue(0) }); } builder.AddFilters( new FilterField { PropertyName = "Gender", Operator = "BETWEEN", Value = new[] {"A", "G"} }); var newResult = builder.Build(); return View("TestQuery", newResult); } 

TestQuery.cshtml

 @model Models.QueryResult @using (Html.BeginForm("TestPost", "Home")) { @Html.HiddenFor(m => m.Date) for (var i = 0; i < Model.Filters.Count(); i++) { @Html.Hidden("filters[" + i + "].PropertyName", Model.Filters[i].PropertyName) @Html.Hidden("filters[" + i + "].Operator", Model.Filters[i].Operator) @Html.Hidden("filters[" + i + "].Value", Model.Filters[i].Value) } 

}

QueryResult.cs

 public class QueryResult { public DateTime Date { get; set; } public ObjectQuery Objects { get; set; } public string SqlStatement { get; set; } public ObjectParameter[] Parameters { get; set; } public AdjustResult AdjustResult { get; set; } public IList Filters { get; set; } public QueryResult() { Filters = new List(); } public void AddFilter(FilterField filter) { Filters.Add(filter); } public string ParsedSqlStatement() { var output = Parameters.Aggregate(SqlStatement, (current, p) => current.Replace("@" + p.Name, FormatObjectToString(p.Value))); return Filters.Aggregate(output, (current, filter) => current + filter.ConvertToSql()); } private string FormatObjectToString(object value) { if (value is int) return value.ToString(); return String.Format("'{0}'", value); } } 

QueryBuilder.cs

 public class QueryBuilder { public IList Filters { get; set; } private IDynamicRepository _repo; private string _tablePrefix; private DateTime _date; private QueryResult _base; public QueryBuilder(IDynamicRepository repository, string tablePrefix, DateTime date) { _repo = repository; _tablePrefix = tablePrefix; _date = date; _base = _repo.GetAll(tablePrefix, date); Filters = new List(); } public void AddFilters(params FilterField[] filters) { foreach (var f in filters) { Filters.Add(f); } } public void RemoveFilter(FilterField filter) { Filters.Remove(filter); } public QueryResult Build() { return _base.Where(Filters.ToArray()); } } 

Extensions.cs

 public static QueryResult Where(this QueryResult result, string predicate, params QueryProperty[] properties) { result.Objects = result.Objects.Where(predicate.ReplaceIdentifier(), properties.Select(p => new ObjectParameter(p.Name, p.Value)).ToArray()); return result; } public static QueryResult Where(this QueryResult result, FilterField filter) { var filterOutput = filter.GetOutput(); result.Objects = result.Objects.Where(filterOutput.Sql.ReplaceIdentifier(), filterOutput.Properties.Select(p => new ObjectParameter(p.Name, p.Value)).ToArray()); result.AddFilter(filter); return result; } public static QueryResult Where(this QueryResult result, params FilterField[] filters) { return filters.Aggregate(result, Where); } 

由于你们中的一些人想要了解更多信息,这里有一些关于一切如何联系在一起的更多细节。 基本上,控制器从UI获取一个filter列表,该列表在WHERE之后归结为SQL语句。 因此,一个filter将变为FIELD = VALUE或FIELD IN(VALUE1,VALUE2)。 querybuilder使用Entity.CreateQuery(“SELECT * FROM TABLE”)创建SQL语句的基础。 一旦运行了querybuilder上的方法Build(),它就会创建一个QueryResult模型,该模型具有查询的日期,并且带有附加filter的查询的所有EntityObjects都转换为WHERE语句以供View使用。 我继续前进并增加了一些课程来展示这些课程的结合方式。

在您的视图中,您不需要像这样设置名称,因为您可以使用HiddenFor为您执行此操作。 将视图中的for循环更改为:

 for (var i = 0; i < Model.Filters.Count(); i++) { @Html.HiddenFor(m => m.Filters[i].PropertyName) @Html.HiddenFor(m => m.Filters[i].Operator) @Html.HiddenFor(m => m.Filters[i].Value) } 

这应该给你正确的标记,这反过来应该有助于默认模型绑定器将filter发送到HttpPost方法中的QueryResult :)。

**编辑:当你绑定多个值或单个值( intstring ,也可能是一个Array )时,你最好将FilterField类中的Value属性更改为List

所以,在FilterField类中替换为:

 public object Value { get; set; } 

有了这个:

 public List Values { get; set; } 

然后将您的标记更改为:

 for (var i = 0; i < Model.Filters.Count(); i++) { @Html.HiddenFor(m => m.Filters[i].PropertyName) @Html.HiddenFor(m => m.Filters[i].Operator) for (var j = 0; j < Model.Filters[i].Values.Count; j++) { @Html.HiddenFor(m => m.Filters[i].Values[j]) } } 

这样,虽然它可能看起来不像世界上最漂亮的代码片段,但它可以省去你不得不破解模型绑定以按照你想要的方式工作的麻烦,它只是默认绑定。

我想你可以从这篇文章中改编这个想法。

您案例中的每个项目都是FilterItems而不是GiftItems ……

默认情况下,具有相同名称的多个输入将绑定到数组。 因此,您需要离散地迭代每个值并将其添加为隐藏输入(具有相同名称)。 当它回发时,它将作为数组绑定。

迭代每个值也应该消除将对象类型设置为值的问题。