南京袁永福 报表软件 C#.NET ASP.NET

南京PX(对二甲苯)项目,这是个问题。
三位一体的电子表单工具,同时支持WinForm表单,HTML表单和XSLT表单,表单模板在B/S和C/S下具有相同的用户体验. ---- C#.NET新型报表工具,支持WinForm和ASP.NET,WEB报表工具.
袁永福 江西九江人 2001年东南大学动力系毕业 电子邮箱:yyf9989@hotmail.com QQ群:41118220

C#发现之旅第十二讲 基于反射和动态编译的快速ORM框架

系列课程说明

    为了让大家更深入的了解和使用C#,我们将开始这一系列的主题为“C#发现之旅”的技术讲座。考虑到各位大多是进行WEB数据库开发的,而所谓发现就是发现我们所不熟悉的领域,因此本系列讲座内容将是C#在WEB数据库开发以外的应用。目前规划的主要内容是图形开发和XML开发,并计划编排了多个课程。在未来的C#发现之旅中,我们按照由浅入深,循序渐进的步骤,一起探索和发现C#的其他未知的领域,更深入的理解和掌握使用C#进行软件开发,拓宽我们的视野,增强我们的软件开发综合能力。

 

课程说明

在上次课程中,我们使用.NET框架提供的特性和反射来创建了一个简单的ORM框架,在这个框架中,由于频繁的进行比较慢的反射操作,因此ORM框架运行速度比较慢,在操作大批量的数据时性能比较差。在本次课程中我们在原先的基础上加上动态编译的技术来实现快速ORM框架。快速ORM框架将不会有性能问题。点击下载本课程的C#2005的演示代码 http://files.cnblogs.com/xdesigner/MyFastORM.zip 

动态编译技术

所谓动态编译技术就是应用程序在运行时,程序内部自动的生成C#代码,然后调用.NET框架提供的C#程序编译器生成临时的程序集,然后将临时程序集加载到应用程序域中动态的调用其中的对象模块。

动态编译技术内部调用了代码生成器。以前我们是在编程时使用代码生成器生成代码文档,然后添加到C#工程中,然后进行整体编译,此时我们是手工的使用代码生成器,这个过程可以称为静态编译。而动态编译技术却是将这个过程自动化了,而且调用代码生成器生成代码文本的过程放置在软件运行时执行。

动态编译技术能同时兼顾灵活性和性能。微软.NET框架本身也有动态编译技术的应用,比如XML序列化和反序列化,ASP.NET框架处理ASPX文件等等。

一般而言使用动态编译技术的过程可以为

1.       应用程序需要调用动态编译功能,则收集一些参数,然后调用动态编译模块。

2.       动态编译模块内部有一个全局的临时编译的程序集的缓存列表,若根据应用程序传递的参数可以在缓存列表中找到相匹配的临时程序集则直接返回这个程序集对象。

3.       动态编译模块收集参数,然后调用内置的代码生成器生成代码字符串。

4.       动态编译模块调用微软.NET框架提供的C#代码编译器,生成一个临时的程序集对象。具体就是调用Microsoft.CSharp.CSharpCodeProvider 提供的方法。在这个过程中,程序将会在磁盘上生成若干临时文件,这个过程会受到微软.NET框架的安全设置的影响。

5.       将临时编译生成的程序集对象保存到全局的临时程序集的缓存列表,然后向应用程序返回这个临时程序集,而应用程序将会使用反射的手段来调用临时程序集提供的功能。

动态编译技术中生成的临时程序集和我们使用开发工具生成的程序集没有差别,运行速度是一样的快。因此动态编译技术除了能实现灵活的功能外还提供良好的性能。

我们要使用动态编译技术,首先得看要实现的功能是否灵活多变,若我们要实现的功能比较简单,使用静态编译技术就足够了,那我们就用不着使用动态编译技术。若功能非常复杂,无法使用代码生成器生成代码来实现它,则也不能使用动态编译技术。

