下载本文代码: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
DataColumn是自定义的属性类,代码并不复杂所以在这里也就省略了。接下来需要通过反射读取自定义属性,并赋值。代码如下:
2
以上代码实现了读取数据源数据并向实体赋值的功能。但这样做速度非常慢,因为每读取一条数据库记录,每读取一个数据字段并向实体赋值的时候,都必须进行一次反射操作。数据量越大,且数据字段或实体属性越多,那么速度就越慢!在以上代码中,对实体的反射操作,其目的就是赋值。可以被等价的语句所替代:
entity.Prop = dr[“Porp”];
用简单的赋值语句肯定要比反射的速度快很多,而大数据量和多数据库字段对其影响也不是很大。不过需要注意的是因为每一个实体的具体属性不相同,所以赋值过程也是不相同的。例如:
News实体赋值代码:
2
User实体赋值代码:
2
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
设置器工厂类EntityPropertyPutterFactory:
2
3
4
5
6
构建器EntityPropertyPutterMaker:
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
代码时序图如下:
具体代码可以参考:
Net.AfritXia.Data/IEntityPropertyPutter.cs
Net.AfritXia.Data/EntityPropertyPutterFactory.cs
Net.AfritXia.Data/EntityPropertyPutterMaker.cs
TestProj/UnitTest_Putter.cs(可以运行该测试文件)