现在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 {
get
;
private
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 {
get
;
set
; }
8
9
public
bool
Ignore {
get
;
set
; }
10
11
}
12
这样,我们就可以很简单的定义一个数据实体了:
1
[DbResult]
2
public
class
MyClass
3
{
4
public
int
IntValue {
get
;
set
; }
5
public
string
StringValue {
get
;
set
; }
6
[DbColumn(ColumnName
=
"
DecimalValue
"
)]
7
public
decimal
?
NullableDecimalAndDifferentName {
get
;
set
; }
8
public
MyEnum EnumIsAlsoSupportted {
get
;
set
; }
9
public
MyEnum
?
NullableEnumIsAlsoSupportted {
get
;
set
; }
10
[DbColumn(Ignore
=
true
)]
11
public
object
NonDbValue {
get
;
set
; }
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 :
class
,
new
()
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 :
class
,
new
()
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),
true
)
as
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),
true
)
as
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了。。。不改了,大家自己加上吧)