使用CodeDom提高ORM性能
下载本文代码:https://files.cnblogs.com/afritxia2008/WebTest.rar(请使用 Visual Studio 2008 打开)
在进行讨论之前,我假设读者已经了解.NET反射、自定义属性、CodeDom这些技术。并接触过ORM框架源码,如果对ORM并不了解,可以参考:http://www.cnblogs.com/xdesigner/archive/2008/06/24/1228702.html。在这篇文章中,我们主要讨论通过CodeDom提高ORM读取数据的性能问题。
ORM(Object/Relation Mapping对象-关系数据库映射)其中的一个功能是将数据源数据赋值给实体。实现方法是利用自定义属性和.NET反射机制。例如:
1
public class LWordEntity
2
{
3
/// <summary>
4
/// 获取或设置留言 ID
5
/// </summary>
6
[DataColumn(ColumnName = "LWordUID")]
7
public int LWordUID
8
{
9
// ![]()
10
}
11![]()
12
/// <summary>
13
/// 获取或设置发送用户
14
/// </summary>
15
[DataColumn(ColumnName = "PostUser")]
16
public string PostUser
17
{
18
// ![]()
19
}
20![]()
21
/// <summary>
22
/// 获取或设置发送时间
23
/// </summary>
24
[DataColumn(ColumnName = "PostTime")]
25
public DateTime PostTime
26
{
27
// ![]()
28
}
29![]()
30
/// <summary>
31
/// 获取或设置文本内容
32
/// </summary>
33
[DataColumn(ColumnName = "TextContent")]
34
public string TextContent
35
{
36
// ![]()
37
}
38
}
DataColumn是自定义的属性类,代码并不复杂所以在这里也就省略了。接下来需要通过反射读取自定义属性,并赋值。代码如下:
1
public void PutEntityProperties(object objEntity, DbDataReader dr)
2
{
3
// 获取实体类型
4
Type objType = objEntity.GetType();
5![]()
6
// 获取属性信息
7
PropertyInfo[] propInfoList = objType.GetProperties();
8![]()
9
if (propInfoList == null || propInfoList.Length <= 0)
10
return;
11![]()
12
foreach (PropertyInfo propInfo in propInfoList)
13
{
14
object[] colAttrList = propInfo.GetCustomAttributes(typeof(DataColumnAttribute), false);
15![]()
16
// 未标记 DataColumn 属性
17
if (colAttrList == null || colAttrList.Length <= 0)
18
continue;
19![]()
20
// 获取数据列属性
21
DataColumnAttribute colAttr = colAttrList[0] as DataColumnAttribute;
22![]()
23
int ordinal = -1;
24![]()
25
try
26
{
27
// 获取数据列序号
28
ordinal = dr.GetOrdinal(colAttr.ColumnName);
29
}
30
catch (Exception ex)
31
{
32
throw new MappingException(
33
String.Format("{0} 未找到该数据列( Cannot Found this Column {0} )", colAttr.ColumnName), ex);
34
}
35![]()
36
// 获取数据列值
37
object objValue = dr.GetValue(ordinal);
38![]()
39
if (objValue is DBNull)
40
{
41
// 将 null 值设置到属性
42
propInfo.SetValue(objEntity, null, null);
43
}
44
else
45
{
46
// 将值设置到属性
47
propInfo.SetValue(objEntity, objValue, null);
48
}
49
}
50
}
51
以上代码实现了读取数据源数据并向实体赋值的功能。但这样做速度非常慢,因为每读取一条数据库记录,每读取一个数据字段并向实体赋值的时候,都必须进行一次反射操作。数据量越大,且数据字段或实体属性越多,那么速度就越慢!在以上代码中,对实体的反射操作,其目的就是赋值。可以被等价的语句所替代:
entity.Prop = dr[“Porp”];
用简单的赋值语句肯定要比反射的速度快很多,而大数据量和多数据库字段对其影响也不是很大。不过需要注意的是因为每一个实体的具体属性不相同,所以赋值过程也是不相同的。例如:
News实体赋值代码:
1
void PutEntityProperties(NewsEntity entity, DbDataReader dr)
2
{
3
// 新闻 ID
4
entity.ID = (int)dr["ID"];
5
// 标题
6
entity.Title = (string)dr["Title"];
7
// 摘要
8
entity.Summary = (string)dr["Summary"];
9
// 发送用户
10
entity.PostUser = (string)dr["PostUser"];
11
// 发送时间
12
entity.PostTime = (DateTime)dr["PostTime"];
13
// 文本内容
14
entity.TextContent = (string)dr["TextContent"];
15
}
User实体赋值代码:
1
void PutEntityProperties(UserEntity entity, DbDataReader dr)
2
{
3
// 用户 ID
4
entity.ID = (int)dr["ID"];
5
// 用户名称
6
entity.UserName = (string)dr["UserName"];
7
// 密码
8
entity.UserPass = (string)dr["UserPass"];
9
// 电子邮件
10
entity.EMail = (string)dr["EMail"];
11
// 注册时间
12
entity.RegisterTime = (DateTime)dr["RegisterTime"];
13
}
14
News与User所具备的属性不同,所以赋值过程,也不相同!但毫无疑问,使用直接赋值的方法是速度最快的!试想一下,假如在做反射的时候不是直接赋值,而是根据自定义属性,动态的生成赋值代码,编译以后临时保存起来。那么以后再进行赋值操作的时候,直接调用这个编译好的赋值代码,不就可以大大提升程序性能了么?有没有一个办法可以自动建立类似上面这样的代码呢?我们可以考虑使用反射和CodeDom技术。
首先为了解决不同实体的不同的赋值过程,我们需要建立一个接口:IEntityPropertyPutter。在该接口中的PutEntityProperties函数负责真正的赋值逻辑。在赋值的过程中会调用IEntityPropertyPutter的具体实现类的实例。具体类图如下:
![]()
EntityPropertyPutterFactory工厂类负责创建IEntityPropertyPutter接口具体实现类的实例。首先该工厂类会从缓存中获取IEntityPropertyPutter接口实例,如果该实例为空(还没有被创建),那么该工厂类会调用EntityPropertyPutterMaker构建者类创建实例(Entity实体类本身也可以直接实现IEntityPropertyPutter接口,来加快程序的运行速度)。在构建者内部会动态创建新的程序集(Assembly),在该程序集中只存在一个QuicklyPutter类。在QuicklyPutter类中描述了具体的赋值逻辑,这些逻辑编码则是根据反射和CodeDom完成的。最后交由CodeDom动态编译……根据不同的实体,所创建的程序集也不相同。所编译成功的程序集是临时存放在内存里,所以QuicklyPutter类用白色表示。具体代码如下:
1
using System;
2
using System.Collections.Generic;
3
using System.Data.Common;
4
using System.Reflection;
5![]()
6
using Net.AfritXia.Data.Mapping;
7![]()
8
namespace Net.AfritXia.Data
9
{
10
partial class SQLHelper
11
{
12
public void PutEntityProperties<T>(T entity, DbDataReader dr) where T : class
13
{
14
// 获取设置器
15
IEntityPropertyPutter<T> putter = EntityPropertyPutterFactory.Create<T>(entity, this.IncludeDebugInformation);
16![]()
17
if (putter == null)
18
throw new NullReferenceException(@"设置器为空( Null Putter )");
19![]()
20
try
21
{
22
// 设置实体属性
23
putter.PutEntityProperties(entity, dr);
24
}
25
catch (Exception ex)
26
{
27
string errorMessage = null;
28
29
// 定义异常信息格式
30
errorMessage = @"从数据库字段{0} 读取值并赋给属性{1} 时出错(实体类型: {2})";
31
// 格式化信息
32
errorMessage = String.Format(errorMessage, putter.CurrentDBColName, putter.CurrentPropName, putter.EntityTypeName);
33![]()
34
// 抛出异常
35
throw new Exception(errorMessage, ex);
36
}
37
}
38
}
39
}
40
设置器工厂类EntityPropertyPutterFactory:
1
using System;
2
using System.Collections;
3
using System.Reflection;
4![]()
5
namespace Net.AfritXia.Data
6
{
7
/// <summary>
8
/// 实体属性设置器工厂类
9
/// </summary>
10
internal sealed class EntityPropertyPutterFactory
11
{
12
// 设置器字典
13
private static readonly Hashtable g_putterHash = Hashtable.Synchronized(new Hashtable());
14![]()
15
/// <summary>
16
/// 创建实体属性设置器
17
/// </summary>
18
/// <typeparam name="T">实体类型模版</typeparam>
19
/// <param name="fromEntity">实体</param>
20
/// <param name="includeDebugInfo">是否包含调试信息</param>
21
/// <returns></returns>
22
public static IEntityPropertyPutter<T> Create<T>(T fromEntity, bool includeDebugInfo) where T : class
23
{
24
if (fromEntity == null)
25
return null;
26![]()
27
// 如果实体本身已经实现了 IEntityPropertyPutter<T> 接口,
28
// 则直接返回
29
if (fromEntity is IEntityPropertyPutter<T>)
30
return (IEntityPropertyPutter<T>)fromEntity;
31![]()
32
IEntityPropertyPutter<T> putter = null;
33![]()
34
// 获取字典关键字
35
string hashKey = fromEntity.GetType().FullName;
36![]()
37
if (g_putterHash.ContainsKey(hashKey))
38
{
39
// 从字典中获取设置器
40
putter = g_putterHash[hashKey] as IEntityPropertyPutter<T>;
41
}
42
else
43
{
44
EntityPropertyPutterMaker maker = null;
45![]()
46
// 创建构建器
47
maker = new EntityPropertyPutterMaker();
48
// 是否包含调试信息
49
maker.IncludeDebugInformation = includeDebugInfo;
50![]()
51
// 新建应用程序集
52
putter = maker.Make<T>();
53
// 保存应用设置器到字典
54
g_putterHash.Add(hashKey, putter);
55
}
56![]()
57
return putter;
58
}
59
}
60
}
构建器EntityPropertyPutterMaker:
1
#undef _Debug // 用于调试
2![]()
3
using System;
4
using System.CodeDom;
5
using System.Collections.Specialized;
6
using System.CodeDom.Compiler;
7
using System.Data.Common;
8
#if _Debug
9
using System.IO;
10
#endif
11
using System.Reflection;
12![]()
13
using Microsoft.CSharp;
14![]()
15
using Net.AfritXia.Data.Mapping;
16![]()
17
namespace Net.AfritXia.Data
18
{
19
/// <summary>
20
/// 构建实体属性设置器
21
/// </summary>
22
internal sealed class EntityPropertyPutterMaker
23
{
24
// 默认名称空间
25
private const string DefaultNamespace = "Net.AfritXia.Data._AutoCode";
26
// QuicklyPutter 类名称
27
private const string QuicklyPutterClassName = "QuicklyPutter";
28![]()
29
// 包含调试信息
30
private bool m_includeDebugInfo = false;
31![]()
32
类构造器
40![]()
41
/// <summary>
42
/// 设置或获取是否包含调试信息
43
/// </summary>
44
public bool IncludeDebugInformation
45
{
46
set
47
{
48
this.m_includeDebugInfo = value;
49
}
50![]()
51
get
52
{
53
return this.m_includeDebugInfo;
54
}
55
}
56![]()
57
/// <summary>
58
/// 构建实体属性设置器
59
/// </summary>
60
/// <typeparam name="T">实体类型模版</typeparam>
61
/// <returns></returns>
62
public IEntityPropertyPutter<T> Make<T>() where T : class
63
{
64
// 创建一个可编译的单元
65
CodeCompileUnit compileUnit = this.MakeCompileUnit();
66
// 创建名称空间
67
CodeNamespace namespace_code = this.MakeNamespace();
68
// 定义类
69
CodeTypeDeclaration class_code = this.MakeClass<T>();
70
// 创建 PutEntityProperties 方法
71
CodeMemberMethod method_code = this.MakeMethod<T>();
72![]()
73
// 添加方法到类
74
class_code.Members.Add(method_code);
75
// 添加类到名称空间
76
namespace_code.Types.Add(class_code);
77
// 添加名称空间到编译单元
78
compileUnit.Namespaces.Add(namespace_code);
79![]()
80
// 创建 C# 编译器
81
CodeDomProvider provider = CodeDomProvider.CreateProvider("CSharp");
82
// 创建编译参数
83
CompilerParameters options = new CompilerParameters();
84![]()
85
// 添加对 System.dll 的引用
86
options.ReferencedAssemblies.Add("System.dll");
87
// 添加对 System.Data.dll 的引用
88
options.ReferencedAssemblies.Add("System.Data.dll");
89
// 添加对该项目的引用
90
options.ReferencedAssemblies.Add(this.GetType().Assembly.Location);
91
// 添加对实体项目的引用
92
options.ReferencedAssemblies.Add(typeof(T).Assembly.Location);
93
// 只在内存中编译
94
options.GenerateInMemory = true;
95![]()
96
#if _Debug
97
string srcFilePath = null;
98![]()
99
srcFilePath = @"C:\{0}_{1}.cs";
100
srcFilePath = String.Format(srcFilePath, typeof(T).Name, QuicklyPutterClassName);
101![]()
102
// 源文件输出流
103
StreamWriter srcOutput = new StreamWriter(srcFilePath, false);
104
// 写出源文件
105
provider.GenerateCodeFromCompileUnit(compileUnit, srcOutput, new CodeGeneratorOptions());
106![]()
107
srcOutput.Flush();
108
srcOutput.Close();
109
#endif
110![]()
111
// 编译并获取编译结果
112
CompilerResults compileResult = provider.CompileAssemblyFromDom(options, compileUnit);
113![]()
114
// 编译失败则抛出异常
115
if (compileResult.NativeCompilerReturnValue != 0)
116
throw new Exception("编译失败 ( Compile Failed )");
117![]()
118
// 创建设置器
119
object putter = compileResult.CompiledAssembly.CreateInstance(DefaultNamespace + "." + QuicklyPutterClassName);
120![]()
121
return (IEntityPropertyPutter<T>)putter;
122
}
123![]()
124
/// <summary>
125
/// 构建可编译单元
126
/// </summary>
127
/// <returns></returns>
128
private CodeCompileUnit MakeCompileUnit()
129
{
130
// 创建一个可编译的单元
131
return new CodeCompileUnit();
132
}
133![]()
134
/// <summary>
135
/// 构建名称空间
136
/// </summary>
137
/// <returns></returns>
138
private CodeNamespace MakeNamespace()
139
{
140
// 创建名称空间
141
return new CodeNamespace(DefaultNamespace);
142
}
143![]()
144
/// <summary>
145
/// 构建 QuicklyPutter 类
146
/// </summary>
147
/// <returns></returns>
148
private CodeTypeDeclaration MakeClass<T>() where T : class
149
{
150
// 定义 QuicklyPutter 类
151
CodeTypeDeclaration class_code = new CodeTypeDeclaration(QuicklyPutterClassName);
152![]()
153
// 令该类实现 IEntityPropertyPutter<T> 接口
154
class_code.BaseTypes.Add(typeof(IEntityPropertyPutter<T>));
155![]()
156
// 添加 EntityTypeName 属性
157
class_code = this.MakeEntityTypeNameProperty<T>(class_code);
158
// 添加 CurrentPropName 属性
159
class_code = this.MakeCurrentPropNameProperty(class_code);
160
// 添加 CurrentDBColName 属性
161
class_code = this.MakeCurrentDBColNameProperty(class_code);
162![]()
163
return class_code;
164
}
165![]()
166
/// <summary>
167
/// 构建 EntityTypeName 属性
168
/// </summary>
169
/// <typeparam name="T">实体类型模版</typeparam>
170
/// <param name="targetClass">目标代码</param>
171
/// <returns></returns>
172
private CodeTypeDeclaration MakeEntityTypeNameProperty<T>(CodeTypeDeclaration targetClass) where T : class
173
{
174
if (targetClass == null)
175
throw new ArgumentNullException("targetClass");
176![]()
177
/*
178
* 以下代码将生成
179
*
180
* public string EntityTypeName
181
* {
182
* get
183
* {
184
* return 实体类型名称字符串
185
* }
186
* }
187
*
188
*
189
*/
190![]()
191
// EntityTypeName 属性
192
CodeMemberProperty entityTypeNameProp_code = null;
193
194
// 创建属性
195
entityTypeNameProp_code = new CodeMemberProperty();
196
// 定义为公共属性
197
entityTypeNameProp_code.Attributes = MemberAttributes.Public;
198
// 返回字符串类型
199
entityTypeNameProp_code.Type = new CodeTypeReference(typeof(string));
200
// 属性名称
201
entityTypeNameProp_code.Name = "EntityTypeName";
202
// 返回语句
203
entityTypeNameProp_code.GetStatements.Add(
204
new CodeMethodReturnStatement(new CodePrimitiveExpression(typeof(T).Name)));
205![]()
206
// 添加属性到类
207
targetClass.Members.Add(entityTypeNameProp_code);
208![]()
209
return targetClass;
210
}
211![]()
212
/// <summary>
213
/// 构建 CurrentPropName 属性
214
/// </summary>
215
/// <param name="targetClass">目标类代码</param>
216
/// <returns></returns>
217
private CodeTypeDeclaration MakeCurrentPropNameProperty(CodeTypeDeclaration targetClass)
218
{
219
if (targetClass == null)
220
throw new ArgumentNullException("targetClass");
221![]()
222
/*
223
* 以下代码将生成
224
*
225
* private string m_currPropName;
226
*
227
* public string CurrentPropName
228
* {
229
* get
230
* {
231
* return this.m_currPropName;
232
* }
233
* }
234
*
235
*/
236![]()
237
// 变量名称
238
const string VaribleName = "m_currPropName";
239![]()
240
// m_currPropName
241
CodeMemberField m_currPropName_code = null;
242![]()
243
// 创建字段
244
m_currPropName_code = new CodeMemberField();
245
// 定义为私有成员
246
m_currPropName_code.Attributes = MemberAttributes.Private;
247
// 创建变量
248
m_currPropName_code = new CodeMemberField(typeof(string), VaribleName);
249![]()
250
// 添加成员到类
251
targetClass.Members.Add(m_currPropName_code);
252![]()
253
// CurrentPropName
254
CodeMemberProperty currPropName_code = null;
255
256
// 创建属性
257
currPropName_code = new CodeMemberProperty();
258
// 定义为公共属性
259
currPropName_code.Attributes = MemberAttributes.Public;
260
// 返回字符串类型
261
currPropName_code.Type = new CodeTypeReference(typeof(string));
262
// 属性名称
263
currPropName_code.Name = "CurrentPropName";
264
// get 返回语句
265
currPropName_code.GetStatements.Add(
266
new CodeMethodReturnStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), VaribleName)));
267![]()
268
// 添加属性到类
269
targetClass.Members.Add(currPropName_code);
270![]()
271
return targetClass;
272
}
273![]()
274
/// <summary>
275
/// 构建 CurrentDBColName 属性
276
/// </summary>
277
/// <param name="targetClass">父级类</param>
278
/// <returns></returns>
279
private CodeTypeDeclaration MakeCurrentDBColNameProperty(CodeTypeDeclaration targetClass)
280
{
281
if (targetClass == null)
282
throw new ArgumentNullException("targetClass");
283![]()
284
/*
285
* 以下代码将生成
286
*
287
* private string m_currDBColName;
288
*
289
* public string CurrentDBColName
290
* {
291
* get
292
* {
293
* return this.m_currDBColName;
294
* }
295
* }
296
*
297
*/
298![]()
299
// 变量名称
300
const string VaribleName = "m_currDBColName";
301
// m_currDBColName
302
CodeMemberField m_currDBColName_code = null;
303![]()
304
// 创建字段
305
m_currDBColName_code = new CodeMemberField();
306
// 定义为私有成员
307
m_currDBColName_code.Attributes = MemberAttributes.Private;
308
// 创建变量
309
m_currDBColName_code = new CodeMemberField(typeof(string), VaribleName);
310![]()
311
// 添加成员到类
312
targetClass.Members.Add(m_currDBColName_code);
313![]()
314
// CurrentDBColName
315
CodeMemberProperty currDBCol_code = null;
316![]()
317
// 创建属性
318
currDBCol_code = new CodeMemberProperty();
319
// 定义为公共属性
320
currDBCol_code.Attributes = MemberAttributes.Public;
321
// 返回字符串类型
322
currDBCol_code.Type = new CodeTypeReference(typeof(string));
323
// 属性名称
324
currDBCol_code.Name = "CurrentDBColName";
325
// get 返回语句
326
currDBCol_code.GetStatements.Add(
327
new CodeMethodReturnStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), "m_currDBColName")));
328![]()
329
// 添加属性到类
330
targetClass.Members.Add(currDBCol_code);
331![]()
332
return targetClass;
333
}
334![]()
335
/// <summary>
336
/// 构建 PutEntityProperties 方法
337
/// </summary>
338
/// <typeparam name="T"></typeparam>
339
/// <param name="fromEntity"></param>
340
/// <returns></returns>
341
private CodeMemberMethod MakeMethod<T>() where T : class
342
{
343
// PutObjectProperties 方法
344
CodeMemberMethod method_code = null;
345
346
// 创建方法
347
method_code = new CodeMemberMethod();
348
// 定义为公共方法
349
method_code.Attributes = MemberAttributes.Public;
350
// 返回类型
351
method_code.ReturnType = new CodeTypeReference(typeof(void));
352
// 方法名称
353
method_code.Name = "PutEntityProperties";
354
// 添加参数 entity
355
method_code.Parameters.Add(new CodeParameterDeclarationExpression(typeof(T), "entity"));
356
// 添加参数 dr
357
method_code.Parameters.Add(new CodeParameterDeclarationExpression(typeof(DbDataReader), "dr"));
358![]()
359
// 获取实体类型
360
Type objType = typeof(T);
361![]()
362
// 获取 DataTable 属性标记
363
object[] tabAttrList = objType.GetCustomAttributes(typeof(DataTableAttribute), false);
364![]()
365
if (tabAttrList == null || tabAttrList.Length <= 0)
366
{
367
throw new MappingException(
368
String.Format(@"类 {0} 未标记 DataTable 属性 ( Unlabeled [DataTable] Attribute On Class {0} )", objType.Name));
369
}
370![]()
371
// 获取属性信息
372
PropertyInfo[] propInfoList = objType.GetProperties();
373![]()
374
if (propInfoList == null || propInfoList.Length <= 0)
375
return null;
376![]()
377
foreach (PropertyInfo propInfo in propInfoList)
378
{
379
object[] colAttrList = propInfo.GetCustomAttributes(typeof(DataColumnAttribute), false);
380![]()
381
// 未标记 DataColumn 属性
382
if (colAttrList == null || colAttrList.Length <= 0)
383
continue;
384![]()
385
// 获取数据列属性
386
DataColumnAttribute colAttr = colAttrList[0] as DataColumnAttribute;
387![]()
388
// 创建方法内容
389
method_code = this.MakeMethodContent(method_code, propInfo, colAttr, this.IncludeDebugInformation);
390
}
391![]()
392
return method_code;
393
}
394![]()
395
/// <summary>
396
/// 构建 PutEntityProperties 方法内容
397
/// </summary>
398
/// <param name="targetMethod"></param>
399
/// <param name="prop"></param>
400
/// <param name="attr"></param>
401
/// <param name="includeDebugInfo"></param>
402
/// <returns></returns>
403
private CodeMemberMethod MakeMethodContent(CodeMemberMethod targetMethod, PropertyInfo prop, DataColumnAttribute attr, bool includeDebugInfo)
404
{
405
if (targetMethod == null)
406
throw new ArgumentNullException("targetMethod");
407![]()
408
if (attr == null)
409
throw new ArgumentNullException("attr");
410![]()
411
// 实体变量名称 entity
412
string varEntityName = targetMethod.Parameters[0].Name;
413
// 数据源变量名称 dr
414
string varDrName = targetMethod.Parameters[1].Name;
415![]()
416
// entity 属性名称
417
string varEntityPropName = String.Format(@"{0}.{1}", varEntityName, prop.Name);
418
// dr 属性名称
419
string varDrPropName = String.Format(@"{0}[""{1}""]", varDrName, attr.Name);
420![]()
421
// 创建变量
422
CodeVariableReferenceExpression entityProp_code = new CodeVariableReferenceExpression(varEntityPropName);
423
// 创建值
424
CodeVariableReferenceExpression dr_code = new CodeVariableReferenceExpression(varDrPropName);
425![]()
426
// 包含调试信息
427
if (includeDebugInfo)
428
{
429
// this.m_currPropName = entity.Prop
430
targetMethod.Statements.Add(new CodeAssignStatement(
431
new CodeVariableReferenceExpression("this.m_currPropName"),
432
new CodePrimitiveExpression(prop.Name)));
433![]()
434
// this.m_currDBColName = attributeName
435
targetMethod.Statements.Add(new CodeAssignStatement(
436
new CodeVariableReferenceExpression("this.m_currDBColName"),
437
new CodePrimitiveExpression(attr.Name)));
438
}
439![]()
440
if (attr.IsNullable)
441
{
442
/*
443
* 以下代码生成的是条件判断代码
444
*
445
* if (dr["
"] != DBNull.Value) {
446
* entity.Prop = dr["
"];
447
* }
448
*
449
*/
450![]()
451
CodeConditionStatement if_code = new CodeConditionStatement();
452![]()
453
// if (dr["
"] != DBNull.Value)
454
if_code.Condition = new CodeBinaryOperatorExpression(
455
new CodeVariableReferenceExpression(varDrPropName),
456
CodeBinaryOperatorType.IdentityInequality,
457
new CodeVariableReferenceExpression("System.DBNull.Value"));
458![]()
459
// entity.Prop = dr["
"];
460
if_code.TrueStatements.Add(new CodeAssignStatement(
461
entityProp_code,
462
new CodeCastExpression(prop.PropertyType, dr_code)));
463![]()
464
targetMethod.Statements.Add(if_code);
465
}
466
else
467
{
468
// entity.Prop = dr["
"];
469
targetMethod.Statements.Add(new CodeAssignStatement(
470
entityProp_code,
471
new CodeCastExpression(prop.PropertyType, dr_code)));
472
}
473![]()
474
return targetMethod;
475
}
476
}
477
}
代码时序图如下:
具体代码可以参考:
Net.AfritXia.Data/IEntityPropertyPutter.cs
Net.AfritXia.Data/EntityPropertyPutterFactory.cs
Net.AfritXia.Data/EntityPropertyPutterMaker.cs
TestProj/UnitTest_Putter.cs(可以运行该测试文件)
posted on 2008-07-06 13:53 AfritXia 阅读(1222) 评论(10) 编辑 收藏
使用CodeDom提高ORM性能
下载本文代码:https://files.cnblogs.com/afritxia2008/WebTest.rar(请使用 Visual Studio 2008 打开)
在进行讨论之前,我假设读者已经了解.NET反射、自定义属性、CodeDom这些技术。并接触过ORM框架源码,如果对ORM并不了解,可以参考:http://www.cnblogs.com/xdesigner/archive/2008/06/24/1228702.html。在这篇文章中,我们主要讨论通过CodeDom提高ORM读取数据的性能问题。
ORM(Object/Relation Mapping对象-关系数据库映射)其中的一个功能是将数据源数据赋值给实体。实现方法是利用自定义属性和.NET反射机制。例如:

2

3

4

5

6

7

8

9


10

11

12

13

14

15

16

17

18


19

20

21

22

23

24

25

26

27


28

29

30

31

32

33

34

35

36


37

38

DataColumn是自定义的属性类,代码并不复杂所以在这里也就省略了。接下来需要通过反射读取自定义属性,并赋值。代码如下:

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

以上代码实现了读取数据源数据并向实体赋值的功能。但这样做速度非常慢,因为每读取一条数据库记录,每读取一个数据字段并向实体赋值的时候,都必须进行一次反射操作。数据量越大,且数据字段或实体属性越多,那么速度就越慢!在以上代码中,对实体的反射操作,其目的就是赋值。可以被等价的语句所替代:
entity.Prop = dr[“Porp”];
用简单的赋值语句肯定要比反射的速度快很多,而大数据量和多数据库字段对其影响也不是很大。不过需要注意的是因为每一个实体的具体属性不相同,所以赋值过程也是不相同的。例如:
News实体赋值代码:

2

3

4

5

6

7

8

9

10

11

12

13

14

15


2

3

4

5

6

7

8

9

10

11

12

13

14

News与User所具备的属性不同,所以赋值过程,也不相同!但毫无疑问,使用直接赋值的方法是速度最快的!试想一下,假如在做反射的时候不是直接赋值,而是根据自定义属性,动态的生成赋值代码,编译以后临时保存起来。那么以后再进行赋值操作的时候,直接调用这个编译好的赋值代码,不就可以大大提升程序性能了么?有没有一个办法可以自动建立类似上面这样的代码呢?我们可以考虑使用反射和CodeDom技术。