注意,动态编译技术会在磁盘中生成临时文件,因此.NET框架的安全设置会影响到动态编译技术的正常运行,而且使用该技术的程序会生成C#代码并保存到临时文件,然后调用.NET框架的C#代码编译器生成临时程序集,而恶意软件会在这两个步骤间隙迅速的修改C#代码文件并插入恶意代码,对此动态编译技术无法判别。

快速ORM框架整体设计

在这里我们将以上节课的ORM框架为基础,对它进行改造,加入动态编译技术来打造一个快速ORM框架。首先我们还得使用BindTableAttributeBindFieldAttribute特性来描述实体类型和数据库的绑定信息。于是我们上节课使用的演示用的实体类型DB_Employees就原封不动的用到现在。该实体类型的代码为

[System.Serializable()]

[BindTable("Employees")]

public class DB_Employees

{  

 

    /// <summary>

    /// 人员全名

    /// </summary>

    public string FullName

    {

        get

        {

            return this.LastName + this.FirstName ;

        }

    }

 

    #region 定义数据库字段变量及属性 //////////////////////////////////////////

    ///<summary>

    /// 字段值 EmployeeID

    ///</summary>

    private System.Int32 m_EmployeeID = 0 ;

    ///<summary>

    /// 字段值 EmployeeID

    ///</summary>

    [BindField("EmployeeID" , Key = true )]

    public System.Int32 EmployeeID

    {

        get

        {

            return m_EmployeeID ;

        }

        set

        {

            m_EmployeeID = value;

        }

    }

    ///<summary>

    /// 字段值 LastName

    ///</summary>

    private System.String m_LastName = null ;

    ///<summary>

    /// 字段值 LastName

    ///</summary>

    [BindField("LastName")]

    public System.String LastName

    {

        get

        {

            return m_LastName ;

        }

        set

        {

            m_LastName = value;

        }

    }

    其他字段……………..

    #endregion

}// 数据库操作类 DB_Employees 定义结束

我们设计快速ORM框架的程序结构如图所示

框架中包含了一个实体类型注册列表,列表中包含了实体类型和相应的RecordORMHelper对象。应用程序在使用框架前必须注册实体类型,向实体类型注册列表添加将要操作的实体类型,应用程序注册实体列表时不会立即导致代码的自动生成和编译。

我们首先定义了一个基础的抽象类型RecordORMHelper,该类型定义了处理实体类型和数据库的映射操作,主要包括从一个System.Data.IDataReader读取数据并创建实体类型,为新增,修改和删除数据库记录而初始化System.Data.IDbCommand对象等等。该类型是快速ORM框架的核心处理对象,数据库处理模块将使用RecordORMHelper来作为统一的接口来处理实体类和数据库的映射操作。

代码生成器分析实体类型列表中所有没有处理的实体类型,获得其中的使用BindTableAttributeBindFieldAttribute特性保存的对象和数据库的映射关系,针对每一个实体类型创建一个Class的代码,该Class是从RecordORMHelper上派生的,并实现了RecordORMHelper预留的接口。代码生成器可以同时为多个实体类型创建C#源代码,此时一份C#源代码中包含了多个从RecordORMHelper派生的Class类。

C#代码编译器接受代码生成器生成的代码,进行编译生成一个临时程序集,该程序集中就包含了多个派生自RecordORMHelper的类型,每一个类型都专门处理某种实体类型。编译器在编译程序是需要指定所引用的其他程序集,这里包括 mscorlib.dllSystem.dllSystem.Data.dll,此外还包括类型RecordORMHelper所在的程序集,也就是包括快速ORM框架的程序集,这里的程序集不一定是DLL格式,也可能是EXE格式。于是我们编译程序时引用了一个EXE,这种操作在使用VS.NET等开发工具时是禁止的。从这里可以看出,一些使用VS.NET开发工具所不可能实现的功能我们可以编程使用.NET框架来实现。

.NET框架自己包含了一个C#代码编译器,它的文件名是CSC.EXE,在.NET框架的安装目录下,在笔者的电脑中其路径是 C:\WINDOWS\Microsoft.NET\Framework\v1.1.4322\csc.exe 或者 C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\csc.exe ,它是一个基于命令行的编辑器,能将C#代码编译生成EXE或者DLL文件。关于C#代码编译器可参考MSDN中的相关说明。

