/// <summary> /// 从POCO实体类获取跟当前实体类的属性名称相同的属性的值,拷贝到当前实体类中,完成数据的映射。 /// 要求拷贝的同名属性是读写属性且类型相同。 /// </summary> /// <param name="pocoClass">POCO实体类,提供源数据</param> /// <param name="isChange">是否改变属性的修改状态</param> /// <returns>映射成功的属性数量</returns> public int MapFrom(object pocoClass, bool isChange) { if (pocoClass == null) { return(0); } int count = 0; int fcount = this.PropertyNames.Length; INamedMemberAccessor[] accessors = new INamedMemberAccessor[fcount]; DelegatedReflectionMemberAccessor drm = new DelegatedReflectionMemberAccessor(); Type type = pocoClass.GetType(); var ef = EntityFieldsCache.Item(this.GetType()); for (int i = 0; i < fcount; i++) { //实体类的属性字段可能跟属性名称不一样 edit at 2015.2.11 string perpertyName = ef.GetPropertyName(PropertyNames[i]); accessors[i] = drm.TryFindAccessor(type, perpertyName); } for (int i = 0; i < fcount; i++) { if (accessors[i] != null) { this.PropertyValues[i] = accessors[i].GetValue(pocoClass); if (isChange) //设置属性修改状态 { this.changedlist[i] = true; } count++; } } return(count); }
/// <summary> /// 根据实体类属性的索引号,获取实体类属性值的索引 /// </summary> /// <param name="propertyIndex">实体类属性的索引号</param> /// <returns></returns> private int getPropertyValueIndex(int propertyIndex) { if (propertyIndex < 0) { throw new Exception("当前实体类属性读取方法指定的 属性索引 值不能小于0 "); } if (propertyValueIndex == null) { //下面一行代码会阻塞多余的线程 EntityFields ef = EntityFieldsCache.Item(this.GetType()); int[] tempArr = new int[ef.Fields.Length]; for (int i = 0; i < tempArr.Length; i++) { tempArr[i] = -1; } if (propertyValueIndex == null) { propertyValueIndex = tempArr; } } if (propertyIndex > propertyValueIndex.Length) { throw new Exception("当前实体类属性读取方法指定的 属性索引 值(" + propertyIndex + ")大于可用的实体类属性数量"); } return(propertyValueIndex[propertyIndex]); }
/// <summary> /// 触发属性改变事件 /// </summary> /// <param name="propertyFieldName">属性改变事件对象</param> protected virtual void OnPropertyChanged(string propertyFieldName) { if (this.PropertyChanged != null) { string currPropName = EntityFieldsCache.Item(this.GetType()).GetPropertyName(propertyFieldName); this.PropertyChanged(this, new PropertyChangedEventArgs(currPropName)); } }
/// <summary> /// 设置实体类的对应的字段名称数组 /// 新版本子类可以实现这个细节,否则框架将反射获得该信息(该特性有利于简化手写的代码)。 /// <remarks>为了兼容性,这里不作为抽象方法</remarks> /// </summary> protected internal virtual void SetFieldNames() { //this.names = names; //如果子类未重写该方法,调用框架的数据库元数据获取方法 EntityFields ef = EntityFieldsCache.Item(this.GetType()); this.names = ef.Fields; }
/// <summary> /// 反序列化实体类 /// </summary> /// <param name="buffer">要反序列化的数据源</param> /// <param name="factEntityType">实体类的实际类型</param> /// <returns>实体类实例</returns> public static object BinaryDeserialize(byte[] buffer, Type factEntityType) { MemoryStream ms2 = new MemoryStream(buffer); BinaryReader br = new BinaryReader(ms2); EntityFields ef = EntityFieldsCache.Item(factEntityType); EntityBase entity = (EntityBase)System.Activator.CreateInstance(factEntityType); BinaryDeserializeCommon(br, ef, entity); br.Close(); ms2.Close(); return(entity); }
/// <summary> /// 获取所有字段的描述,跟字段名一一对应 /// </summary> /// <returns></returns> public string[] GetFieldDescriptions() { string[] desc = this.SetFieldDescriptions(); if (desc == null) { EntityFields ef = EntityFieldsCache.Item(this.GetType()); desc = ef.FieldDescriptions; if (desc == null) { throw new NotImplementedException("实体类没有重写实现 SetFieldDescriptions 方法。"); } } return(desc); }
/// <summary> /// 获取属性的值,如果没有,将返回属性类型的默认值;注意该方法调用将发起属性获取事件 。 /// </summary> /// <param name="propertyName"></param> /// <returns></returns> public object GetPropertyValueWithEvent(string propertyName) { EntityFields ef = EntityFieldsCache.Item(this.GetType()); string fieldName = ef.GetPropertyField(propertyName); if (fieldName != null) { this.OnPropertyGeting(fieldName); object result = PropertyList(fieldName); if (result == null || result == DBNull.Value) { Type propType = ef.GetPropertyType(fieldName); result = Activator.CreateInstance(propType);//活动类型的默认值 } return(result); } return(new ArgumentOutOfRangeException("不存在指定的属性名:" + propertyName)); }
/// <summary> /// 获取或者设置指定属性名称的值,属性名必须是一个PDF.NET实体类的属性(调用了getProperty 和 setProperty方法),不能是普通属性。 /// 如果属性不存在,获取该属性值将为null,而设置该属性值将抛出异常。 /// </summary> /// <param name="propertyName">属性名称</param> /// <returns></returns> public object this[string propertyName] { get { EntityFields ef = EntityFieldsCache.Item(this.GetType()); string fieldName = ef.GetPropertyField(propertyName); if (fieldName != null) { this.OnPropertyGeting(fieldName); return(PropertyList(fieldName)); } //获取虚拟的字段值 return(PropertyList(propertyName));; } set { EntityFields ef = EntityFieldsCache.Item(this.GetType()); string fieldName = ef.GetPropertyField(propertyName); if (fieldName != null) { //如果是实体类基础定义的字段,必须检查设置的值得类型 //2017.5.5 增加类型相容转换处理,包括空字符串,可用于大批量文本数据导入情况 Type fieldType = ef.GetPropertyType(fieldName); try { object Value = CommonUtil.ChangeType(value, fieldType); this.setProperty(fieldName, Value); } catch (Exception ex) { throw new ArgumentException("实体类的属性字段" + propertyName + " 需要" + fieldType.Name + " 类型的值,但准备赋予的值不是该类型!", ex); } } else { //设置虚拟的字段值 this.setProperty(propertyName, value); } } }
/// <summary> /// 获取指定字段名对应的描述 /// </summary> /// <param name="fieldName">字段名</param> /// <returns>字段描述</returns> public string GetFieldDescription(string fieldName) { int index = -1; EntityFields ef = EntityFieldsCache.Item(this.GetType()); for (int i = 0; i < ef.Fields.Length; i++) { string s = ef.Fields[i]; if (s == fieldName) { index = i; break; } } if (index == -1) { throw new ArgumentOutOfRangeException(fieldName + " 不在实体类定义的字段范围内。"); } return(GetFieldDescriptions()[index]); }
/// <summary> /// 获取或者设置指定属性名称的值,属性名必须是一个PDF.NET实体类的属性(调用了getProperty 和 setProperty方法),不能是普通属性。 /// 如果属性不存在,获取该属性值将为null,而设置该属性值将抛出异常。 /// </summary> /// <param name="propertyName">属性名称</param> /// <returns></returns> public object this[string propertyName] { get { EntityFields ef = EntityFieldsCache.Item(this.GetType()); string fieldName = ef.GetPropertyField(propertyName); if (fieldName != null) { this.OnPropertyGeting(fieldName); return(PropertyList(fieldName)); } //获取虚拟的字段值 return(PropertyList(propertyName));; } set { EntityFields ef = EntityFieldsCache.Item(this.GetType()); string fieldName = ef.GetPropertyField(propertyName); if (fieldName != null) { //如果是实体类基础定义的字段,必须检查设置的值得类型 if (value != null) { Type fieldType = ef.GetPropertyType(fieldName); if (value.GetType() != fieldType) { throw new ArgumentException("实体类的属性字段" + propertyName + " 需要" + fieldType.Name + " 类型的值,但准备赋予的值不是该类型!"); } } this.setProperty(fieldName, value); } else { //设置虚拟的字段值 this.setProperty(propertyName, value); } } }
/// <summary> /// 将当前实体类的属性值(非空值)映射到相同属性名称的POCO实体类中。要求拷贝的同名属性是读写属性且类型相同。 /// </summary> /// <param name="pocoClass">POCO实体类</param> /// <returns>映射成功的属性数量</returns> public int MapToPOCO(object pocoClass) { if (pocoClass == null) { return(0); } int count = 0; int fcount = this.PropertyNames.Length; INamedMemberAccessor[] accessors = new INamedMemberAccessor[fcount]; DelegatedReflectionMemberAccessor drm = new DelegatedReflectionMemberAccessor(); Type type = pocoClass.GetType(); var ef = EntityFieldsCache.Item(this.GetType()); for (int i = 0; i < fcount; i++) { //实体类的属性字段可能跟属性名称不一样 edit at 2015.10.24 string perpertyName = ef.GetPropertyName(PropertyNames[i]); accessors[i] = drm.TryFindAccessor(type, perpertyName); } for (int i = 0; i < fcount; i++) { if (accessors[i] != null) { //this.PropertyValues[i] 可能为空,这会导致下面的赋值报错 //这种情况可能发生在实体类的数据是经过OQL部分Select 字段造成的,或者字段本身的值为NULL object Value = this.PropertyValues[i]; if (Value != null && Value != DBNull.Value) { accessors[i].SetValue(pocoClass, this.PropertyValues[i]); count++; } } } return(count); }
/// <summary> /// 将字典形式的参数数据,转换成数据库格式的参数素组 /// </summary> /// <param name="dictPara">表字段名字典</param> /// <param name="db">数据访问对象</param> /// <returns></returns> protected internal static IDataParameter[] GetParameters(Dictionary <string, TableNameField> dictPara, AdoHelper db) { if (dictPara == null) { return(null); } IDataParameter[] paras = new IDataParameter[dictPara.Count]; int index = 0; foreach (string key in dictPara.Keys) { object Value = dictPara[key]; if (Value is IDataParameter) { paras[index] = (IDataParameter)Value; } else { string paraName = key.StartsWith("@")?key.Substring(1):key; //参数名无需加上 ParameterChar //if (!key.StartsWith(db.GetParameterChar)) // paraName = db.GetParameterChar + key.Substring(1); var tnf = dictPara[key]; paras[index] = db.GetParameter(paraName, tnf.FieldValue); //为字符串类型的参数指定长度 edit at 2012.4.23 //增加判断tnf.Name!=null,这可能是因为使用了自定义查询的SqlMap的OQL,感谢网友 吉林-stdbool 发现此问题 if (tnf.Name != null && paras[index].Value != null && paras[index].Value.GetType() == typeof(string)) { //增加字符串长度的检查,如果值得长度大于定义的长度,抛出异常提示 2014.10.21 //int size = tnf.Entity.GetStringFieldSize(tnf.Field); //采用下面的方法,避免没有实体类元数据缓存 edit at 2015-12-5 SimplyField sf = EntityFieldsCache.Item(tnf.Entity.GetType()).GetPropertyFieldSize(tnf.Field, tnf.Entity); int size = sf.FieldLength; if (size != 0) //如果字段不是text等类型 { int length = paras[index].Value.ToString().Length; //if (length > size+2) //处理 like查询可能增加 %% 匹配的情况 //特别注意: //如果size==Int.Max,那么 size+2 会得到负数,从而导致 length > size+2 表达式为false //所以,修改成下面的样子,理论上不会再出错了。 //感谢网友 广州-银古 朋友发现此 bug ,2017.2.16 if (length - 2 > size && size > 0) { throw new NotSupportedException("当前实体类映射的字段" + paraName + " 长度没有定义或者长度小于了当前实际值的长度:" + length + ",请在实体类定义里面使用 setProperty 的重载方法指定合适的字段长度。"); } if (size > 0) { ((IDbDataParameter)paras[index]).Size = size; ((IDbDataParameter)paras[index]).DbType = sf.FieldDbType; //由实体类指定字段类型 } else { ((IDbDataParameter)paras[index]).Size = Math.Abs(size); ((IDbDataParameter)paras[index]).DbType = DbType.AnsiString; } } } } index++; } return(paras); }
/// <summary> /// 将字典形式的参数数据,转换成数据库格式的参数素组 /// </summary> /// <param name="dictPara">表字段名字典</param> /// <param name="db">数据访问对象</param> /// <returns></returns> protected internal static IDataParameter[] GetParameters(Dictionary <string, TableNameField> dictPara, AdoHelper db) { if (dictPara == null) { return(null); } if (db == null) { throw new ArgumentNullException("参数 db 不能为空!"); } IDataParameter[] paras = new IDataParameter[dictPara.Count]; int index = 0; foreach (string key in dictPara.Keys) { string paraName = key.StartsWith("@") ? key.Substring(1) : key; //参数名无需加上 ParameterChar //if (!paraName.StartsWith(db.GetParameterChar)) // paraName = db.GetParameterChar + paraName; var tnf = dictPara[key]; if (tnf == null) { continue; } paras[index] = db.GetParameter(paraName, tnf.FieldValue); //为字符串类型的参数指定长度 edit at 2012.4.23 //增加判断tnf.Name!=null,这可能是因为使用了自定义查询的SqlMap的OQL,感谢网友 吉林-stdbool 发现此问题 if (tnf.Name != null && paras[index].Value != null && paras[index].Value.GetType() == typeof(string)) { //增加字符串长度的检查,如果值得长度大于定义的长度,抛出异常提示 2014.10.21 //int size = tnf.Entity.GetStringFieldSize(tnf.Field); //采用下面的方法,避免没有实体类元数据缓存 edit at 2015-12-5 SimplyField sf = EntityFieldsCache.Item(tnf.Entity.GetType()).GetPropertyFieldSize(tnf.Field, tnf.Entity); int size = sf.FieldLength; if (size != 0) //如果字段不是text等类型 { int length = paras[index].Value.ToString().Length; //if (length > size+2) //处理 like查询可能增加 %% 匹配的情况 //特别注意: //如果size==Int.Max,那么 size+2 会得到负数,从而导致 length > size+2 表达式为false //所以,修改成下面的样子,理论上不会再出错了。 //感谢网友【广州-银古】朋友发现此 bug ,2017.2.16 //感谢网友【郑州-何】朋友发现在“OR”条件比较的查询下,查询的值应该可以超过字段长度的问题。 //为了确保安全,不被恶意攻击,这里限制为不得超过字段设定长度的40 倍。2018.5.30 if (length > size * 40 && size > 0) { throw new NotSupportedException("当前实体类映射的字段" + paraName + " 长度没有定义或者与该字段进行条件比较的值超过了字段设定长度的40倍,有被恶意攻击的风险!预定义的字段长度:" + length); } if (size > 0) { ((IDbDataParameter)paras[index]).Size = size; ((IDbDataParameter)paras[index]).DbType = sf.FieldDbType; //由实体类指定字段类型 } else { ((IDbDataParameter)paras[index]).Size = Math.Abs(size); ((IDbDataParameter)paras[index]).DbType = DbType.AnsiString; } } } index++; } return(paras); }
/// <summary> /// 二进制序列化 /// </summary> /// <param name="entity">实体类实例</param> /// <returns>字节数组</returns> public static byte[] BinarySerialize(EntityBase entity) { MemoryStream ms = new MemoryStream(); BinaryWriter bw = new BinaryWriter(ms); Type factEntityType = entity.GetType(); EntityFields ef = EntityFieldsCache.Item(factEntityType); byte b; //写入实体类标记 bw.Write(ENTITY_ITEM_FLAG); int length = entity.PropertyValues.Length - 1; for (int i = 0; i <= length; i++) { object obj = entity.PropertyValues[i]; //if (obj == System.DBNull.Value) obj = null;//DBNull.Value在Convert 的时候会失败 //为每个属性添加null标记 if (obj == System.DBNull.Value || obj == null) { b = 0;//NULL标记 } else { if (length == i) { b = byte.MaxValue; } else { b = 1; //如果标志位为 byte.MaxValue ,则表示已经写入所有的属性字段,可用于以后添加实体属性但又要成功读取原有的记录 } } bw.Write(b); //写入标记 if (b > 0) { Type propertyType = ef.GetPropertyType(entity.PropertyNames[i]); if (propertyType == null) { throw new Exception("PDF.NET实体类序列化错误:未知的实体属性类型,请检查实体类的属性和字段定义是否匹配。"); } switch (propertyType.Name) { case "Int32": bw.Write(Convert.ToInt32(obj)); break; case "String": bw.Write(Convert.ToString(obj)); break; case "DateTime": bw.Write(Convert.ToDateTime(obj).ToBinary()); break; case "Int16": bw.Write(Convert.ToInt16(obj)); break; case "Int64": bw.Write(Convert.ToInt64(obj)); break; case "Single": bw.Write(Convert.ToSingle(obj)); break; case "Double": bw.Write(Convert.ToDouble(obj)); break; case "Decimal": bw.Write(Convert.ToDecimal(obj)); break; case "Boolean": bw.Write(Convert.ToBoolean(obj)); break; case "Byte": bw.Write(Convert.ToByte(obj)); break; case "Char": bw.Write(Convert.ToChar(obj)); break; case "Byte[]": Byte[] buffer = (Byte[])obj; bw.Write(buffer.Length); //写入字节序列的长度 if (buffer.Length > 0) { bw.Write(buffer); } break; } } } bw.Close(); ms.Close(); return(ms.ToArray()); }