使用DataReader把数据直接读取到一个指定的类型列表中。省去了像使用Xml或DataTable等中间类型转换,提高了性能。相当于直接从数据库中读取了存储的对象。
使用Emit比使用反射性能要高很多。因为它相当于直接运行编码的函数,而反射有很大的性能损耗。然后编写这个动态的函数也需要一定的消耗,所以对于反复使用的类型可以缓存这个编写好的方法以便快速的执行。
(修改:增加了一个编译条件WRITE_FILE用来将内存方法保存到文件以便编译成C#语言,增加漏掉的辅助函数)
/// <summary> /// 对类型T编写一个动态方法来设置其属性,并返回对动态方法的调用结果。 /// 对于值是DBNull.Value的数据将不对属性进行赋值并保持对象的默认值。 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="reader"></param> /// <param name="infoMessage"></param> /// <returns></returns> private static List<T> readEntities<T>(IDataReader reader, out string infoMessage) { infoMessage = null; if (reader == null || reader.IsClosed) { infoMessage = "连接已关闭!"; return null; } if (m_CatchMethod == null) { m_CatchMethod = new Dictionary<Type, DynamicMethod>(); }Type itemType = typeof(T); if (!m_CatchMethod.ContainsKey(itemType)) { #if WRITE_FILE AssemblyName aName = new AssemblyName("DynamicAssembly"); AssemblyBuilder ab = AppDomain.CurrentDomain.DefineDynamicAssembly(aName, AssemblyBuilderAccess.RunAndSave); ModuleBuilder mb = ab.DefineDynamicModule(aName.Name, aName.Name + ".dll"); TypeBuilder tb = mb.DefineType("DynamicType", TypeAttributes.Public); #endif Type listType = typeof(List<T>); Type objectType = typeof(object); Type objArrayType = typeof(object[]); Type boolType = typeof(bool); Type[] methodArgs = { typeof(IDataReader) }; MethodInfo LAdd = listType.GetMethod("Add"); PropertyInfo[] properties = null; getMappedPropertys(itemType, reader, out properties); #if WRITE_FILE MethodBuilder dm = tb.DefineMethod("ReadEntities", MethodAttributes.Public| MethodAttributes.Static, listType, methodArgs); #else DynamicMethod dm = new DynamicMethod("ReadEntities", listType, methodArgs, typeof(SqlServer)); #endif //开始编写动态方法,动态方法在性能上优于反射,无需编译直接运行。 ILGenerator ilg = dm.GetILGenerator(); //List<T> list; LocalBuilder list = ilg.DeclareLocal(listType); //T item; LocalBuilder item = ilg.DeclareLocal(itemType); //object[] values; LocalBuilder values = ilg.DeclareLocal(objArrayType); //object objValue; LocalBuilder objValue = ilg.DeclareLocal(objectType); //type nulls LocalBuilder[] typeNulls = null; //设置类型对应的空值,以便在遇到DBNull.Value时用其值设置到对像成员上。 initTypesNull(properties, ilg, out typeNulls);
Label exit = ilg.DefineLabel(); Label loop = ilg.DefineLabel(); Label[] lblArray = new Label[properties.Length * 2]; for (int i = 0; i < lblArray.Length; i++) { lblArray[i] = ilg.DefineLabel(); } //list = new List<T>(); ilg.Emit(OpCodes.Newobj, listType.GetConstructor(Type.EmptyTypes)); ilg.Emit(OpCodes.Stloc_S, list);
//values=new object[FieldCount]; ilg.Emit(OpCodes.Ldc_I4, reader.FieldCount); ilg.Emit(OpCodes.Newarr, objectType); ilg.Emit(OpCodes.Stloc_S, values);
// while (arg.Read()) { ilg.MarkLabel(loop); ilg.Emit(OpCodes.Ldarg_0); ilg.Emit(OpCodes.Callvirt, Reader_Read); ilg.Emit(OpCodes.Brfalse, exit);
//reader.GetValues(values); ilg.Emit(OpCodes.Ldarg_0); ilg.Emit(OpCodes.Ldloc_S, values); ilg.Emit(OpCodes.Callvirt, Reader_GetValues); ilg.Emit(OpCodes.Pop);
//item=new T(); ilg.Emit(OpCodes.Newobj, itemType.GetConstructor(Type.EmptyTypes)); ilg.Emit(OpCodes.Stloc_S, item);
//item.Property=Convert(values[index]); for (int index = 0; index < properties.Length; index++) { PropertyInfo pi = properties[index]; if (pi == null) { continue; }
//objValue=value[index]; ilg.Emit(OpCodes.Ldloc_S, values); ilg.Emit(OpCodes.Ldc_I4, index); ilg.Emit(OpCodes.Ldelem_Ref); ilg.Emit(OpCodes.Stloc_S, objValue);
//tmpBool=Convert.IsDBNull(objValue); ilg.Emit(OpCodes.Ldloc_S, objValue); ilg.Emit(OpCodes.Call, Convert_IsDBNull);
//if (!tmpBool){ ilg.Emit(OpCodes.Brtrue_S, lblArray[index * 2]); //item.Field=Convert(objValue).ToXXX(); ilg.Emit(OpCodes.Ldloc_S, item); ilg.Emit(OpCodes.Ldloc_S, objValue); if (!convertValueToType(ilg, pi, out infoMessage)) { return null; } ilg.Emit(OpCodes.Callvirt, pi.GetSetMethod()); //} ilg.Emit(OpCodes.Br_S, lblArray[index * 2 + 1]); //else { ilg.MarkLabel(lblArray[index * 2]); //item.Field=objValue; ilg.Emit(OpCodes.Ldloc_S, item); ilg.Emit(OpCodes.Ldloc_S, typeNulls[index]); ilg.Emit(OpCodes.Callvirt, pi.GetSetMethod()); //} ilg.MarkLabel(lblArray[index * 2 + 1]); }
//list.Add(item); ilg.Emit(OpCodes.Ldloc_S, list); ilg.Emit(OpCodes.Ldloc_S, item); ilg.Emit(OpCodes.Callvirt, LAdd); //} ilg.Emit(OpCodes.Br, loop); ilg.MarkLabel(exit);
// return list; ilg.Emit(OpCodes.Ldloc_S, list); ilg.Emit(OpCodes.Ret); #if WRITE_FILE Type t = tb.CreateType(); ab.Save(aName.Name + ".dll"); #else //添加到缓存 m_CatchMethod.Add(itemType, dm); #endif if (m_CatchMethod.Count > 100) { infoMessage = "缓存的处理方法数据以达" + m_CatchMethod.Count; } }
if (m_CatchMethod.ContainsKey(itemType)) { DynamicMethod dm = m_CatchMethod[itemType]; //使用代理来执行动态方法,这样比直接使用Invoke效率更高,因为无需组织一个参数数组。 ReadEntityInvoker<List<T>> invoker = dm.CreateDelegate(typeof(ReadEntityInvoker<List<T>>)) as ReadEntityInvoker<List<T>>; return invoker.Invoke(reader); } infoMessage = "没有找到对应类型的处理方法。"; return null; }
/// <summary> /// 定义对象的每一个成员类型对应的空值。 /// </summary> /// <param name="properties">对象属性数组</param> /// <param name="ilg">代码生成器</param> /// <param name="typeNulls">类型对应的空值,Null或Nullable<basetype>()</param> private static void initTypesNull(PropertyInfo[] properties, ILGenerator ilg, out LocalBuilder[] typeNulls) { typeNulls = new LocalBuilder[properties.Length]; for (int index = 0; index < properties.Length; index++) { PropertyInfo pi = properties[index]; typeNulls[index] = ilg.DeclareLocal(pi.PropertyType); if (pi.PropertyType.IsValueType) { ilg.Emit(OpCodes.Ldloca_S, typeNulls[index]); ilg.Emit(OpCodes.Initobj, pi.PropertyType); } else { ilg.Emit(OpCodes.Ldnull); ilg.Emit(OpCodes.Stloc_S, typeNulls[index]); } } } /// <summary> /// 由T的属性类型决定使用的Convert方法。 /// </summary> /// <param name="ilg"></param> /// <param name="pi"></param> /// <param name="infoMessage"></param> /// <returns></returns> private static bool convertValueToType(ILGenerator ilg, PropertyInfo pi, out string infoMessage) { infoMessage = null; //不可空类型 TypeCode code = Type.GetTypeCode(pi.PropertyType); switch (code) { case TypeCode.Int16: ilg.Emit(OpCodes.Call, Convert_ToInt16); return true; case TypeCode.Int32: ilg.Emit(OpCodes.Call, Convert_ToInt32); return true; case TypeCode.Int64: ilg.Emit(OpCodes.Call, Convert_ToInt64); return true; case TypeCode.Boolean: ilg.Emit(OpCodes.Call, Convert_ToBoolean); return true; case TypeCode.String: ilg.Emit(OpCodes.Callvirt, Object_ToString); return true; case TypeCode.DateTime: ilg.Emit(OpCodes.Call, Convert_ToDateTime); return true; case TypeCode.Decimal: ilg.Emit(OpCodes.Call, Convert_ToDecimal); return true; case TypeCode.Double: ilg.Emit(OpCodes.Call, Convert_ToDouble); return true; } //可空类型处理 Type type = Nullable.GetUnderlyingType(pi.PropertyType); if (type != null) { code = Type.GetTypeCode(type); switch (code) { case TypeCode.Int16: ilg.Emit(OpCodes.Call, Convert_ToNullInt16); return true; case TypeCode.Int32: ilg.Emit(OpCodes.Call, Convert_ToNullInt32); return true; case TypeCode.Int64: ilg.Emit(OpCodes.Call, Convert_ToNullInt64); return true; case TypeCode.Boolean: ilg.Emit(OpCodes.Call, Convert_ToNullBoolean); return true; case TypeCode.DateTime: ilg.Emit(OpCodes.Call, Convert_ToNullDateTime); return true; case TypeCode.Decimal: ilg.Emit(OpCodes.Call, Convert_ToNullDecimal); return true; case TypeCode.Double: ilg.Emit(OpCodes.Call, Convert_ToNullDouble); return true; } } infoMessage = string.Format("不支持\"{0}\"类型的转换!", pi.PropertyType.Name); return false; } /// <summary> /// 读取Reader的列与Type属性的对应关系。 /// </summary> /// <param name="type">要分析的类型</param> /// <param name="reader">使用DataReader对象</param> /// <param name="mappedProerties">返回属性数组,无对应的是元素是Null。</param> private static void getMappedPropertys(Type type, IDataReader reader, out PropertyInfo[] mappedProerties) { mappedProerties = new PropertyInfo[reader.FieldCount]; string[] fields = new string[reader.FieldCount]; for (int i = 0; i < reader.FieldCount; i++) { fields[i] = reader.GetName(i); } List<PropertyInfo> properties = new List<PropertyInfo>(type.GetProperties()); for (int i = 0; i < reader.FieldCount; i++) { foreach (PropertyInfo pt in properties) { FieldAttribute fa = Attribute.GetCustomAttribute(pt, typeof(FieldAttribute)) as FieldAttribute; if ((fa != null && string.Compare(fa.Field, fields[i], true) == 0) || string.Compare(pt.Name, fields[i], true) == 0) { properties.Remove(pt); mappedProerties[i] = pt; break; } } } }