快速ORM框架的控制模块接受应用程序的请求,首先检查实体类型注册列表,若列表中没有找到相应的RecordORMHelper对象,则调用代码生成器生成代码,然后调用C#代码编译器编译生成临时的程序集,然后加载临时程序集,使用反射(调用System.Reflection.Assembly.GetType函数)找到其中所有的的RecordORMHelper类型,然后根据类型动态的创建对象实例,并填充到实体类型注册列表。最后调用RecordORMHelper预定的接口来实现ORM功能。

若我们在使用快速ORM框架前,将所有可能要用到的实体对象类型添加到实体类型注册列表中,则快速ORM框架会生成一个临时程序集,但我们是陆陆续续的往ORM框架注册实体对象类型,则快速ORM框架内部可能会多次调用代码生成器和代码编译器来生成临时程序集,这样最后会生成多个临时程序集。一般的建议在使用框架前将向ORM框架注册所有可能用到的实体对象类型,这样框架只会执行一次动态编译的操作。

基础类型RecordORMHelper

本类型属于ORM框架的底层模块。其代码为

public abstract class RecordORMHelper

{

     /// <summary>

     /// 对象操作的数据表名称

     /// </summary>

     public abstract string TableName

     {

         get ;

     }

 

     /// <summary>

     /// 从数据读取器读取数据创建一个记录对象

     /// </summary>

     /// <param name="reader">数据读取器</param>

     /// <returns>读取的数据</returns>

     public object ReadRecord( System.Data.IDataReader reader )

     {

         int[] indexs = GetFieldIndexs( reader );

         return InnerReadRecord( reader  ,indexs );

     }

 

     /// <summary>

     /// 从数据读取器读取数据创建若干个记录对象

     /// </summary>

     /// <param name="reader">数据读取器</param>

     /// <param name="MaxRecordCount">允许读取的最大的记录个数,为0则无限制</param>

     /// <returns>读取的数据对象列表</returns>

     public System.Collections.ArrayList ReadRecords( System.Data.IDataReader reader , int MaxRecordCount )

     {

         System.Collections.ArrayList list = new System.Collections.ArrayList();

         int[] indexs = GetFieldIndexs( reader );

         while( reader.Read())

         {

              object record = InnerReadRecord( reader , indexs );

              list.Add( record );

              if( MaxRecordCount > 0 && list.Count >= MaxRecordCount )

              {

                   break;

              }

         }//while

         return list ;

     }

 

     /// <summary>

     /// 从一个数据读取器中读取一条记录对象,必须重载

     /// </summary>

     /// <param name="reader">数据读取器</param>

     /// <param name="FieldIndexs">字段序号列表</param>

     /// <returns>读取的记录对象</returns>

     protected abstract object InnerReadRecord( System.Data.IDataReader reader , int[] FieldIndexs );

    

 

     /// <summary>

     /// 为删除记录而初始化数据库命令对象

     /// </summary>

     /// <param name="cmd">数据库命令对象</param>

     /// <param name="objRecord">实体对象实例</param>

     /// <returns>添加的SQL参数个数</returns>

     public abstract int FillDeleteCommand( System.Data.IDbCommand cmd , object objRecord );

 

     /// <summary>

     /// 为插入记录而初始化数据库命令对象

     /// </summary>

     /// <param name="cmd">数据库命令对象</param>

     /// <param name="objRecord">实体对象实例</param>

     /// <returns>添加的SQL参数个数</returns>

     public abstract int FillInsertCommand( System.Data.IDbCommand cmd , object objRecord );

 

     /// <summary>

     /// 为更新数据库记录而初始化数据库命令对象

     /// </summary>

     /// <param name="cmd">数据库命令对象</param>

     /// <param name="objRecord">实体对象实例</param>

     /// <returns>添加的SQL参数个数</returns>

     public abstract int FillUpdateCommand( System.Data.IDbCommand cmd , object objRecord );

    

     /// <summary>

     /// 字段列表数组

     /// </summary>

     protected abstract string[] RecordFieldNames

     {

         get ;

     }

 

     /// <summary>

