为了验证实体对象的属性值是否合法,其实就是验证各字段的MaxLength,Range,Required等属性标签,我在Model父类中写了这个方法,代码如下,
/// <summary> /// 验证实体的数据是否符合要求 /// </summary> /// <param name="p">被验证的实体</param> /// <returns>符合返回true</returns> public static bool IsValid(this Parent p) { try { var context = new ValidationContext(p, null, null); var results = new List<ValidationResult>(); TypeDescriptor.AddProviderTransparent(new AssociatedMetadataTypeTypeDescriptionProvider(p.GetType()), p.GetType()); bool isValid = Validator.TryValidateObject(p, context, results, true); return isValid; } finally { GC.Collect(); } }
我还特意加了个finally,在每次调用完成都强制垃圾回收..
可是调用这个方法次数一多,就报下边这个异常了,大约在9030到9050次之间就会出这个异常..
我的测试代码如下:
Item_Prop是Parent的子类.
你可能会问,干嘛要调用这么多次...
好吧,其实我在BaseDAO里写了一个TransInsert(List<Parent> list){}的方法,
这个方法用于向数据库中批量插入一批实体数据.
在foreach list时,对每个item进行IsValid()验证...于是,list项一多就出现上边的异常了..
同一个list中,item超9030之后就会出现这个异常了,
还有一种情况是,我有多个list..
list<SubClass1> listA
list<SubClass2>listB
list<SubClassN>listN
即使每个list中的项不到9000,只有100到500项,分别将这多个(N个)list插入时,也会出现那个异常...
我总结了一下,就是IsValid()方法调用次数一多,就会报那个异常...
请问这个方法为什么会导致这个异常?怎么解决?谢谢!
开始我还以为是插入到数据库中的时候出了问题呢..后来将这句注释掉之后就正常了.
Parent,Item_Cat的类结构如下 :
Parent
using System; using System.Collections; using SoEasy.Common; using System.Data; namespace SoEasy.Model.BaseEntity { /// <summary> /// 实体父类 /// </summary> [Serializable] public abstract class Parent { /// <summary> /// 保存所有字段与属性的映射关系 /// </summary> private IList lst = new ArrayList(); /// <summary> /// 是否存在某字段映射关系 /// 返回:true:存在,false:不存在 /// </summary> /// <param name="fieldName">字段名称</param> private bool IsExistsField(string fieldName) { bool flag = false; for (int i = 0; i < lst.Count; i++) { Hashtable ht = lst[i] as Hashtable; if (ht != null && ht[Constant.STR_KEY].Equals(fieldName)) { flag = true; break; } } return flag; } /// <summary> /// 设定字段与属性的映射关系 /// </summary> /// <param name="fieldName">字段名</param> /// <param name="value">字段值</param> protected void SetFieldMapping(string fieldName, object value) { if (!IsExistsField(fieldName)) { Hashtable ht = new Hashtable(); ht.Add(Constant.STR_KEY, fieldName); ht.Add(Constant.STR_VALUE, value); lst.Add(ht); } } /// <summary> /// 计算已设定值的字段数 /// </summary> /// <returns></returns> public int CountFields() { return lst.Count; } /// <summary> /// 得到指定位置的列名 /// </summary> /// <param name="index">保存列的索引</param> /// <returns></returns> public string ColumnName(int index) { string name = ""; if (lst.Count > index) { Hashtable ht = lst[index] as Hashtable; if (ht != null) { name = ht[Constant.STR_KEY].ToString(); } } return name; } /// <summary> /// 得到指定某列处保存的值 /// </summary> /// <param name="index">保存列的索引</param> /// <returns></returns> public object ColumnValue(int index) { object obj = null; if (lst.Count > index) { Hashtable ht = lst[index] as Hashtable; if (ht != null) { obj = ht[Constant.STR_VALUE]; } } return obj; } /// <summary> /// 非=号的查询限制条件 /// </summary> public NotEqualCondition OtherCondition { get; set; } /// <summary> /// 获取GUID /// </summary> /// <param name="guid"></param> /// <returns></returns> public string GetGuid() { return Guid.NewGuid().ToString(); } /// <summary> /// 映射表名及主键字段信息 /// 子类必需实现该方法 /// </summary> /// <returns></returns> public abstract Hashtable MappingTableInfo(); /// <summary> /// 将DataTable中的首行数据转成对应的实体对象 /// </summary> /// <param name="dt">DataTable</param> /// <returns>返回实体对象,null表示转换失败</returns> abstract public Parent GetModelFromDataTable(DataTable dt); } }
Item_Cat 类
using System; using System.Collections; using System.ComponentModel.DataAnnotations; using SoEasy.Model.BaseEntity; using SoEasy.Common; using System.Data; namespace Model { /// <summary> /// 分类表 /// </summary> [Serializable] public class Item_Cat : Parent { public override System.Collections.Hashtable MappingTableInfo() { Hashtable ht = new Hashtable(); ht.Add(Constant.STR_DB_TABLE, "item_cat"); ht.Add(Constant.STR_DB_PK, "CID"); return ht; } public override Parent GetModelFromDataTable(DataTable dt) { Item_Cat x = null; if (dt != null && dt.Rows.Count > 0) { x = new Item_Cat(); DataRow dr = dt.Rows[0]; x.Cid = long.Parse(dr["Cid"].ToString()); x.Name = dr["Name"].ToString(); x.Parentcid = long.Parse(dr["Parentcid"].ToString()); x.Isparent = int.Parse(dr["Isparent"].ToString()); x.Sort_Num = long.Parse(dr["Sort_Num"].ToString()); x.Is_Enable = int.Parse(dr["Is_Enable"].ToString()); x.Creater_Id = dr["Creater_Id"].ToString(); x.Create_Time = DateTime.Parse(dr["Create_Time"].ToString()); x.Updater_Id = dr["Updater_Id"].ToString(); x.Update_Time = DateTime.Parse(dr["Update_Time"].ToString()); } return x; } long cid; /// <summary> ///分类ID /// </summary> public long Cid { get { return cid; } set { cid = value; SetFieldMapping("Cid", value); } } string name; /// <summary> ///分类名称 /// </summary> [MaxLength(100)] public string Name { get { return name; } set { name = value; SetFieldMapping("Name", value); } } long parentcid; /// <summary> ///父分类ID /// </summary> public long Parentcid { get { return parentcid; } set { parentcid = value; SetFieldMapping("Parentcid", value); } } int isparent; /// <summary> ///是否是父分类 /// </summary> public int Isparent { get { return isparent; } set { isparent = value; SetFieldMapping("Isparent", value); } } long sort_num; /// <summary> ///排序号 /// </summary> public long Sort_Num { get { return sort_num; } set { sort_num = value; SetFieldMapping("Sort_Num", value); } } int is_enable; /// <summary> ///是否启用 /// </summary> public int Is_Enable { get { return is_enable; } set { is_enable = value; SetFieldMapping("Is_Enable", value); } } string creater_id; /// <summary> /// /// </summary> [MaxLength(36)] public string Creater_Id { get { return creater_id; } set { creater_id = value; SetFieldMapping("Creater_Id", value); } } DateTime create_time; /// <summary> /// /// </summary> public DateTime Create_Time { get { return create_time; } set { create_time = value; SetFieldMapping("Create_Time", value); } } string updater_id; /// <summary> /// /// </summary> [MaxLength(36)] public string Updater_Id { get { return updater_id; } set { updater_id = value; SetFieldMapping("Updater_Id", value); } } DateTime update_time; /// <summary> /// /// </summary> public DateTime Update_Time { get { return update_time; } set { update_time = value; SetFieldMapping("Update_Time", value); } } } }
测试方法:
static void InsertTest() { List<Parent> ll = new List<Parent>(); Item_Prop c = new Item_Prop(); c.Cid = 23; for (int i = 0; i <10000; i++) { c.IsValid(); // ll.Add(c); } //bl.TransInsert(ll); }
扩展类:
using SoEasy.Common; using SoEasy.Model.BaseEntity; using System; using System.Collections.Generic; using System.ComponentModel; using System.ComponentModel.DataAnnotations; using System.Data; namespace SoEasy.Model.BaseEntity { public static class ModelExtension { /// <summary> /// 将实体类转成表(实体类的属性要赋值后才能转) /// </summary> /// <param name="p"></param> /// <returns></returns> public static DataTable ToDataTable(this Parent p) { DataTable dt = new DataTable(); for (int i = 0; i < p.CountFields(); i++) { dt.Columns.Add(new DataColumn(p.ColumnName(i), p.ColumnValue(i).GetType())); } DataRow dr = dt.NewRow(); for (int i = 0; i < p.CountFields(); i++) { dr[i] = p.ColumnValue(i); } dt.Rows.Add(dr); return dt; } /// <summary> /// 验证实体的数据是否符合要求 /// </summary> /// <param name="p">被验证的实体</param> /// <returns>符合返回true</returns> public static bool IsValid(this Parent p) { try { var context = new ValidationContext(p, null, null); var results = new List<ValidationResult>(); TypeDescriptor.AddProviderTransparent(new AssociatedMetadataTypeTypeDescriptionProvider(p.GetType()), p.GetType()); bool isValid = Validator.TryValidateObject(p, context, results, true); return isValid; } finally { } } /// <summary> /// 将DataTable转换成T类型的实体对象 /// </summary> /// <typeparam name="T">T类必须继承自Parent类</typeparam> /// <param name="dt">DataTable数据源</param> /// <param name="throwException">出现异常时是否抛出</param> /// <returns>T类实体对象,null表示转换失败</returns> public static T GetTModel<T>(this DataTable dt, bool throwException = false) where T : Parent, new() { return ExceptionHelper.ExceptionRecord(() => { T t = null; try { t = new T(); t = (T)t.GetModelFromDataTable(dt); } catch (Exception ex) { t = null; throw new Exception("将DataTable转换成实体类时发现异常:" + ex.Message, ex); } return t; }, throwException); } } }
整体就这样了.
先把 GC.Collect(); 去掉试试
试过了,开始就没加GC.Collect();的.
后来发现加了没用,也删掉了.
还是没解决..麻烦再帮我想想吧
@hexllo: 问题可能出在你是针对同一个Item_Prop实例进行大量的调用,你可以将创建Item_Prop实例的代码放在循环中试试。
@dudu: 试过了,我向一个List中Add 1000000个 new Item_Cat都没有报错.
@hexllo: 提问中的stack overflow是一种正常情况。如果你想一探究竟,需要了解一下方法调用时的call stack机制(推荐阅读:函数调用过程探究)以及.NET CLR中的stack walking原理(推荐阅读:Stackwalking in the CLR)。
@dudu: 看了一下,还是不明白为什么这个方法会出异常..而其它方法重复调用不会,这个方法里的代码有什么特殊之处吗?
在我印象中只有无限递归时才会出现StackOverflow的异常啊...
@hexllo: 可以将这个方法中的代码一行一行注释进行测试,看是不是某行代码引起的?
@hexllo: 另外,可以试试在 GC.Collect(); 之后加上下面的代码:
GC.WaitForPendingFinalizers();
@dudu: 您说对了!!!
是这行看起来很复杂的代码引起的:
TypeDescriptor.AddProviderTransparent(new AssociatedMetadataTypeTypeDescriptionProvider(p.GetType()), p.GetType());
不过不知道为什么它会引发StackOverflow,总之,很感谢!
古人说过一个名言:
What Andy gives, Bill takes away。
真是有道理。不过改成
What Andy gives, Programmer takes away。
应该更准确一些。
哎…… 肯定 是注释掉这一行代码有问题嘛 ,但是你只上传了极小的一部分代码, 只字片言,断章取义,我们是想破脑袋也不知道你的代码哪里有问题啊?
呵呵,提供的就是出现问题的代码呀,
放一大堆上来反而不好找.
我非常确定是IsValid()方法的问题.
不调用它就不出异常了..
@hexllo: 关键是你上传的方法,还有好几个类啊?这几个类是什么意思?怎么定义的?你不上传我们不知道啊?
@田麦成: 嗯,我把这些类都放上去了,麻烦帮我看一下吧,谢谢了