不过在这个之前,先把我们需要的一些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了。。。不改了,大家自己加上吧)
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了。。。不改了,大家自己加上吧)