     /// 针对特定的数据读取器获得实体对象的各个属性对应的数据栏目的编号

     /// </summary>

     /// <param name="reader">数据读取器</param>

     /// <returns>编号列表</returns>

     private int[] GetFieldIndexs( System.Data.IDataReader reader )

     {

         if( reader == null )

         {

              throw new ArgumentNullException("reader");

         }

 

         string[] FieldNames = this.RecordFieldNames ;

 

         int[] indexs = new int[ FieldNames.Length ] ;

         int FieldCount = reader.FieldCount ;

         string[] names = new string[ FieldCount ] ;

         for( int iCount = 0 ; iCount < FieldCount ; iCount ++ )

         {

              names[ iCount ] = reader.GetName( iCount ) ;

         }

         for( int iCount = 0 ; iCount < indexs.Length ; iCount ++ )

         {

              indexs[ iCount ] = -1 ;

              string name = FieldNames[ iCount ] ;

              for( int iCount2 = 0 ; iCount2 < FieldCount ; iCount2 ++ )

              {

                   if( EqualsFieldName( name , names[ iCount2 ] ))

                   {

                       indexs[ iCount ] = iCount2 ;

                       break;

                   }

              }

         }

         for( int iCount = 0 ; iCount < FieldCount ; iCount ++ )

         {

              string name = reader.GetName( iCount );

              for( int iCount2 = 0 ; iCount2 < indexs.Length ; iCount2 ++ )

              {

                   if( EqualsFieldName( name , FieldNames[ iCount2 ] ))

                   {

                       indexs[ iCount2 ] = iCount ;

                   }

              }

         }

 

         return indexs ;

     }

 

     /// <summary>

     /// 连接多个字符串,各个字符串之间用逗号分隔,本函数会在动态生成的派生类中使用

     /// </summary>

     /// <param name="strs">字符串集合</param>

     /// <returns>连接所得的大字符串</returns>

     protected string ConcatStrings( System.Collections.IEnumerable strs )

     {

         System.Text.StringBuilder myStr = new System.Text.StringBuilder();

         foreach( string str in strs )

         {

              if( myStr.Length > 0 )

              {

                   myStr.Append(",");

              }

              myStr.Append( str );

         }//foreach

         return myStr.ToString();

     }

     /// <summary>

     /// 判断两个字段名是否等价

     /// </summary>

     /// <param name="name1">字段名1</param>

     /// <param name="name2">字段名2</param>

     /// <returns>true:两个字段名等价 false:字段名不相同</returns>

     private bool EqualsFieldName( string name1 , string name2 )

     {

         if( name1 == null || name2 == null )

         {

              throw new ArgumentNullException("name1 or name2");

         }

         name1 = name1.Trim();

         name2 = name2.Trim();

         // 进行不区分大小写的比较

         if( string.Compare( name1 , name2 , true ) == 0 )

         {

              return true ;

         }

         int index = name1.IndexOf(".");

         if( index > 0 )

         {

              name1 = name1.Substring( index + 1 ).Trim();

         }

         index = name2.IndexOf(".");

         if( index > 0 )

         {

              name2 = name2.Substring( index + 1 ).Trim();

         }

         return string.Compare( name1 , name2 , true ) == 0 ;

     }

 

     #region 从数据库读取的原始数据转换为指定数据类型的函数群,本函数会在动态生成的派生类中使用

 

     protected byte ConvertToByte( object v , byte DefaultValue )

     {

         if( v == null || DBNull.Value.Equals( v ))

              return DefaultValue ;

         else

              return Convert.ToByte( v );

     }

 

     protected sbyte ConvertToSByte( object v , sbyte DefaultValue )

     {

         if( v == null || DBNull.Value.Equals( v ))

              return DefaultValue ;

         else

              return Convert.ToSByte( v );

     }

     其他的 ConvertTo 函数

     #endregion

 

     /// <summary>

     /// 将日期数据转换为数据库中的格式,本函数会在动态生成的派生类中使用.

     /// </summary>

     /// <param name="Value">日期数据</param>

     /// <param name="Format">保存格式化字符串</param>

     /// <returns>转换后的数据</returns>