首先为了解决不同实体的不同的赋值过程,我们需要建立一个接口:IEntityPropertyPutter。在该接口中的PutEntityProperties函数负责真正的赋值逻辑。在赋值的过程中会调用IEntityPropertyPutter的具体实现类的实例。具体类图如下:

EntityPropertyPutterFactory工厂类负责创建IEntityPropertyPutter接口具体实现类的实例。首先该工厂类会从缓存中获取IEntityPropertyPutter接口实例,如果该实例为空(还没有被创建),那么该工厂类会调用EntityPropertyPutterMaker构建者类创建实例(Entity实体类本身也可以直接实现IEntityPropertyPutter接口,来加快程序的运行速度)。在构建者内部会动态创建新的程序集(Assembly),在该程序集中只存在一个QuicklyPutter类。在QuicklyPutter类中描述了具体的赋值逻辑,这些逻辑编码则是根据反射和CodeDom完成的。最后交由CodeDom动态编译……根据不同的实体,所创建的程序集也不相同。所编译成功的程序集是临时存放在内存里,所以QuicklyPutter类用白色表示。具体代码如下:

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

设置器工厂类EntityPropertyPutterFactory:

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

构建器EntityPropertyPutterMaker:

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

248

249

250

251

252

253

254

255

256

257

258

