现在ORM已经是一门非常成熟的技术了,相信用的人不少,加上Linq to sql和Entity Framework的推波助澜,现在还用DataSet和DataTable的人已经越来越少了,不过,如果项目里面不用ORM工具,就不得不回归到DataSet时代吗?

    也许,我们没法改变项目的决策,但是,我们可以自己制造工具。

    这里先忽略掉那些麻烦的sql,调用那个存储过程之类的事情,假设我们已经通过各种手段(不管你是SqlHelper的拥护者还是enterprise library的支持者,或者是自己动手丰衣足食的DIY派),得到了一个IDataReader实例(什么,你不知道IDataReader接口?去msdn看看吧),并且把msdn上的说明:

    提供一种方法来读取一个或多个通过在数据源执行命令所获得的只进结果集流

    简化成:

    提供一种方法来读取一个通过在数据源执行命令所获得的只进结果集流

    也就是说,忽略掉一个存储过程返回多个结果集的情况下,我们可以直接把这个IDataReader实例中承载的数据转换为一个List<T>的形式。

    读到这里你可能会觉得这样的功能用一个反射就可以搞定了,何必写这么一篇水文?

    不过,这里用的不是常规反射。什么意思?反射还有常规和非常规之分吗?

    常规的反射是直接使用GetProperty和SetValue就可以实现这样的功能了,但是常规反射的问题是反射的效率问题,这样的代码效率是手写代码的1/1000,也就是说,虽然你写的很轻松,但是对CLR来说,这个代码执行的非常累。

    我这里要用的是反射中的Emit技术,动态拼装IL,获得一个高性能转换器,其执行效率至少是手写代码1/2,也就是说,使用这个代码的目的是你写的轻松,CLR执行的也轻松。

    不过,在这个之前,需要先定义数据实体,这个又是每个人的习惯都不一样的东西,我个人是习惯于利用Attribute来表明哪个属性是哪一列,不过考虑到便捷性,会用一个类级的Attribute表明这个类默认为是数据列,或者表明不是默认为是数据列:

 1     [AttributeUsage(AttributeTargets.Class, Inherited = true, AllowMultiple = false)]
 2     internal sealed class DbResultAttribute : Attribute
 3     {
 4 
 5         public DbResultAttribute()
 6             : this(true) { }
 7 
 8         public DbResultAttribute(bool defaultAsDbColumn)
 9         {
10             DefaultAsDbColumn = defaultAsDbColumn;
11         }
12 
13         public bool DefaultAsDbColumn { getprivate set; }
14 
15     }
16 


    而对于普通的属性,再给与一次修改列名和排除在数据列之外的机会:

 1     [AttributeUsage(AttributeTargets.Property, Inherited = true, AllowMultiple = false)]
 2     internal sealed class DbColumnAttribute : Attribute
 3     {
 4 
 5         public DbColumnAttribute() { }
 6 
 7         public string ColumnName { getset; }
 8 
 9         public bool Ignore { getset; }
10 
11     }
12 


    这样,我们就可以很简单的定义一个数据实体了:

 1     [DbResult]
 2     public class MyClass
 3     {
 4         public int IntValue { getset; }
 5         public string StringValue { getset; }
 6         [DbColumn(ColumnName = "DecimalValue")]
 7         public decimal? NullableDecimalAndDifferentName { getset; }
 8         public MyEnum EnumIsAlsoSupportted { getset; }
 9         public MyEnum? NullableEnumIsAlsoSupportted { getset; }
10         [DbColumn(Ignore = true)]
11         public object NonDbValue { getset; }
12     }
13 
14     public enum MyEnum
15     {
16         X,
17         Y,
18         Z,
19     }
20 


    定义简单吧,不过,可以看出来实现部分也是异常的复杂,除了标准的int,string,decimal类型,还要支持可空类型,以及枚举类型,甚至是可空枚举类型。。。不过,如果数据库类型和定义的类型(枚举看它的基础类型)不匹配就会直接抛出异常,这点要注意一下。

    千里之行始于足下,先做最简单的,搭建一个简单的环境,并且读取这个实体类的信息吧:

 1     public static class DataReaderExtensions
 2     {
 3 
 4         #region Public Static Methods
 5 
 6         public static List<T> Select<T>(this IDataReader reader)
 7             where T : classnew()
 8         {
 9             if (reader == null)
10                 throw new ArgumentNullException("reader");
11             return EntityConverter<T>.Select(reader);
12         }
13 
14         #endregion
15 
16         #region Class: EntityConverter<T>
17 
18         private class EntityConverter<T>
19             where T : classnew()
20         {
21 
22             #region Struct: DbColumnInfo
23 
24             private struct DbColumnInfo
25             {
26                 public readonly string PropertyName;
27                 public readonly string ColumnName;
28                 public readonly Type Type;
29                 public readonly MethodInfo SetMethod;
30 
31                 public DbColumnInfo(PropertyInfo prop, DbColumnAttribute attr)
32                 {
33                     PropertyName = prop.Name;
34                     ColumnName = attr.ColumnName ?? prop.Name;
35                     Type = prop.PropertyType;
36                     SetMethod = prop.GetSetMethod(false);
37                 }
38             }
39 
40             #endregion
41 
42             #region Fields
43             private static Converter<IDataReader, List<T>> batchDataLoader;
44             #endregion
45 
46             #region Properties
47 
48             private static Converter<IDataReader, List<T>> BatchDataLoader
49             {
50                 get
51                 {
52                     if (batchDataLoader == null)
53                         batchDataLoader = CreateBatchDataLoader(new List<DbColumnInfo>(GetProperties()));
54                     return batchDataLoader;
55                 }
56             }
57 
58             #endregion
59 
60             #region Init Methods
61 
62             private static IEnumerable<DbColumnInfo> GetProperties()
63             {
64                 DbResultAttribute dbResult = Attribute.GetCustomAttribute(typeof(T), typeof(DbResultAttribute), trueas DbResultAttribute;
65                 foreach (var prop in typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.Public))
66                 {
67                     if (prop.GetIndexParameters().Length > 0)
68                         continue;
69                     var setMethod = prop.GetSetMethod(false);
70                     if (setMethod == null)
71                         continue;
72                     var attr = Attribute.GetCustomAttribute(prop, typeof(DbColumnAttribute), trueas DbColumnAttribute;
73                     if (dbResult != null && dbResult.DefaultAsDbColumn)
74                         if (attr != null && attr.Ignore)
75                             continue;
76                         else
77                             attr = attr ?? new DbColumnAttribute();
78                     else
79                         if (attr == null || attr.Ignore)
80                             continue;
81                     yield return new DbColumnInfo(prop, attr);
82                 }
83             }
84 
85             #endregion
86 
87     }
88 


    这里,我们先建立一个DataReaderExtensions类,这个类的功能只有一个,给IDataReader添加一个Select<T>的方法,然后把思想转嫁给一个内部类EntityConverter<T>的Select方法(这个方法在那里?还没贴出来哪,别急),为什么用这么一个内部泛型类那?建议参考老赵的这篇文章,主要是出于性能方面的考虑。

    然后我们在通过GetProperties方法,获得关于这个实体类型的信息,放到一个叫DbColumnInfo的结构体里面(为什么用结构体哪?还是性能方面的考虑,减少垃圾对象,让GC过的轻松点)。这里的GetProperties方法就是一个彻头彻尾的普通反射,那么性能自然也就是手写代码的1/1000,不过别急,这个段代码对每个T而言仅仅跑1次,也就是说,某个类型Select过了,那么下一次,这个类型就不需要跑这个GetProperties方法了。

    不过,有一点不要误解,即使缓存了PropertyInfo,那也仅仅是减少了发现成员的代价,PropertyInfo.SetValue的代价依然是反射的标准代价,执行效率是1/1000。

    也就是说,之后的任务才是本文的重点内容,消除掉PropertyInfo.SetValue的代价,让我们的反射跑得飞起来。

    不过在这个之前,先把我们需要的一些IDataReader里面的方法先反射出来,做好缓存(放在DataReaderExtensions类里面,避免为每个T反射一次):

 1         #region Static Readonly Fields
 2         private static readonly MethodInfo DataRecord_ItemGetter_Int =
 3             typeof(IDataRecord).GetMethod("get_Item"new Type[] { typeof(int) });
 4         private static readonly MethodInfo DataRecord_GetOrdinal =
 5             typeof(IDataRecord).GetMethod("GetOrdinal");
 6         private static readonly MethodInfo DataReader_Read =
 7             typeof(IDataReader).GetMethod("Read");
 8         private static readonly MethodInfo Convert_IsDBNull =
 9             typeof(Convert).GetMethod("IsDBNull");
10         private static readonly MethodInfo DataRecord_GetDateTime =
11             typeof(IDataRecord).GetMethod("GetDateTime");
12         private static readonly MethodInfo DataRecord_GetDecimal =
13             typeof(IDataRecord).GetMethod("GetDecimal");
14         private static readonly MethodInfo DataRecord_GetDouble =
15             typeof(IDataRecord).GetMethod("GetDouble");
16         private static readonly MethodInfo DataRecord_GetInt32 =
17             typeof(IDataRecord).GetMethod("GetInt32");
18         private static readonly MethodInfo DataRecord_GetInt64 =
19             typeof(IDataRecord).GetMethod("GetInt64");
20         private static readonly MethodInfo DataRecord_GetString =
21             typeof(IDataRecord).GetMethod("GetString");
22         private static readonly MethodInfo DataRecord_IsDBNull =
23             typeof(IDataRecord).GetMethod("IsDBNull");
24         #endregion
25 

    接下来就是Emit了,但是鉴于其的难度比较高,就不仔细说明了,如果各位有兴趣,可以单独聊,这里就直接把这成堆的天书贴上来了(未处理short、byte、bool的情况,因为Oracle不支持这个类型,所以一直没写,同样枚举也只支持基础类型是int的或long的):

  1             #region Init Methods
  2 
  3             private static Converter<IDataReader, List<T>> CreateBatchDataLoader(List<DbColumnInfo> columnInfoes)
  4             {
  5                 DynamicMethod dm = new DynamicMethod(string.Empty, typeof(List<T>),
  6                     new Type[] { typeof(IDataReader) }, typeof(EntityConverter<T>));
  7                 ILGenerator il = dm.GetILGenerator();
  8                 LocalBuilder list = il.DeclareLocal(typeof(List<T>));
  9                 LocalBuilder item = il.DeclareLocal(typeof(T));
 10                 Label exit = il.DefineLabel();
 11                 Label loop = il.DefineLabel();
 12                 // List<T> list = new List<T>();
 13                 il.Emit(OpCodes.Newobj, typeof(List<T>).GetConstructor(Type.EmptyTypes));
 14                 il.Emit(OpCodes.Stloc_S, list);
 15                 // [ int %index% = arg.GetOrdinal(%ColumnName%); ]
 16                 LocalBuilder[] colIndices = GetColumnIndices(il, columnInfoes);
 17                 // while (arg.Read()) {
 18                 il.MarkLabel(loop);
 19                 il.Emit(OpCodes.Ldarg_0);
 20                 il.Emit(OpCodes.Callvirt, DataReader_Read);
 21                 il.Emit(OpCodes.Brfalse, exit);
 22                 //      T item = new T { %Property% =  };
 23                 BuildItem(il, columnInfoes, item, colIndices);
 24                 //      list.Add(item);
 25                 il.Emit(OpCodes.Ldloc_S, list);
 26                 il.Emit(OpCodes.Ldloc_S, item);
 27                 il.Emit(OpCodes.Callvirt, typeof(List<T>).GetMethod("Add"));
 28                 // }
 29                 il.Emit(OpCodes.Br, loop);
 30                 il.MarkLabel(exit);
 31                 // return list;
 32                 il.Emit(OpCodes.Ldloc_S, list);
 33                 il.Emit(OpCodes.Ret);
 34                 return (Converter<IDataReader, List<T>>)dm.CreateDelegate(typeof(Converter<IDataReader, List<T>>));
 35             }
 36 
 37             private static LocalBuilder[] GetColumnIndices(ILGenerator il, List<DbColumnInfo> columnInfoes)
 38             {
 39                 LocalBuilder[] colIndices = new LocalBuilder[columnInfoes.Count];
 40                 for (int i = 0; i < colIndices.Length; i++)
 41                 {
 42                     // int %index% = arg.GetOrdinal(%ColumnName%);
 43                     colIndices[i] = il.DeclareLocal(typeof(int));
 44                     il.Emit(OpCodes.Ldarg_0);
 45                     il.Emit(OpCodes.Ldstr, columnInfoes[i].ColumnName);
 46                     il.Emit(OpCodes.Callvirt, DataRecord_GetOrdinal);
 47                     il.Emit(OpCodes.Stloc_S, colIndices[i]);
 48                 }
 49                 return colIndices;
 50             }
 51 
 52             private static void BuildItem(ILGenerator il, List<DbColumnInfo> columnInfoes,
 53                 LocalBuilder item, LocalBuilder[] colIndices)
 54             {
 55                 // T item = new T();
 56                 il.Emit(OpCodes.Newobj, typeof(T).GetConstructor(Type.EmptyTypes));
 57                 il.Emit(OpCodes.Stloc_S, item);
 58                 for (int i = 0; i < colIndices.Length; i++)
 59                 {
 60                     if (IsCompatibleType(columnInfoes[i].Type, typeof(int)))
 61                     {
 62                         // item.%Property% = arg.GetInt32(%index%);
 63                         ReadInt32(il, item, columnInfoes, colIndices, i);
 64                     }
 65                     else if (IsCompatibleType(columnInfoes[i].Type, typeof(int?)))
 66                     {
 67                         // item.%Property% = arg.IsDBNull ? default(int?) : (int?)arg.GetInt32(%index%);
 68                         ReadNullableInt32(il, item, columnInfoes, colIndices, i);
 69                     }
 70                     else if (IsCompatibleType(columnInfoes[i].Type, typeof(long)))
 71                     {
 72                         // item.%Property% = arg.GetInt64(%index%);
 73                         ReadInt64(il, item, columnInfoes, colIndices, i);
 74                     }
 75                     else if (IsCompatibleType(columnInfoes[i].Type, typeof(long?)))
 76                     {
 77                         // item.%Property% = arg.IsDBNull ? default(long?) : (long?)arg.GetInt64(%index%);
 78                         ReadNullableInt64(il, item, columnInfoes, colIndices, i);
 79                     }
 80                     else if (IsCompatibleType(columnInfoes[i].Type, typeof(decimal)))
 81                     {
 82                         // item.%Property% = arg.GetDecimal(%index%);
 83                         ReadDecimal(il, item, columnInfoes[i].SetMethod, colIndices[i]);
 84                     }
 85                     else if (columnInfoes[i].Type == typeof(decimal?))
 86                     {
 87                         // item.%Property% = arg.IsDBNull ? default(decimal?) : (int?)arg.GetDecimal(%index%);
 88                         ReadNullableDecimal(il, item, columnInfoes[i].SetMethod, colIndices[i]);
 89                     }
 90                     else if (columnInfoes[i].Type == typeof(DateTime))
 91                     {
 92                         // item.%Property% = arg.GetDateTime(%index%);
 93                         ReadDateTime(il, item, columnInfoes[i].SetMethod, colIndices[i]);
 94                     }
 95                     else if (columnInfoes[i].Type == typeof(DateTime?))
 96                     {
 97                         // item.%Property% = arg.IsDBNull ? default(DateTime?) : (int?)arg.GetDateTime(%index%);
 98                         ReadNullableDateTime(il, item, columnInfoes[i].SetMethod, colIndices[i]);
 99                     }
100                     else
101                     {
102                         // item.%Property% = (%PropertyType%)arg[%index%];
103                         ReadObject(il, item, columnInfoes, colIndices, i);
104                     }
105                 }
106             }
107 
108             private static bool IsCompatibleType(Type t1, Type t2)
109             {
110                 if (t1 == t2)
111                     return true;
112                 if (t1.IsEnum && Enum.GetUnderlyingType(t1) == t2)
113                     return true;
114                 var u1 = Nullable.GetUnderlyingType(t1);
115                 var u2 = Nullable.GetUnderlyingType(t2);
116                 if (u1 != null && u2 != null)
117                     return IsCompatibleType(u1, u2);
118                 return false;
119             }
120 
121             private static void ReadInt32(ILGenerator il, LocalBuilder item,
122                 List<DbColumnInfo> columnInfoes, LocalBuilder[] colIndices, int i)
123             {
124                 il.Emit(OpCodes.Ldloc_S, item);
125                 il.Emit(OpCodes.Ldarg_0);
126                 il.Emit(OpCodes.Ldloc_S, colIndices[i]);
127                 il.Emit(OpCodes.Callvirt, DataRecord_GetInt32);
128                 il.Emit(OpCodes.Callvirt, columnInfoes[i].SetMethod);
129             }
130 
131             private static void ReadNullableInt32(ILGenerator il, LocalBuilder item,
132                 List<DbColumnInfo> columnInfoes, LocalBuilder[] colIndices, int i)
133             {
134                 var local = il.DeclareLocal(columnInfoes[i].Type);
135                 Label intNull = il.DefineLabel();
136                 Label intCommon = il.DefineLabel();
137                 il.Emit(OpCodes.Ldloca, local);
138                 il.Emit(OpCodes.Ldarg_0);
139                 il.Emit(OpCodes.Ldloc_S, colIndices[i]);
140                 il.Emit(OpCodes.Callvirt, DataRecord_IsDBNull);
141                 il.Emit(OpCodes.Brtrue_S, intNull);
142                 il.Emit(OpCodes.Ldarg_0);
143                 il.Emit(OpCodes.Ldloc_S, colIndices[i]);
144                 il.Emit(OpCodes.Callvirt, DataRecord_GetInt32);
145                 il.Emit(OpCodes.Call, columnInfoes[i].Type.GetConstructor(
146                     new Type[] { Nullable.GetUnderlyingType(columnInfoes[i].Type) }));
147                 il.Emit(OpCodes.Br_S, intCommon);
148                 il.MarkLabel(intNull);
149                 il.Emit(OpCodes.Initobj, columnInfoes[i].Type);
150                 il.MarkLabel(intCommon);
151                 il.Emit(OpCodes.Ldloc_S, item);
152                 il.Emit(OpCodes.Ldloc, local);
153                 il.Emit(OpCodes.Callvirt, columnInfoes[i].SetMethod);
154             }
155 
156             private static void ReadInt64(ILGenerator il, LocalBuilder item,
157                 List<DbColumnInfo> columnInfoes, LocalBuilder[] colIndices, int i)
158             {
159                 il.Emit(OpCodes.Ldloc_S, item);
160                 il.Emit(OpCodes.Ldarg_0);
161                 il.Emit(OpCodes.Ldloc_S, colIndices[i]);
162                 il.Emit(OpCodes.Callvirt, DataRecord_GetInt64);
163                 il.Emit(OpCodes.Callvirt, columnInfoes[i].SetMethod);
164             }
165 
166             private static void ReadNullableInt64(ILGenerator il, LocalBuilder item,
167                 List<DbColumnInfo> columnInfoes, LocalBuilder[] colIndices, int i)
168             {
169                 var local = il.DeclareLocal(columnInfoes[i].Type);
170                 Label intNull = il.DefineLabel();
171                 Label intCommon = il.DefineLabel();
172                 il.Emit(OpCodes.Ldloca, local);
173                 il.Emit(OpCodes.Ldarg_0);
174                 il.Emit(OpCodes.Ldloc_S, colIndices[i]);
175                 il.Emit(OpCodes.Callvirt, DataRecord_IsDBNull);
176                 il.Emit(OpCodes.Brtrue_S, intNull);
177                 il.Emit(OpCodes.Ldarg_0);
178                 il.Emit(OpCodes.Ldloc_S, colIndices[i]);
179                 il.Emit(OpCodes.Callvirt, DataRecord_GetInt64);
180                 il.Emit(OpCodes.Call, columnInfoes[i].Type.GetConstructor(
181                     new Type[] { Nullable.GetUnderlyingType(columnInfoes[i].Type) }));
182                 il.Emit(OpCodes.Br_S, intCommon);
183                 il.MarkLabel(intNull);
184                 il.Emit(OpCodes.Initobj, columnInfoes[i].Type);
185                 il.MarkLabel(intCommon);
186                 il.Emit(OpCodes.Ldloc_S, item);
187                 il.Emit(OpCodes.Ldloc, local);
188                 il.Emit(OpCodes.Callvirt, columnInfoes[i].SetMethod);
189             }
190 
191             private static void ReadDecimal(ILGenerator il, LocalBuilder item,
192                 MethodInfo setMethod, LocalBuilder colIndex)
193             {
194                 il.Emit(OpCodes.Ldloc_S, item);
195                 il.Emit(OpCodes.Ldarg_0);
196                 il.Emit(OpCodes.Ldloc_S, colIndex);
197                 il.Emit(OpCodes.Callvirt, DataRecord_GetDecimal);
198                 il.Emit(OpCodes.Callvirt, setMethod);
199             }
200 
201             private static void ReadNullableDecimal(ILGenerator il, LocalBuilder item,
202                 MethodInfo setMethod, LocalBuilder colIndex)
203             {
204                 var local = il.DeclareLocal(typeof(decimal?));
205                 Label decimalNull = il.DefineLabel();
206                 Label decimalCommon = il.DefineLabel();
207                 il.Emit(OpCodes.Ldloca, local);
208                 il.Emit(OpCodes.Ldarg_0);
209                 il.Emit(OpCodes.Ldloc_S, colIndex);
210                 il.Emit(OpCodes.Callvirt, DataRecord_IsDBNull);
211                 il.Emit(OpCodes.Brtrue_S, decimalNull);
212                 il.Emit(OpCodes.Ldarg_0);
213                 il.Emit(OpCodes.Ldloc_S, colIndex);
214                 il.Emit(OpCodes.Callvirt, DataRecord_GetDecimal);
215                 il.Emit(OpCodes.Call, typeof(decimal?).GetConstructor(new Type[] { typeof(decimal) }));
216                 il.Emit(OpCodes.Br_S, decimalCommon);
217                 il.MarkLabel(decimalNull);
218                 il.Emit(OpCodes.Initobj, typeof(decimal?));
219                 il.MarkLabel(decimalCommon);
220                 il.Emit(OpCodes.Ldloc_S, item);
221                 il.Emit(OpCodes.Ldloc, local);
222                 il.Emit(OpCodes.Callvirt, setMethod);
223             }
224 
225             private static void ReadDateTime(ILGenerator il, LocalBuilder item,
226                 MethodInfo setMethod, LocalBuilder colIndex)
227             {
228                 il.Emit(OpCodes.Ldloc_S, item);
229                 il.Emit(OpCodes.Ldarg_0);
230                 il.Emit(OpCodes.Ldloc_S, colIndex);
231                 il.Emit(OpCodes.Callvirt, DataRecord_GetDateTime);
232                 il.Emit(OpCodes.Callvirt, setMethod);
233             }
234 
235             private static void ReadNullableDateTime(ILGenerator il, LocalBuilder item,
236                 MethodInfo setMethod, LocalBuilder colIndex)
237             {
238                 var local = il.DeclareLocal(typeof(DateTime?));
239                 Label dtNull = il.DefineLabel();
240                 Label dtCommon = il.DefineLabel();
241                 il.Emit(OpCodes.Ldloca, local);
242                 il.Emit(OpCodes.Ldarg_0);
243                 il.Emit(OpCodes.Ldloc_S, colIndex);
244                 il.Emit(OpCodes.Callvirt, DataRecord_IsDBNull);
245                 il.Emit(OpCodes.Brtrue_S, dtNull);
246                 il.Emit(OpCodes.Ldarg_0);
247                 il.Emit(OpCodes.Ldloc_S, colIndex);
248                 il.Emit(OpCodes.Callvirt, DataRecord_GetDateTime);
249                 il.Emit(OpCodes.Call, typeof(DateTime?).GetConstructor(new Type[] { typeof(DateTime) }));
250                 il.Emit(OpCodes.Br_S, dtCommon);
251                 il.MarkLabel(dtNull);
252                 il.Emit(OpCodes.Initobj, typeof(DateTime?));
253                 il.MarkLabel(dtCommon);
254                 il.Emit(OpCodes.Ldloc_S, item);
255                 il.Emit(OpCodes.Ldloc, local);
256                 il.Emit(OpCodes.Callvirt, setMethod);
257             }
258 
259             private static void ReadObject(ILGenerator il, LocalBuilder item,
260                 List<DbColumnInfo> columnInfoes, LocalBuilder[] colIndices, int i)
261             {
262                 Label common = il.DefineLabel();
263                 il.Emit(OpCodes.Ldloc_S, item);
264                 il.Emit(OpCodes.Ldarg_0);
265                 il.Emit(OpCodes.Ldloc_S, colIndices[i]);
266                 il.Emit(OpCodes.Callvirt, DataRecord_ItemGetter_Int);
267                 il.Emit(OpCodes.Dup);
268                 il.Emit(OpCodes.Call, Convert_IsDBNull);
269                 il.Emit(OpCodes.Brfalse_S, common);
270                 il.Emit(OpCodes.Pop);
271                 il.Emit(OpCodes.Ldnull);
272                 il.MarkLabel(common);
273                 il.Emit(OpCodes.Unbox_Any, columnInfoes[i].Type);
274                 il.Emit(OpCodes.Callvirt, columnInfoes[i].SetMethod);
275             }
276 
277             #endregion
278 
279             #region Internal Methods
280 
281             internal static List<T> Select(IDataReader reader)
282             {
283                 return BatchDataLoader(reader);
284             }
285 
286             #endregion
287 


    好,到这里,这个扩展方法已经完成了,来试用一下吧:

 1         public List<MyClass> GetList()
 2         {
 3             var reader = GetReader();
 4             return reader.Select<MyClass>();
 5         }
 6 
 7         public IDataReader GetReader()
 8         {
 9             // todo : GetReader
10             throw new NotImplementedException();
11         }
12 


    是不是很简单,大家不妨做个效率测试,看看是这段基于反射的代码效率高还是大家手写的效率高,以及填充到DataSet的效率,当然要注意数据的访问时间本身是不缺定的。(里面用了不少优化的访问方式,如果大家在处理IDataReader的手法不够娴熟的话,还真难说效率谁的高,呵呵

最后的示例代码里面忘记用using了。。。不改了,大家自己加上吧

posted on 2009-11-12 13:23  Zhenway  阅读(15603)  评论(38编辑  收藏  举报