     protected object DateTimeToDBValue( DateTime Value , string Format )

     {

         if( Format != null || Format.Length > 0 )

         {

              return Value.ToString( Format );

         }

         else

         {

              return Value ;

         }

     }

 

}//public abstract class RecordORMHelper

在这个类型中,TableName属性返回该实体对象类型绑定的数据库名称,因此该属性值由BindTableAttribute特性指定,RecordFieldNames属性返回一个字符串数组,该数组列出了所有的绑定的字段的名称,也就是实体类型包含的所有的BindFieldAttribute指定的字段名称组成的数组。

实体类型注册列表

在快速ORM框架主模块MyFastORMFramework中定义了一个myRecordHelpers的变量

private static System.Collections.Hashtable myRecordHelpers = new System.Collections.Hashtable();

这个myRecordHelpers就是实体类型注册列表。该列表中键值就是实体对象类型,而它的数据值就是一个个动态生成的从RecordORMHelper派生的对象实例。我们定义了一个函数向该列表注册实体对象类型

public void RegisterType( Type t )

{

     if( myRecordHelpers.ContainsKey( t ) == false )

     {

         this.GetBindProperties( t );

         myRecordHelpers[ t ] = null ;

     }

}

这个过程很简单,就是向该列表的键值列表添加实体对象类型,这里调用了GetBindProperties函数,该函数内部会仔细检查实体对象类型是否符合快速ORM框架的要求,若不符合则会报错,因此这里调用GetBindProperties函数就是检查实体对象类型是否合格。

ORM框架操作数据库前都会查询实体类型注册列表获得所需的数据库操作帮助器,也就是调用函数GetHelepr,其代码为

private RecordORMHelper GetHelper( Type RecordType )

{

     RecordORMHelper helper = null ;

     if( myRecordHelpers.ContainsKey( RecordType ))

     {

         helper = ( RecordORMHelper ) myRecordHelpers[ RecordType ] ;

         if( helper != null )

         {

              return helper ;

         }

     }

     else

     {

         this.GetBindProperties( RecordType );

         myRecordHelpers[ RecordType ] = null;

     }

     BuildHelpers( null );

     helper = ( RecordORMHelper ) myRecordHelpers[ RecordType ] ;

     if( helper == null )

     {

         throw new ArgumentException("为类型 " + RecordType.FullName + " 初始化系统错误");

     }

     return helper ;

}

在这个函数中,参数就是实体对象类型,首先从注册列表中获得数据库操作帮助器,若没有找到则进行注册,然后调用BuildHelpers执行动态编译生成数据库操作帮助器。然后再尝试从注册列表中获得数据库操作帮助器。

ORM框架中,GetHelper函数会频繁的调用,因此使用实体对象类型注册列表可以提高系统性能。应用系统多次连续的调用RegisterType函数会导致类型注册列表中有多个类型对应的数据库操作帮助器是空的,而再BuildHelpers函数内部会对所有的没有设定数据库操作帮助器的实体对象类型执行动态编译的操作,能一下子生成多个数据库操作帮助器,这样能尽量减少动态编译的次数。

代码生成器

在动态编译框架中,代码生成器是非常重要的部分。没有代码生成器,动态编译框架成了无源之水,无米之炊了。代码生成器的主要工作就是使用反射解析数据库实体类的结构,分析其中的数据库绑定信息,然后使用字符串拼凑的操作来生成C#代码字符串。

要设计出代码生成器,首先的设计出其要输出的C#代码的结构,我们可以不使用那个基础的RecordORMHelper而完全依赖生成的C#代码来完成数据库的映射功能,不过即使用代码生成器,我们也得考虑到代码的重用,于是我们把一些通用的代码放到RecordORMHelper中,然后动态生成的C#类就继承自RecordORMHelper

ORM框架中还包含了一个IndentTextWriter的支持缩进的文本书写器,虽然我们可以完全使用字符串加号操作来生成代码文本,但使用IndentTextWriter能让工作更高效,生成的代码也便于人们阅读,这有利于代码生成器的调试和维护。在IndentTextWriter中,使用BeginGroup来开始缩进一段代码块,使用EndGroup来结束缩进一段代码块,使用WriteLine来输出一行代码文本。