259

260

261

262

263

264

265

266

267

268

269

270

271

272

273

274

275

276

277

278

279

280

281

282

283

284

285

286

287

288

289

290

291

292

293

294

295

296

297

298

299

300

301

302

303

304

305

306

307

308

309

310

311

312

313

314

315

316

317

318

319

320

321

322

323

324

325

326

327

328

329

330

331

332

333

334

335

336

337

338

339

340

341

342

343

344

345

346

347

348

349

350

351

352

353

354

355

356

357

358

359

360

361

362

363

364

365

366

367

368

369

370

371

372

373

374

375

376

377

378

379

380

381

382

383

384

385

386

387

388

389

390

391

392

393

394

395

396

397

398

399

400

401

402

403

404

405

406

407

408

409

410

411

412

413

414

415

416

417

418

419

420

421

422

423

424

425

426

427

428

429

430

431

432

433

434

435

436

437

438

439

440

441

442

443

444

445


446


447

448

449

450

451

452

453


454

455

456

457

458

459


460

461

462

463

464

465

466

467

468


469

470

471

472

473

474

475

476

477

代码时序图如下:

具体代码可以参考:
Net.AfritXia.Data/IEntityPropertyPutter.cs
Net.AfritXia.Data/EntityPropertyPutterFactory.cs
Net.AfritXia.Data/EntityPropertyPutterMaker.cs
TestProj/UnitTest_Putter.cs(可以运行该测试文件)
posted on 2008-07-06 13:53 AfritXia 阅读(1222) 评论(10) 编辑 收藏