近日使用asp.net mvc4做一个提交提交viewmodel集合对象的例子,发现viewmodel如果继承了ICollection<T>接口,post提交是action中该viewmodel对象的实例就为空,不知道为什么。请大神们指点。以下是我的代码:
1、首先看一下我的html代码,该页面使用的是TestModel对象。
@model ShoppingMall.Admin.Models.Product.TestModel @{ ViewBag.Title = "Test"; Layout = "~/Views/Shared/_Layout.cshtml"; } @using (Html.BeginForm("Test", "Product", FormMethod.Post)) { <div> 名称 : @Html.TextBoxFor(x=>x.TestName) <input type="submit" value="搜索" /> </div> }
2、再看一下定义的TestModel对象
using System; using System.Collections.Generic; using System.Linq; using System.Web; namespace ShoppingMall.Admin.Models.Product { public class TestModel : BaseListModel2<ItemTestModel> { public string TestName { get; set; } } public class ItemTestModel { } }
基类BaseListModel2的定义如下,该对象继承了ICollection<T>
using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Web; namespace ShoppingMall.Admin.Models { public class BaseListModel2<T> : ICollection<T> { /// <summary> /// 构造方法 /// </summary> public BaseListModel2() { } #region Count /// <summary> /// 获取元素的数量 /// </summary> public virtual Int32 Count { get { return this.Children.Count; } } #endregion #region Children /// <summary> /// 获取元素的集合 /// </summary> public virtual List<T> Children { get; private set; } #endregion #region Add /// <summary> /// 将对象添加到集合的结尾处。 /// </summary> /// <param name="item">要添加到集合的末尾处的对象</param> public virtual void Add(T item) { this.Children.Add(item); } #endregion #region Remove /// <summary> /// 从集合中移除特定对象的第一个匹配项。 /// </summary> /// <param name="item">要从集合中移除的对象</param> /// <returns>如果成功移除 item,则为 true;否则为 false。如果在集合中没有找到item,该方法也会返回 false。</returns> public virtual Boolean Remove(T item) { return this.Children.Remove(item); } #endregion #region RemoveAt /// <summary> /// 移除集合的指定索引处的元素。 /// </summary> /// <param name="index">要移除的元素的从零开始的索引</param> public virtual void RemoveAt(Int32 index) { this.Children.RemoveAt(index); } #endregion /// <summary> /// /// </summary> /// <param name="item"></param> /// <returns></returns> public int IndexOf(T item) { return this.Children.IndexOf(item); } /// <summary> /// /// </summary> /// <param name="index"></param> /// <param name="item"></param> public void Insert(Int32 index, T item) { this.Children.Insert(index, item); } /// <summary> /// /// </summary> public void Clear() { this.Children.Clear(); } /// <summary> /// /// </summary> /// <param name="item"></param> /// <returns></returns> public Boolean Contains(T item) { return this.Children.Contains(item); } /// <summary> /// /// </summary> /// <param name="array"></param> /// <param name="arrayIndex"></param> public void CopyTo(T[] array, Int32 arrayIndex) { this.Children.CopyTo(array, arrayIndex); } /// <summary> /// /// </summary> public Boolean IsReadOnly { get { return ((IList)this.Children).IsReadOnly; } } /// <summary> /// /// </summary> /// <returns></returns> public IEnumerator<T> GetEnumerator() { return this.Children.GetEnumerator(); } /// <summary> /// /// </summary> /// <returns></returns> IEnumerator IEnumerable.GetEnumerator() { return this.Children.GetEnumerator(); } } }
3、定义的controller中的action如下:
[HttpGet] public ActionResult Test() { return View(); } [HttpPost] public ActionResult Test(TestModel model) { return RedirectToAction("Test"); }
4、经过调试问题出来了,每次post提交数据model实例都是null,但是如果BaseListModel2<T>不继承 ICollection<T>时model实例是有数据的,请大神们指点是有什么引起的。
首先,你的BaseListModel2<T>类应该说是【实现】而非继承ICollection<T>接口。
此外,你碰到的这个问题应该是可枚举接口IEnumerable的实现导致的。具体说就是,每次post提交数据model实例,实际上这有一个过程是,根据提交的数据进行参数类型的实例化及成员赋值的过程,所以我估计【关键问题】在于你把参数类TestModel的Children属性的set访问器设置成了private,导致Children永远为null。而当你的参数类TestModel继承了可枚举接口IEnumerable,那么它的实例化过程可能会处理可枚举接口的一些特殊处理,例如会调用GetEnumerator()或者Insert()等方法,而你的这些方法实现中都引用了“永远为空”的Children属性,所以可能导致不可预测的问题。
ps.这是我的思考,具体是不是这样导致的,因为现在微软已经将.net库开源啦,所以你可以去看有关源码一探究竟。Good Luck!