在快速ORM框架中,代码生成器包含在函数MyFastORMFramework.GenerateCode中。现对其过程进行说明

启用命名参数

MyFastORMFramework中定义了NamedParameter属性用于决定是否启动命名参数。为了安全,代码生成器生成的SQL命令文本不会包含具体的数值,而是使用SQL命令参数的方式。若设置该属性,则启用命名参数,此时代码生成器生成SQL文本中使用“@参数名”来表示SQL命令参数占位,若没有设置该属性,则未启用命名参数,此时代码生成器生成的SQL文本中使用“?”来表示SQL命令参数占位。比如对于新增记录,若启用命令参数,则生成的SQL文本为“Insert Into Table ( Field1 , Field2 ) Values ( @Value1 , @Value2 )”,若不启用命名参数则生成的SQL文本为“Insert Into Table( Field1 , Field2 ) Values( ? , ? )”。

某些类型的数据库不支持无命名的参数,有些支持,因此本快速ORM框架提供了NamedParamter属性方法让使用者进行调整,使得快速ORM框架能适用于更多类型的数据库。

生成读取数据的代码

基础类型RecordORMHelper中函数 ReadRecord调用GetFieldIndexsInnerReadRecord函数从一个IDataReader中读取一行数据并创建一个实体类型的实例。GetFieldIndexs 函数用于获得一个整数数组,该数组的元素就是实体类各个属性对应的数据读取器的从0开始计算的字段栏目序号。例如对于属性 DB_Employees. EmployeeID,它是对象的第一个属性成员,其绑定的字段是“EmployeeID”。若数据读取器的第三个栏目,也就是对它调用IDataReader.GetName( 3 )的值是“employeeid,GetFieldIndexs函数返回的数组第一个元素值就是3。若数据读取器没有找到和“EmployeeID”相匹配的栏目,则GetFieldIndexs函数返回的数组的第一个元素值是-1。使用GetFieldIndexs的返回值,ORM框架可以使用比较快速的IDataReader.GetValue( index )来读取数据而不必使用慢速的 IDataReader.GetValue( name )了。

InnerReadRecord需要代码生成器来生成代码进行扩展,对于DB_Employees,其扩展的代码为

protected override object InnerReadRecord( System.Data.IDataReader reader , int[] FieldIndexs )

{

    MyORM.DB_Employees record = new MyORM.DB_Employees();

    int index = 0 ;

   

    index = FieldIndexs[ 0 ]; // 读取字段 EmployeeID

    if( index >= 0 )

    {

        record.EmployeeID = ConvertToInt32( reader.GetValue( index ) , ( int ) 0) ;

    }

   

    index = FieldIndexs[ 1 ]; // 读取字段 LastName

    if( index >= 0 )

    {

        record.LastName = ConvertToString( reader.GetValue( index ) , null ) ;

    }

    读取其他字段值……

    return record ;

}

在这段自动生成的代码中,参数reader就是类型为IDataReader的数据读取器,而FieldIndexs就是GetFieldIndexs的返回值。在InnerReadRecord函数中会一次读取FieldIndexs的元素值,根据属性的数据类型而调用ConvertToInt32ConvertToString等一系列的ConvertTo函数,而这一系列的函数已经在基础类型RecordORMHelper中定义好了。

从这个自动生成的代码可以看出,ORM框架使用实体类的属性,GetFieldIndexs和数据读取器实现了如下的映射过程

在这个过程中,GetFieldIndexs函数提供了一个映射表,而自动生成的代码就是利用这个映射表将数据从DataReader复制到DB_Employees的属性中。

我们自动生成代码实现了InnerReadRecord函数后,在ORM框架中就可以调用基础的RecordORMHelper中的ReadRecord函数读取一行数据并生成一个实体对象,而函数ReadRecordsReadRecord的另外一个读取多个数据的版本。

根据上述设计,我们可以使用以下代码来生成InnerReadRecord代码

myWriter.WriteLine("// 从数据读取器读取数据创建对象");

myWriter.WriteLine("protected override object InnerReadRecord( System.Data.IDataReader reader , int[] FieldIndexs )");

myWriter.BeginGroup("{");

myWriter.WriteLine( RecordType.FullName + " record = new " + RecordType.FullName + "();");

myWriter.WriteLine("int index = 0 ;");

// 获得类型中绑定到数据库的属性信息

for( int iCount = 0 ; iCount < ps.Length ; iCount ++ )

{

     System.Reflection.PropertyInfo p = ps[ iCount ] ;

     if( p.CanWrite == false )

     {

         continue ;

     }

 

     BindFieldAttribute fa = ( BindFieldAttribute ) Attribute.GetCustomAttribute(

         p , typeof( BindFieldAttribute ));

 

     myWriter.WriteLine("");

     myWriter.WriteLine("index = FieldIndexs[ " + iCount + " ]; // 读取字段 " + GetBindFieldName( p ));

     myWriter.WriteLine("if( index >= 0 )");

     myWriter.BeginGroup("{");

     Type pt = p.PropertyType ;

     object DefaultValue = this.GetDefaultValue( p );

     string strRead = null;

     if( pt.Equals( typeof( byte )))

     {

         strRead = "ConvertToByte( reader.GetValue( index ) , " + GetValueString( pt , DefaultValue ) + ")";

     }

     else if( pt.Equals( typeof( sbyte )))

     {

         strRead = "ConvertToSByte( reader.GetValue( index ) , " + GetValueString( pt , DefaultValue ) + ")";

     }

     else if( pt.Equals( typeof( short )))

     {

         strRead = "ConvertToInt16( reader.GetValue( index ) , " + GetValueString( pt , DefaultValue ) + ")";

     }

     处理其他数据类型……

     else if( pt.Equals( typeof( DateTime )))

     {

         string strDefault = "DateTime.MinValue" ;

         DateTime dt = Convert.ToDateTime( DefaultValue );

         if( dt.Equals( DateTime.MinValue ) == false )

         {

              strDefault = "new DateTime( " + dt.Ticks + ")";

         }

         strRead = "ConvertToDateTime( reader.GetValue( index ) , " + strDefault + " , " + ( fa.ReadFormat == null ? "null" : "\"" + fa.ReadFormat + "\"" ) + " )";

     }

     else if( pt.Equals( typeof( string )))

     {

         strRead = "ConvertToString( reader.GetValue( index ) , " + ( DefaultValue == null ? "null" : "@\"" + DefaultValue.ToString() + "\"" ) + " )";

     }

     else if( pt.Equals( typeof( char )))

     {

         strRead = "ConvertToChar( reader.GetValue( index ) , " + GetValueString( pt , DefaultValue ) + " )";

     }

     else

     {

         throw new InvalidOperationException("不支持属性类型" + p.Name + " " + pt.FullName );

     }

     myWriter.WriteLine("record." + p.Name + " = " + strRead + " ;" );

     myWriter.EndGroup("}");

}//for

myWriter.WriteLine("");

myWriter.WriteLine("return record ;");

myWriter.EndGroup(")//InnerReadRecord");

在这段代码中,ps是一个事先分析了DB_Employees结构而得出的System.Rection.PropertyInfo数组,包含了所有附加了BindFieldAttribute的成员属性,它是调用GetBindProperties函数获得的返回值。GetDefaultValue用于获得针对某个属性的默认值,若调用reader.GetValue( index )获得了一个空值,也就是DBNull.Value则设置属性为默认值;GetValueString是将一个数值转换为C#代码的表达样式。然后针对不同的属性数据类型生成对应的ConvertTo代码。

函数GetBindProperties的代码为

private System.Reflection.PropertyInfo[] GetBindProperties( Type RecordType )

{

     if( RecordType == null )

     {

         throw new ArgumentNullException("ReocrdType");

     }

     if( RecordType.IsPublic == false )

     {

         throw new ArgumentException("类型 " + RecordType.FullName + " 不是公开类型");

     }

     if( RecordType.IsClass == false )

     {

         throw new ArgumentException("类型 " + RecordType.FullName + " 不是类"</