[观点]RAM硬盘

mikel阅读(790)

Jim Gray在过去40年中对技术发展有过巨大的贡献,“内存是新的硬盘,硬盘是新的磁带”是他的名言。“实时”Web应用不断涌现,达到海量规模的系统越来越多,这种后浪推前浪的发展模式对软硬件又有何影响?
Tim Bray早在网格计算成为热门话题之前,就讨论过以RAM和网络为中心的硬件结构的优势,可以用这种硬件建立比磁盘集群速度更快的RAM集群。

对 于数据的随机访问,内存的速度比硬盘高几个数量级(即使是最高端的磁盘存储系统也只是勉强达到1,000次寻道/秒)。其次, 随着数据中心的网络速度提高,访问内存的成本更进一步降低。通过网络访问另一台机器的内存比访问磁盘成本更低。就在我写下这段话的时候,Sun的 Infiniband产品线中有一款具备9个全互联非阻塞端口交换机,每个端口的速度可以达到30Gbit/sec!Voltaire产品的端口甚至更 多;简直不敢想象。(如果你想了解这类超高性能网络的最新进展,请关注Andreas Bechtolsheim在Standford开设的课程。)

Tim还指出Jim Gray的名言中后半句所阐述的真理:“对于随机访问,硬盘慢得不可忍受;但如果你把硬盘当成磁带来用,它吞吐连续数据的速率令人震惊;它天生适合用来给以RAM为主的应用做日志(logging and journaling)。”
时间闪到几年之后的今天,我们发现硬件的发展趋势在RAM和网络领域势头不减,而在硬盘领域则止步不前。Bill McColl提到用于并行计算的海量内存系统已经出现

内 存是新的硬盘!硬盘速度提高缓慢,内存芯片容量指数上升,in-memory软件架构有望给各类数据密集的应用带来数量级的性能提升。小型机架服务器 (1U、2U)很快就会具备T字节、甚至更大量的内存,这将会改变服务器架构中内存和硬盘之间的平衡。硬盘将成为新的磁带,像磁带一样作为顺序存储介质使 用(硬盘的顺序访问相当快速),而不再是随机存储介质(非常慢)。这里面有着大量的机会,新产品的性能有望提高10倍、100倍。

Dare Obsanjo指出如果不把这句真言当回事,会带来什么样的恶劣后果—— 也就是Twitter正面临的麻烦。论及Twitter的内容管理,Obsanjo说,“如果一个设计只是简单地反映了问题描述,你去实现它就会落入磁盘 I/O的地狱。不管你用Ruby on Rails、Cobol on Cogs、C++还是手写汇编都一样,读写负载照样会害死你。”换言之,应该把随机操作推给RAM,只给硬盘留下顺序操作。
Tom WhiteHadoop Core项目的提交者,也是Hadoop项目管理委员会的成员。他对Gray的真言中“硬盘是新的磁带”部分作了更深入地探讨。White在讨论MapReduce编程模型的时候指出,为何对于Hadloop这类工具来说,硬盘仍然是可行的应用程序数据存储介质:

本 质上,在MapReduce的工作方式中,数据流式地读出和写入硬盘,MapReduce是以硬盘的传输速率不断地对这些数据进行排序和合并。 与之相比,访问关系数据库中的数据,其速率则是硬盘的寻道速率(寻道指移动磁头到盘面上的指定位置读取或写入数据的过程)。为什么要强调这一点?请看看寻 道时间和磁盘传输率的发展曲线。寻道时间每年大约提高5%,而数据传输率每年大约提高20%。寻道时间的进步比数据传输率慢——因此采用由数据传输率决定 性能的模型是有利的。MapReduce正是如此。

虽然固态硬盘(SSD)能否改变寻道时间/传输率的对比还有待观察,White文章的跟贴中,很多人都认为SSD会成为RAM/硬盘之争中的平衡因素
Nati Shalom对内存和硬盘在数据库部署和使用中的角色作了一番有理有据的评述。 Shalom着重指出用数据库集群和分区来解决性能和可伸缩性的局限。他说,“数据库复制和数据库分区都存在相同的基本问题,它们都依赖于文件系统/硬盘 的性能,建立数据库集群也非常复杂”。他提议的方案是转向In-Memory Data Grid(IMDG),用Hibernate二级缓存或者GigaSpaces Spring DAO之类的技术作支撑,将持久化作为服务(Persistence as a Service)提供给应用程序。Shalom解释说,IMDG

提供在内存中的基于对象的数据库能力,支持核心的数据库功能,诸如高级索引和查询、事务语义和锁。IMDG还从应用程序的代码中抽象出了数据的拓扑。通过这样的方式,数据库不会完全消失,只是挪到了“正确的”位置。

IMDG相比直接RDBMS访问的优势列举如下:

  • 位于内存中,速度和并发能力都比文件系统优越得多
  • 数据可通过引用访问
  • 直接对内存中的对象执行数据操作
  • 减少数据的争用
  • 并行的聚合查询
  • 进程内(In-process)的局部缓存
  • 免除了对象-关系映射(ORM)

你是否需要改变对应用和硬件的思维方式,最终取决于你要用它们完成的工作。但似乎公论认为,开发者解决性能和可伸缩性的思路已经到了该变一变的时候。

查看英文原文:RAM is the new disk…

[MVC]asp.net的MVC编程、MV编程以及URL重写

mikel阅读(784)

前一段时间做一个网站项目(运行于win2003+.net2.0环境),其间自己写了一个MVC架构,觉得比较适合团队快速开发ASP.NET web站点,现在拿出来和大家一起共享,请多指正。

一、MVC编程:
      针对用户的浏览器来说,网站可以看作一个实体、一个接口,其接收浏览器的请求,并将相应的信息返回给浏览器;因此,网站程序完全可以用一个程序来完成,而 实际上也确实如此,IIS、apache等web服务器本身就是一个程序,而运行其中的asp、aspx、php、jsp、html等等的单个页面,只不 过是帮助web服务器来实现一定功能而已。

      由此可以引申出:我们完全可以用一个aspx页面来处理针对网站的所有动态请求。

      而这个页面,我们就把它起名为index.aspx吧。

      在apache、tomcat等web服务器,都有相应的技术,将网站符合一定规则的所有http请求,都转向一个程序页面(如index.jsp或 index.php)来处理。而IIS在IIS7(前面提到,我们的网站服务器是windows2003,IIS版本为6.0)出来以前,只能借助于第三 方组件实现(windows2008的IIS7.0可以不借助第三方组件实现URLRewrite,网上可以找到相关代码)。这其中比较有名的2个是 ISAPI_Rewrite(Full版收费,Lite版免费)和IonicIsapiRewriter(免费),而对于我们这个MVC架构来 说,ISAPI_Rewrite Lite版(下载ISAPI_Rewrite Lite这里有ISAPI_Rewrite Lite的最新版本,我们使用的是ISAPI_Rewrite3_0047_Lite.msi)就足够了,由它来控制请求到我们的index.aspx(具体代码参见DotNetMVC示例网站代码)。

(一) 网站的目录结构
      为了使图片、css文件、js文件、html文件等免于ISAPI_Rewrite处理,需在网站根目录建立一个单独的mvc目录,其中存放MVC架构需要的aspx文件,从而网站的目录结构如下:
ROOT
  |–App_Code
        |–DotNetMVC
              |–DAL
                  |–UserDA.cs
              |–Model
                  |–User.cs
              |–Util
                  |–ControllerUtil.cs
                  |–DBUtil.cs
  |–Bin
      |–log4net.dll
  |–css
  |–images
  |–js
  |–mvc
      |–application
             |–controllers
                  |-default.aspx
                  |-user.aspx
             |–views
                  |–default
                       |-index_view.aspx
                  |–share
                  |–user
                       |-home_view.aspx
                       |-list_view.aspx
      |–cache
      |–log
      |-index.aspx
  |-Default.aspx
  |-Global.asax
  |-web.config

      上面的结构中,css、images、js放什么文件分别存放站点用的样式文件、图片文件、JavaScript脚本文件;Bin存放公共组件(目前有一 个日志组件log4net.dll,其配置信息在web.config配置文件中),App_Code存放自定义的公共类,这是.net2.0规定 的,mvc目录的作用上面说了,不再赘述。

     下面再看App_Code和mvc下的子目录构成:
      App_Code->Util目录,存放一些实用工具类,目前有2个,DBUtil.cs是SQLServer数据库处理工具类,而 ControllerUtil.cs则是我们这个MVC架构比较核心的URL路由工具类,其中ParseUri方法解析URI返回控制器、控制分支、参数 等,LinkTo方法产生需要的网站链接URL(具体代码参见DotNetMVC示例网站代码)。大家还可以在其中添加一些需要的功能,如上传、Email、字符串、分页、图形处理等。
      而App_Code->DAL和App_Code->Model目录中的内容一起组成MVC架构中M层逻辑,其中Model目录的仅仅是数据 库表的数据抽象类(这些类可以自己写,也可以通过工具结合数据库表自动生成),DAL目录中的类则通过访问数据库返回数据抽象类的对象实例或实例列表,供 控制层调用。
      mvc根目录下的index.aspx文件,如上所述是MVC架构的入口文件。
      mvc->cache和mvc->log目录分别存放缓存文件和日志文件,如果需要的话。
      mvc->application目录中的controllers目录和views目录分别是控制器文件和视图文件,即MVC架构中的C和V层,其 中一个控制器对应一个aspx文件(如user.aspx),并在views目录中有一个视图子目录对应(如views->user),其中存放多 个此控制器可能用到的视图文件(如home_view.aspx和list_view.aspx等);views->share目录中,则是一些视 图文件可能用到的公共文件,如头尾包含页、母板页等。

(二)MVC运转流程:

      前面曾经提到,要把所有针对本站点的动态请求,都统一到index.aspx这个文件来集中处理,因此需要借助ISAPI_Rewrite,在ISAPI_Rewrite Lite的配置文件httpd.conf(位于ISAPI_Rewrite Lite的安装目录)中增加下面一条记录即可:
RewriteRule /mvc/(.*)$ /mvc/index.aspx\?idu=$1 [NC,NE]

      这条规则是将针对/mvc这个URI及其后的所有访问,都导向到index.aspx这个文件来处理,并将URI路径作为idu参数的值传给index.aspx
      下面看看index.aspx文件的内容:

<%@ Page Language="C#" %>
<%@ import namespace="System.Threading" %>
<%
    
string controller;
    
string method;
    
string[] parameters;
    
string uri_string = Request.QueryString["idu"];
    DotNetMVC.Util.ControllerUtil.ParseUri(uri_string, 
out controller, out method, out parameters);

    Context.Items.Add(
"method", method);
    Context.Items.Add(
"parameters", parameters);
    
    
string ctlpage = "system/application/controllers/" + controller + ".aspx";
    
try
    
{
        Server.Transfer(ctlpage, 
true);
    }

    
catch (HttpException e)
    
{
        log4net.LogManager.GetLogger(
"LogFile").Error("\r\n客户机IP:" + Request.UserHostAddress + "\r\n错误地址:" + Request.Url, e);
    }

%>

      上面代码的关键是,通过对idu值的解析,取得controller、method、parameters这3个参数;其中controller对应于一 个控制器文件,method对应控制器中的一个控制分支,parameters则是参数的字符串数组(参数顺序自己把握),其对应URI如下:
/mvc/controller/method/parameter1/parameter2/parameterN
      目前这个MVC架构的URI,都是一个控制器,后跟一个分支,再后跟多个参数,如果没有controller、method、parameters,则默 认调用default控制器的index分支,如果没有method、parameters,则默认调用本控制器的index分支;如果以上不满足要求, 大家可以自己更改ControllerUtil类的ParseUri方法或者写一个复杂的URL Routing(我个人认为不管什么,够用就好,controller、method已经能够表达复杂的网站层次了)。Server.Transfer方 法的第2个参数为true,是确保request、cookie、session等变量能够传递到控制器程序中。

      下面通过default.aspx这个控制器(可以一个功能或一个模块作为一个控制器)说明一下控制器的流程:

<%@ Page Language="C#" %>
<%@ import namespace="DotNetMVC.Util" %>
<%
string method = (string)Context.Items["method"];
string[] parameters = (string[])Context.Items["parameters"];
try
{
    
if (method == "index")
    
{
        Context.Items.Add(
"loginurl", ControllerUtil.LinkTo("user""login"""));
        Context.Items.Add(
"userlisturl", ControllerUtil.LinkTo("user""list"""));
        Server.Execute(
"application/views/default/index_view.aspx");
    }

}

catch (HttpException e)
{
    log4net.LogManager.GetLogger(
"LogFile").Error("\r\n客户机IP:" + Request.UserHostAddress + "\r\n错误地址:" + Request.Url, e);
}

%>

      上面代码中,首先通过Context获取index.aspx中 得到的method、parameters参数,然后根据method值的不同,走不同的处理分支(这里可以调用M层获取数据)产生数据并保存在 Context(目前仅找到用Context来传递数据到视图)中,再调用不同的视图;这里只有一个默认index分支,去调用首页视图 index_view.aspx。


      最后看看views->default目录下index_view.aspx这个视图文件内容:

<%@ Page Language="C#" EnableViewStateMac="false"%>
<%
    
string loginurl = (string)Context.Items["loginurl"];
    
string userlisturl = (string)Context.Items["userlisturl"];
%>
<!– #include file="../share/top.aspx" –>
<center>
<form name=frm1 method=post action=<%=loginurl %>>
昵称:
<input type=text name=nick size=20>&nbsp;&nbsp;
密码:
<input type=password name=pass size=20>
<input type=submit value=登陆> &nbsp;<a href=<%=userlisturl %>>去用户列表</a>
</form>
</center>
<!– #include file="../share/bottom.aspx" –>

      这个视图文件中的代码则通过Context接收控制器传过来的数据并显示它。
      至此,整个MVC的运转流程就完成了;其他更复杂的一些操作,大家可以参照DotNetMVC示例网站代码,相信根据这个示例,就可以直接建立并快速开发自己的MVC网站了,并有URL重写功能;在其中的user控制器及视图中,大家可以看到这种编程有一个好处,那就是可以充分利用visual studio的代码智能感知功能。
      不过,有一点需要大家注意,因为需要经过ISAPI_Rewrite过滤器与Transfer、Execute处理,所以会有一些性能损失。根据流程的复 杂程度,其损失所占比重会有所不同:越复杂的流程,其损失所占比重越小,越简单的流程,其损失所占比重越大;所以大家在用这套流程开发网站时,应充分注意 这一点,并适当进行程序优化,或者干脆别用它了

二、MV编程:

      大家在看上面的DotNetMVC示例网站代码的时候,肯定有一个感觉:在控制层产生数据,要赋给Context,视图层再从Context中取出数据来显示,似乎多此一举;所以引出下面的MV编程(可能会丧失一些灵活性和可维护性,但开发一般的网站应该足够了)。

      我们只需要把DotNetMVC示例网站代码中的代码简单做一下修改就可以了。

      首先,修改index.aspx如下:

<%@ Page Language="C#" %>
<%@ import namespace="System.Threading" %>
<%
    
string controller;
    
string method;
    
string[] parameters;
    
string uri_string = Request.QueryString["idu"];
    DotNetMVC.Util.ControllerUtil.ParseUri(uri_string, 
out controller, out method, out parameters);

    Context.Items.Add(
"method", method);
    Context.Items.Add(
"parameters", parameters);

    
string ctlpage = "application/views/" + controller + "/" + method + ".aspx";
    
try
    
{
        
//Server.Transfer(ctlpage, true);
        Server.Execute(ctlpage);
    }

    
catch (HttpException e)
    
{
        log4net.LogManager.GetLogger(
"LogFile").Error("\r\n客户机IP:" + Request.UserHostAddress + "\r\n错误地址:" + Request.Url, e);
    }

%>

      这里直接用controller和method组合出视图路径,并用Server.Execute去执行它(因为Server.Transfer总是抛出 System.Threading.ThreadAbortException异常,虽然不影响运行,这似乎是ASP.NET的一个小问题)。
      然后删除控制器目录的内容,并将控制器页面中的代码转移到视图中即可,具体请参看DotNetMV示例网站代码。这样,既减少了编程复杂度,又增加的开发效率和运行性能,同时URL重写和代码智能感知等优点仍然得以保留。

附:

      下载DotNetMVC示例网站代码DotNetMV示例网站代码到本地后,请注意示例网站代码是一个完整的网站架构,大家可以直接在VS中打开此网站(VS2005和VS2008),根据实际修改web.config文件,在IIS中配置dotnetmv(c)为站点根目录,并配置站点属性“目录安全性”标签为“集成windows身份验证”,同时将站点应用程序配置为“启用ASP服务器脚本调试”和“启用ASP客户端脚本调试”;运行调试前,请先安装ISAPI_RewriteLite(下载ISAPI_Rewrite Lite),并修改其配置文件;调试时,请注意修改站点的“启动选项”使用本地IIS,因为VS中的DevelopWebServer无法加载ISAPI_Rewrite过滤器。
      另外,测试数据表建立脚本给出如下:

Create TABLE [dbo].[users](
    
[id] [int] IDENTITY(1,1NOT NULL,
    
[nick] [varchar](50) COLLATE Chinese_PRC_CI_AS NULL,
    
[pass] [varchar](20) COLLATE Chinese_PRC_CI_AS NULL,
    
[reg_date] [datetime] NULL CONSTRAINT [DF_test_inputdate]  DEFAULT (getdate())
ON [PRIMARY]

[资源]Lucene.net的相关网站

mikel阅读(698)

 lucene 官方网站,  Luncene.net 官方网站(英文) 这两个网站大家应该都知道了,里面还有一个邮件列表,有兴趣可以加入这个邮件列表,这样就可以和全球的爱好者一起来讨论Lucene.net的相关问题了。
    中文资源
    博客园 lucene.net 讨论小组 一个园子里面的,当然要重点推荐一下了。
    博客园 搜索引擎讨论小组 我创建的小组,主要讨论搜索引擎技术,数据挖掘技术和中文分词技术,由于和lucene高度相关,所以一些lucene的问题也会在这个小组中进行讨论。
    Nutch中文网论坛 Lucene学习资料 
    JavaEye lucene爱好者
    
    中文资源目前就找到这么一点,感觉实在太少了,还望大家补充。
    
    英文资源
    lucene apache 组织中的 wiki 这个网址有很多lucene 的相关资源
    lucene 的英文博客网站 这个是lucene的鼻祖Doug Cutting的Blog
    维基百科Lucene相关的介绍
    Lucene forum at nabble.com
    http://groups.drupal.org/lucene-and-nutch
    http://lucene.grantingersoll.com/

[文档]ADO三大对象的属性、事件、方法

mikel阅读(730)

Connection对象
属性
属性名称数据类型和用途
Attributes 可读写Long类型,通过两个常数之和指定是否使用保留事务(retainningtransactions)。常数 adXactCommitRetaining表示调用CommitTrans方法时启动一个新事务;常数adXactAbortRetaning表示调用 RollbackTrans方法时启动一个新事务。默认值为0,表示不使用保留事务。
CommandTimeout可读写Long类型,指定中止某个相关Command对象的Execute调用之前必须等待的时间。默认值为30秒。
ConnectionString可读写String类型,提供数据提供者或服务提供者打开到数据源的连接所需要的特定信息
ConnectionTimeout可读写Long类型,指定中止一个失败的Connection.Open方法调用之前必须等待的时间,默认值为15秒。
CursorLocation可读写Long类型,确定是使用客户端(adUseClient)游标引擎,还是使用服务器端(adUseServer)游标引擎。默认值是adUseServer。
DefaultDatabase可读写String类型,如果ConnectString中未指定数据库名称,就使用这里所指定的名称,对SQLServer而言,其值通常是pubs
IsolationLevel可读写Long类型,指定和其他并发事务交互时的行为或事务。见IsolationLevel常数
ModeLong类型,指定对Connection的读写权限。见Mode常数
Provider可读写String类型,如果ConnectionString中未指定OLEDB数据或服务提供者的名称,就使用这时指定的名称。默认值是MSDASQL(MicrosoftOLEDBProviderforODBC)。
State可读写Long类型,指定连接是处于打开状态,还是处于关闭状态或中间状态。见State常数
Version只读String类型,返回ADO版本号。
注意:上面所列出的大多数可读写的属性,只有当连接处于关闭状态时才是可写的。
只有当用户为Connection对象用BeginTrans…CommitTrans…RollbackTrans方法定义了不遗余力,事务隔离程度的指定才真正有效。如果有多个数据库用户同时执行事务,那么应用程序中必须指定如何响应运行中的其他事务。
方法
方法用途
BeginTrans初始化一个事务;其后必须有CommitTrans和/或RollbackTrans相呼应
Close关闭连接
CommitTrans提交一个事务,以完成对数据源的永久改变(要求使用之前必须调用了BeginTrans方法)
Execute人SelectSQL语句返回一个forward-onlyRecordset对象,也用来执行那些不返回Recordset语句,如Insert、Update、Delete查询或DDL语句
Open用连接字符串来打开一个连接
OpenSchema返回一个Recordset对象以提供数据源的结构信息(metadata)
RollbackTrans取消一个事务,恢复对数据源做的临时性改变(要求使用之前必须调用了BeginTrans方法)
注:只有Execute、Open和OpenSchema三个方法才能接受变元参数。Execute的语法为:
cnnName.ExecutestrCommand,[lngRowsAffected[,lngOptions]]
strCommand 的值可以是SQL语句、表名、存储过程名,也可以是数据提供者所能接受的任意字符串。为了提高性能,最好为lngOptions参数指定合适的值(详见 lngOptions参数用到的常数),以使提供者解释语句时不用再去判定其类型。可选参数lngRowsAffected将返回Insert、 Update或Delete查询执行以后所影响的数目。这些查询会返回一个关闭的Recordset对象。一个Select查询将返回 lngRowsAffected值为0并且返回带有一行或多行内容的打开的forward-onlyRecordset。
事件
事件名称触发时机
BeginTransCompleteBeginTrans方法执行以后。
PrivateSubcnnName_BeginTransComplet(ByValTransactionLevelAsLong,ByValpErrorAsADODB.Error,adStatusAsADODB.EventStatusEnum,ByValpConnectionAsADODB.Connection)
CommitTransCompleteCommitTrans方法执行以后
PrivateSubConnection1_CommitTransComplete(ByValpErrorAsADODB.Error,adStatusAsADODB.EventStatusEnum,ByValpConnectionAsADODB.Connection)
ConnectComplete成功建立到数据源的Connection之后
PrivateSubConnection1_ConnectComplete(ByValpErrorAsADODB.Error,adStatusAsADODB.EventStatusEnum,ByValpConnectionAsADODB.Connection)
DisconnectConnection关闭之后
PrivateSubConnection1_Disconnect(adStatusAsADODB.EventStatusEnum,ByValpConnectionAsADODB.Connection)
ExecuteComplete完成Connection.Execute或Command.Execute之时
PrivateSubConnection1_ExecuteComplete(ByValRecordsAffectedAsLong,ByValpErrorAsADODB.Error,adStatusAsADODB.EventStatusEnum,ByValpCommandAsADODB.Command,ByValpRecordsetAsADODB.Recordset,ByValpConnectionAsADODB.Connection)
InfoMessage一个Error对象被添加到ADODB.Connectio.Error集合之时
PrivateSubConnection1_InfoMessage(ByValpErrorAsADODB.Error,adStatusAsADODB.EventStatusEnum,ByValpConnectionAsADODB.Connection)
RollbackTransCompleteRollbackTrans方法执行之后
PrivateSubConnection1_RollbackTransComplete(ByValpErrorAsADODB.Error,adStatusAsADODB.EventStatusEnum,ByValpConnectionAsADODB.Connection)
WillConnect即将调用Connection.Open方法之时
PrivateSubConnection1_WillConnect(ConnectionStringAsString,UserIDAsString,PasswordAsString,OptionsAsLong,adStatusAsADODB.EventStatusEnum,ByValpConnectionAsADODB.Connection)
WillExecute即将调用Connection.Execute或Command.Execute方法之时
PrivateSubConnection1_WillExecute(SourceAsString,CursorTypeAsADODB.CursorTypeEnum,LockTypeAsADODB.LockTypeEnum,OptionsAsLong,adStatusAsADODB.EventStatusEnum,ByValpCommandAsADODB.Command,ByValpRecordsetAsADODB.Recordset,ByValpConnectionAsADODB.Connection)
注:其中的adStatus参数所用到的常数的名称和含义详见adStatus所用的常数
常数
IsolationLevel常数
常数含义
adXactCursorStability只允许读其他事务已提交的改变(默认值)
adXactBrowse允许读其他事务未提交的改变
adXactChaos本事务不会覆盖其他位于更高隔离程度的事务所做的改变
adXactIsolated所有事务相互独立
adXactReadCommitted等同于adXactCursorStability
adXactReadUncommitted等同于adXactBrowse
adXactRepeatableRead禁止读其他事务的改变
adXactSerializable等同于adXactIsolated
adXactUnspecified不能确定提供者的事务隔离程度
Mode常数
常数含义
adModeUnknown未指定数据源的连接许可权(默认值)
adModeRead连接是只读的
adModeReadWrite连接是可读写的
adModeShareDenyNone不拒绝其他用户的读写访问(JetOLEDBProvider的默认值)
adModeShareDenyRead拒绝其他用户打开到数据源的读连接
adModeShareDenyWrite拒绝其他用户打开到数据源的写连接
adModeShareExclusive以独占方式打开数据源
adModeWrite连接是只写的
State常数
常数含义
adStateClosedConnection(或其他对象)是关闭的(默认值)
adStateConnecting正在连接数据源的状态
adStateExecutingConnection或Command对象的Execute方法已被调用
adStateFetching返回行(row)到Recordset对象
adStateOpenConnection(或其他对象)是打开的(活动的)
Execute方法中lngOption参数用到的常数
Command类型常数含义
adCmdUnknownCommand类型未定(默认值),由数据提供者去判别Command语法
adCmdFileCommand是和对象类型相应的文件名称
adCmdStoredProcCommand是存储过程名称
adCmdTableCommand是能产生内部Select*FROMTableName查询的表名称
adCmdTableDirectCommand是能直接从表中获取行内容的表名称
adCmdTextCommand是一条SQL语句
ADODB事件处理子过程参数adStatus所用的常数
常数含义
adStatusCancel操作被用户取消
adStatusCnatDeny操作不能拒绝其他用户对数据源的访问
adStatusErrorsOccurred操作导致错误并已送到Errors集合中
adStatusOK操作成功
adStatusUnWantedEvent操作过程中一个未预料到的事件被激活->

[代码]生成验证码的控件

mikel阅读(772)

看过网上很多关于ASP.NET生成 验证码的东西。大部分都牺牲了一个aspx页面来生成验证码图片。 而且还不利于二次使用。 于是想一直琢磨着想写一个不用牺牲一个aspx页面来完成验证码的控件,在使用的时候只需要引用一个dll并且在工具箱中直接拖到页面上就可。 经过几天努力还真琢磨出来了一个个人认为比较好的方法,于是拿出来共享一下,希望和大家交流交流意见。
现在开始

using System;
using System.Collections.Generic;
using System.Text;

using System.Web;
using System.Drawing;
using System.IO;
using System.Web.SessionState;

namespace jdTools.Web
{
    
public class VerifyImageHttpHandle : IHttpHandler, IRequiresSessionState
    
{
        
private string GetRandomNumberString(int codeCount)
        
{
            
int j1;
            
string strChoice = "2,3,4,5,6,7,8,9,A,B,C,D,E,F,G,H,J,K,L,M,N,P,Q,R,S,T,U,V,W,X,Y,Z";
            
string[] strResult = strChoice.Split(new Char[] ',' });
            
string strReturn = "";
            Random rnd 
= new Random(unchecked((int)DateTime.Now.Ticks));
            
for (int i = 0; i < codeCount; i++)
            
{
                Random rnd1 
= new Random(rnd.Next() * unchecked((int)DateTime.Now.Ticks));
                j1 
= rnd1.Next(strResult.Length);
                rnd 
= new Random(rnd.Next() * unchecked((int)DateTime.Now.Ticks));
                strReturn 
= strReturn + strResult[j1].ToString();
            }

            
return strReturn;  
        }

        
private Color GetColor()
        
{
            
return Color.Black;
        }

        
private Bitmap CreateImage(string str_ValidateCode)
        
{
            Random newRandom 
= new Random((int)DateTime.Now.Ticks);
            
int int_ImageWidth = str_ValidateCode.Length * 14;
            
int int_ImageHeight = 20;
            
//  图高20px
            Bitmap theBitmap = new Bitmap(int_ImageWidth, int_ImageHeight);
            Graphics theGraphics 
= Graphics.FromImage(theBitmap);
            
//  白色背景
            theGraphics.Clear(Color.White);
            
//  灰色边框
            theGraphics.DrawRectangle(new Pen(Color.LightGray, 1), 00, int_ImageWidth  1, int_ImageHeight);

            
int num = theBitmap.Width * theBitmap.Height * 30 / 100;
            
for (int iCount = 0; iCount < num; iCount++)
            
{
                
// 在随机的位置使用随机的颜色设置图片的像素
                int x = newRandom.Next(theBitmap.Width);
                
int y = newRandom.Next(theBitmap.Height);
                
int r = newRandom.Next(255);
                
int g = newRandom.Next(255);
                
int b = newRandom.Next(255);
                Color c 
= Color.FromArgb(r, g, b);
                theBitmap.SetPixel(x, y, c);
            }
//for


            
//  10pt的字体
            Font theFont = new Font("Arial"12, FontStyle.Bold);

            
for (int int_index = 0; int_index < str_ValidateCode.Length; int_index++)
            
{
                
string str_char = str_ValidateCode.Substring(int_index, 1);
                Brush newBrush 
= new SolidBrush(GetColor());
                Point thePos 
= new Point(int_index * 13 + 1 + newRandom.Next(3), 1 + newRandom.Next(3));
                theGraphics.DrawString(str_char, theFont, newBrush, thePos);
            }

            
            theGraphics.Dispose();
            
return theBitmap;
            
        }


        
#region IHttpHandler 成员

        
public bool IsReusable
        
{
            
get return true; }
        }

        
public void ProcessRequest(HttpContext context)
        
{
            HttpResponse Response 
= context.Response;
            
//  将生成的图片发回客户端
            MemoryStream ms = new MemoryStream();
            
string NumStr=GetRandomNumberString(5);
            context.Session.Add(
"Verify", NumStr);

            Bitmap theBitmap 
= CreateImage(NumStr);
            theBitmap.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
            Response.ClearContent(); 
//需要输出图象信息 要修改HTTP头 
            Response.ContentType = "image/jpeg";
            Response.BinaryWrite(ms.ToArray());
            theBitmap.Dispose();
            ms.Close();
            ms.Dispose();
            Response.End();
        }

        
#endregion

    }

}

GetRandomNumberString方法用于生成指定个数的随机数字符串。
IHttpHandle接口的ProcessRequest方法可以启用 HTTP Web 请求的处理。
VerifyImageHttpHandle 继承了IHttpHandle接口用于自定义程序处理的Web请求。然后在web.config的<system.web>节中配置

      <httpHandlers>
        
<add verb="*" path="VerifyImg.jd" type="jdTools.Web.VerifyImageHttpHandle"/>
      
</httpHandlers>

此时在浏览器中访问VerifyImg.jd时就会得到一个验证码图片,生成验证码的内容就完成了。
现在我们要自定义一个WEB自定义控件。其代码如下:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;


namespace jdTools.Web.UI
{
    [ToolboxData(
"<{0}:VerifyImage runat=server></{0}:VerifyImage>")]
    
public class VerifyImage : WebControl
    
{
        
public string Value
        
{
            
get return HttpContext.Current.Session["Verify"].ToString(); }
        }

        
public virtual ImageAlign ImageAlign
        
{
            
get
            
{
                
object obj2 = this.ViewState["ImageAlign"];
                
if (obj2 != null)
                
{
                    
return (ImageAlign)obj2;
                }

                
return ImageAlign.NotSet;
            }

            
set
            
{
                
if ((value < ImageAlign.NotSet) || (value > ImageAlign.TextTop))
                
{
                    
throw new ArgumentOutOfRangeException("value");
                }

                
this.ViewState["ImageAlign"= value;
            }


        }


        
public VerifyImage()
            : 
base(HtmlTextWriterTag.Img)
        
{ }
        
protected override void AddAttributesToRender(HtmlTextWriter writer)
        
{
            
base.AddAttributesToRender(writer);
            writer.AddStyleAttribute(HtmlTextWriterStyle.Cursor, 
"pointer");
            writer.AddAttribute(
"onclick""this.src='VerifyImg.jd?id='+Math.random()"true);
            writer.AddAttribute(HtmlTextWriterAttribute.Src, 
"VerifyImg.jd");
            
string str2;
            
switch (this.ImageAlign)
            
{
                
case ImageAlign.Left:
                    str2 
= "left";
                    
break;

                
case ImageAlign.Right:
                    str2 
= "right";
                    
break;

                
case ImageAlign.Baseline:
                    str2 
= "baseline";
                    
break;

                
case ImageAlign.Top:
                    str2 
= "top";
                    
break;

                
case ImageAlign.Middle:
                    str2 
= "middle";
                    
break;

                
case ImageAlign.Bottom:
                    str2 
= "bottom";
                    
break;

                
case ImageAlign.AbsBottom:
                    str2 
= "absbottom";
                    
break;

                
case ImageAlign.AbsMiddle:
                    str2 
= "absmiddle";
                    
break;

                
case ImageAlign.NotSet:
                    str2
="";
                    
break;
                
default:
                    str2 
= "texttop";
                    
break;
            }

            
if (str2 != "")
                writer.AddAttribute(HtmlTextWriterAttribute.Align, str2);
        }

    }

}

VerifyImage 类定义了两个属性。其中Value属性用于取得session中存放的验证码的值。
这里就很简单了,没有必要详说。
在使用的时候aspx页面如下:

                    <ul>
                        
<li>用户名:<asp:TextBox ID="txtUserName" runat="server"></asp:TextBox></li>
                        
<li>密 码:<asp:TextBox ID="txtPassWord" runat="server"></asp:TextBox></li>
                        
<li>检证码:<asp:TextBox ID="txtVerify" runat="server" Width="50px"></asp:TextBox><jd:VerifyImage ID="verify" runat="server" Height="23px" ImageAlign="AbsMiddle"/></li>
                    
</ul>

aspx.cs中:

            if (txtVerify.Text != verify.Value)
            
{
                
//.验证码错误
            }

            
else
            
{
                
//验证码正确
            }

[资源].net网站

mikel阅读(687)

博客园博客堂CSDN 当然是不用说了,大家都知道的。
51ASPX.com
国内非常优秀的ASP.NET源码发布下载站,相信很多人都到过这里下载过源码。
孟子E章
很多非常实用的技术实例,当你遇到什么问题不妨先到这里看看,没准儿会有意想不到的收获。
CodeUsing.com
一个不错的.NET资源分享站,也算一个Web2.0的网站,注册用户可以发布资源。
msproject.cn
简单整洁的.NET开源技术社区,以前是一个开源技术论坛,改版后又增加了一些功能:视屏、公司点评等。
.NET正则表达式库(regex-lib.net)
博客园 Anders Liu 刚发布的正则表达式分享网站

C#资源大全
不用说,是一些开源资源了,只是好像两年没有更新了。
http://java.poac.ac.cn/codeopen/jiaocheng/cs-open/index.html

[代码]JavaScript 图片切换展示效果

mikel阅读(882)

看到alibaba的一个图片切换效果,感觉不错,想拿来用用。但代码一大堆的,看着昏,还是自己来吧。
由于有了做JavaScript 图片滑动展示效果的经验,做这个就容易得多了。
先看看效果:
根据alibaba做的效果:

  • 1
  • 2
  • 3

alibaba的按钮有一个延迟的功能,应该加个定时器,但这个不是重点就省了吧。
扩展成能左右切换:

  • 1
  • 2
  • 3

其他扩展功能:



切换速度:
停顿时间:


程序说明:
首先需要一个容器,设置它的overflow为hidden,position为relative;
容器里面还要一个滑动对象,设置它的position为
absolute;
initialize()函数里初始化一些属性。
在切换之前先执行Start()函数,进行相关设置,
主要是设置Index属性(索引)和_target属性(目标值):


if(this.Index < 0){
    
this.Index = this._count  1;
else if  (this.Index >= this._count){ this.Index = 0; }
this._target = 1 * this._parameter * this.Index;

接着就执行Move()函数开始移动了,原理通过设置滑动对象的top(或left)来做出移动的效果,
而减速的效果就需要执行GetStep()函数来获取步长:

var iStep = (iTarget  iNow) / this.Step;

用目标值减当前值再除以一个参数,得到步长,
这样取得的步长在当前值越接近目标值时会越来越小,也做成了减速的效果,
然后在top(或left)的设置中加上这个步长,并设置定时器继续Move(),直到到达目标值:


this._slider.style[style] = (iNow + iStep) + "px";
this._timer = setTimeout(function(){ oThis.Move(); }, this.Time);

下面例子里的容器结构:


<div class="container" id="idTransformView">
  
<ul class="slider" id="idSlider">
    
<li><img src="http://shundebk.cn/temp/1.jpg"/></li>
    
<li><img src="http://shundebk.cn/temp/2.jpg"/></li>
    
<li><img src="http://shundebk.cn/temp/3.gif"/></li>
  
</ul>
  
<ul class="num" id="idNum">
    
<li>1</li>
    
<li>2</li>
    
<li>3</li>
  
</ul>
</div>

要美观的话需要css设置一下:


.container, .container *{margin:0; padding:0;}
.container
{width:408px; height:168px; overflow:hidden;}
.slider
{position:absolute;}
.slider li
{ list-style:none;display:inline;}
.slider img
{ width:408px; height:168px; display:block;}
.slider2
{width:2000px;}
.slider2 li
{float:left;}
.num
{ position:absolute; right:5px; bottom:5px;}
.num li
{
    float
: left;
    color
: #FF7300;
    text-align
: center;
    line-height
: 16px;
    width
: 16px;
    height
: 16px;
    font-family
: Arial;
    font-size
: 12px;
    cursor
: pointer;
    overflow
: hidden;
    margin
: 3px 1px;
    border
: 1px solid #FF7300;
    background-color
: #fff;
}
.num li.on
{
    color
: #fff;
    line-height
: 21px;
    width
: 21px;
    height
: 21px;
    font-size
: 16px;
    margin
: 0 1px;
    border
: 0;
    background-color
: #FF7300;
    font-weight
: bold;
}

样式的设置跟程序也有一定关系,例如宽和高,这里就不说明了。
接着就可以实例化了:


var tv = new TransformView ("idTransformView", "idSlider", 168, 3, {
    onStart : function(){ Each(objs, function(o, i) { o.className = tv.Index == i ? "on" : ""; })  }//按钮样式
});

这里主要有4个参数,分别是容器对象、滑动对象、切换参数和切换数量。
当程序是上下切换时,切换参数切换的高度,当左右切换时,是切换的宽度。
切换数量就是有多少个切换对象了,例如上面的例子就是3个。
最后的参数是一些设置:
属性: 默认值//说明
Up:   true,//是否向上(否则向左)
Step:  5,//滑动变化率
Time:  10,//滑动延时
Auto:  true,//是否自动转换
Pause:  2000,//停顿时间(Auto为true时有效)
onStart: function(){},//开始转换时执行
onFinish: function(){}//完成转换时执行

例子里设置了onStart属性,作用是在切换开始时,设置按钮的样式
例子里完整的测试代码:


function Each(list, fun){
    
for (var i = 0, len = list.length; i < len; i++) { fun(list[i], i); }
};
var objs = $("idNum").getElementsByTagName("li");
var tv = new TransformView("idTransformView""idSlider"1683,  {
    onStart : 
function(){ Each(objs, function(o, i) { o.className = tv.Index == i ? "on" : ""; }) }//按钮样式
});
tv.Start();
Each(objs, 
function (o, i){
    o.onmouseover 
= function(){
        o.className 
= "on";
        tv.Auto 
= false;
        tv.Index 
= i;
        tv.Start();
    }
    o.onmouseout 
= function(){
        o.className 
= "";
        tv.Auto 
= true;
        tv.Start();
    }
})

源码:


var $ = function (id) {
    
return "string" == typeof id ? document.getElementById(id) : id;
};
var Class = {
  create: 
function() {
    
return function() {
      
this.initialize.apply(this, arguments);
    }
  }
}
Object.extend 
= function(destination, source) {
    
for (var property in source) {
        destination[property] 
= source[property];
    }
    
return destination;
}
var TransformView = Class.create();
TransformView.prototype 
= {
  
//容器对象,滑动对象,切换参数,切换数量
  initialize: function(container, slider, parameter, count, options) {
    
if(parameter <= 0 || count <= 0return;
    
var oContainer = $(container), oSlider = $(slider), oThis = this;
    
this.Index = 0;//当前索引
    
    
this._timer = null;//定时器
    this._slider = oSlider;//滑动对象
    this._parameter = parameter;//切换参数
    this._count = count || 0;//切换数量
    this._target = 0;//目标参数
    
    
this.SetOptions(options);
    
    
this.Up = !!this.options.Up;
    
this.Step = Math.abs(this.options.Step);
    
this.Time = Math.abs(this.options.Time);
    
this.Auto = !!this.options.Auto;
    
this.Pause = Math.abs(this.options.Pause);
    
this.onStart = this.options.onStart;
    
this.onFinish = this.options.onFinish;
    
    oContainer.style.overflow 
= "hidden";
    oContainer.style.position 
= "relative";
    
    oSlider.style.position 
= "absolute";
    oSlider.style.top 
= oSlider.style.left = 0;
  },
  
//设置默认属性
  SetOptions: function(options) {
    
this.options = {//默认值
        Up:            true,//是否向上(否则向左)
        Step:        5,//滑动变化率
        Time:        10,//滑动延时
        Auto:        true,//是否自动转换
        Pause:        2000,//停顿时间(Auto为true时有效)
        onStart:    function(){},//开始转换时执行
        onFinish:    function(){}//完成转换时执行
    };
    Object.extend(
this.options, options || {});
  },
  
//开始切换设置
  Start: function() {
    
if(this.Index < 0){
        
this.Index = this._count  1;
    } 
else if (this.Index >= this._count){ this.Index = 0; }
    
    
this._target = 1 * this._parameter * this.Index;
    
this.onStart();
    
this.Move();
  },
  
//移动
  Move: function() {
    clearTimeout(
this._timer);
    
var oThis = this, style = this.Up ? "top" : "left", iNow = parseInt(this._slider.style[style]) || 0, iStep = this.GetStep(this._target, iNow);
    
    
if (iStep != 0) {
        
this._slider.style[style] = (iNow + iStep) + "px";
        
this._timer = setTimeout(function(){ oThis.Move(); }, this.Time);
    } 
else {
        
this._slider.style[style] = this._target + "px";
        
this.onFinish();
        
if (this.Auto) { this._timer = setTimeout(function(){ oThis.Index++; oThis.Start(); }, this.Pause); }
    }
  },
  
//获取步长
  GetStep: function(iTarget, iNow) {
    
var iStep = (iTarget  iNow) / this.Step;
    
if (iStep == 0return 0;
    
if (Math.abs(iStep) < 1return (iStep > 0 ? 1 : 1);
    
return iStep;
  },
  
//停止
  Stop: function(iTarget, iNow) {
    clearTimeout(
this._timer);
    
this._slider.style[this.Up ? "top" : "left"= this._target + "px";
  }
};

下载测试代码

[CodeDOM] 使用C# 2.0实现语言无关的代码生成器

mikel阅读(879)

一、什么是CodeDom? 现 在的程序规模越来越大,虽然在计算发展的几十年间,产生了许多快捷、高效的编程语言和开发工具,如C#、Visual Studio、java等。也产生了许多用以辅助软件设计、开发的思想和方法,如UML、OOP、Agile等。尽管利用这些技术和方法可以大大提高程序 编写的效率,但是仍可能有重复的编码工作。因此,现在出现了许多可以自动产生源代码或者目标文件的软件,即Code Wizards。
    一 般这些Code Wizards在生成源代码时都是通过设置模板文件,然后根据这些模板文件生成源代码。有很多Code Wizards只能生成固定的语言(如java、C#等)。虽然有一些Code Wizards可以生成多种语言,但也只是固定的几种。而且生成源代码部分都是显示地固定在程序中。这样非常不易扩展。如CodeSmith系统,这是一 个非常不错的Code Wizard。它使用一个扩展名为cst的文件来设置模板。这个模板文件的格式类似于ASP.NET。如果想生成C#源代码,必须要在其中显示地标明,并 且模板的固定部分要使用C#语言编写。如果这样的话,同样功能要生成不同语言的代码,如C#和VB.net。就要编写两个模板文件。这是非常不方便的。
    从 以上的描述来看, Code Wizard所面临的一个重要问题就是如何使用一个模版文件来生成不同语言的源代码。幸好Microsoft提供了一种解决方案,这就是CodeDOM技 术。CodeDOM的全称是代码文档对象模型(Code Document Object Model)。整个CodeDOM就是一张对象图(object graph)。它用这张图中的所有对象描述了面向对象语言中的几乎所有的语法现象,如类、接口、方法、属性等。
CodeDOM通过对象模型对语言进行了抽象,然后利用具体语言所提供的生成源代码的机制来生成源代码,并可调用相应的编译器将源码生成*.dll或*.exe。从而可以达到与语言无关的目的。图1描述了使用CodeDOM
生成和编译源代码的过程。


图1 CodeDom生成和编译源代码的过程

从上图可以看出,CodeWizard只使用CodeDOM对语言进行抽象,然后通过CodeDomProvider生成源代码。最后通过编译器生成中间语言。下面将详细讨论如何利用CodeDOM来实现CodeWizard。

二、实现CodeWizard
下 面要实现的这个CodeWizard非常简单。其功能主要是将一个数据表映射成一个类。这个类提供了Add和Save方法以及和数据表的每个字段相对应的 属性。使用这个类可以向数据表添加记录。为了便于描述,将这个数据表保存成xml文件格式。每条记录为一个item结点,每一个字段为这个结点的一个属 性。表名为这个xml文件的根结点名称。这个xml文件的格式如下所示:
<MyTable>                                 
<item id = "01" name = "Bill"/>
<item id = "02" name = "Mike" />
</MyTable>
这个CodeWizard通过一个模板文件来定义数据表的结构。模板文件的格式如下:
<MyTable>
<id type = "System.Int32"/>
<name type = "System.String"/>
</MyTable>
其 中type为字段的类型,它的值是在.net framework中的System中定义的简单类型,如System.Int32、System.String、System.Char等。下面就详细 讨论如何利用这个模板文件和CodeDOM技术来生成C#和VB.net的源代码。
   
CodeDOM的结构
CodeDOM由两部分组成:
1. 用 于描述抽象代码结构的一组类。其中CodeCompileUnit类是这些类的根。代表一个源码文件(如C#的*.cs和VB.net的*.vb)。在使 用CodeDOM时,必须先建立一个CodeCompileUnit类的对象,然后在这个对象中加入必要的namespace、class等面向对象元 素。
2.     用于生成和编译源代码的类。这个类必须从CodeDomProvider类继承。每种.net framework
所支持的语言都有自己的CodeDomProvider类。如在C#中的CodeDomProvider类叫CSharpCodeProvider,而在VB.net中叫VBCodeProvider

三、数据表类的定义
要用CodeDOM定义一个类需要三步:
1. 建立一个CodeCompileUnit对象。这个类相当于一个源码文件。
2. 建立一个CodeNamespace对象。理论上在.net framework上运行的程序语言,如C#、VB.net
等,可以没有namespace。但在CodeDOM中必须使用这个类,如果不想要namespace,可以将namespace的名字设为 null或空串。
2.     建立一个CodeTypeDeclaration对象。这个类可以建立Class和Interface两种Type。在
这个例子中只建立Class。如果想建立Interface,只需将IsInterface属性设为true即可。
主要的实现代码如下:

private CodeCompileUnit m_CodeCompileUnit; private CodeNamespace m_CodeNameSpace; private CodeTypeDeclaration m_Class; private void InitCodeDom() { m_CodeCompileUnit = new CodeCompileUnit(); m_CodeNameSpace = new CodeNamespace("xml.tables"); m_CodeCompileUnit.Namespaces.Add(m_CodeNameSpace); m_Class = new CodeTypeDeclaration(m_ClassName); m_CodeNameSpace.Types.Add(m_Class); }

    其中namespace的名子是“xml.tables”。在建立完namespace后,将其加入到m_CodeCompileUnit的 Namespaces集合中。m_ClassName是一个String变量,它的值就是数据表的表名。最后将所建立的类加入到namespace的 Types集合中。在产生完类后。需要在这个类中加入四部分内容,它们分别是:全局变量、属性、构造函数和方法(Add和Save方法)。下面就分别讨论 它们的实现过程。

 
全局变量的生成
这个数据表类中有四种全局变量:用于操作xml文件的类型为XmlDocument的变量、用于保存数据表文件名的变量、用于确定是否为加入状态的Boolean型变量、以及用于保存每个字段值的变量组。具体实现代码如下:

private void GenerateFields() { // 产生 "private XmlDocument m_xml = new XmlDocument();" CodeMemberField xml = new CodeMemberField("System.Xml.XmlDocument", "m_xml"); CodeObjectCreateExpression createxml = new CodeObjectCreateExpression("System.Xml.XmlDocument"); xml.InitExpression = createxml; m_Class.Members.Add(xml); // 产生 "private String m_XmlFile;" CodeMemberField xmlfile = new CodeMemberField("System.String", "m_XmlFile"); m_Class.Members.Add(xmlfile); // 根据模板文件产生保存字段值的变量 String fieldname = "", fieldtype = ""; foreach (XmlNode xn in m_Xml.DocumentElement.ChildNodes) { fieldname = "m_" + xn.Name; fieldtype = xn.Attributes["type"].Value; CodeMemberField field = new CodeMemberField(fieldtype, fieldname); m_Class.Members.Add(field); } // 产生 "private bool m_AddFlag;" CodeMemberField addflag = new CodeMemberField("System.Boolean", "m_AddFlag"); m_Class.Members.Add(addflag); }


    在以上代码中每段程序上方的注释是它们所生成的C#源代码。在输入这段代码之前,需要引入两个namespace。

using System.CodeDom;
using System.CodeDom.Compiler;

四、属性的生成

 
    在数据表类中每个属性代表数据表的一个字段,名子就是字段名。这些属性和保存字段的全局变量一一对应。下面是具体的实现代码:

private void GenerateProperties() { String fieldname = "", fieldtype = ""; foreach (XmlNode xn in m_Xml.DocumentElement.ChildNodes) { fieldname = xn.Name; fieldtype = xn.Attributes["type"].Value; CodeMemberProperty property = new CodeMemberProperty(); property.Attributes = MemberAttributes.Public | MemberAttributes.Final; property.Name = fieldname; property.Type = new CodeTypeReference(fieldtype); property.HasGet = true; property.HasSet = true; CodeVariableReferenceExpression field = new CodeVariableReferenceExpression("m_" + fieldname); // 产生 return m_property CodeMethodReturnStatement propertyReturn = new CodeMethodReturnStatement(field); property.GetStatements.Add(propertyReturn); // 产生 m_property = value; CodeAssignStatement propertyAssignment = new CodeAssignStatement(field, new CodePropertySetValueReferenceExpression()); property.SetStatements.Add(propertyAssignment); m_Class.Members.Add(property); } }


    这些生成的属性是可读写的。这就需要将HasGet和HasSet两个属性设为true,然后分别将get和set方法中的语句分别加到GetStatements和SetStatements中。

构造函数的生成
    构造函数的主要工作是打开数据表。如果数据表不存在,就创建这个数据表文件。在编写代码之前,需要先定义三个全局变量。因为这三个全局变量在程序中会多次 用到。它们的类型都是CodeVariableReferenceExpression。这个类型变量其实在生成源码中的作用就是对某一个变量的引用。具 体的实现代码如下:

private CodeVariableReferenceExpression m_XmlFileExpression; private CodeVariableReferenceExpression m_XmlExpression; private CodeVariableReferenceExpression m_AddFlagExpression; private void InitVariants() { m_XmlFileExpression = new CodeVariableReferenceExpression("m_XmlFile"); m_XmlExpression = new CodeVariableReferenceExpression("m_xml"); m_AddFlagExpression = new CodeVariableReferenceExpression("m_AddFlag"); }

下面是生成构造函数的源代码:


Add和Save方法生成
Add方法只有一条语句,功能是将m_AddFlag设为true,以使数据表类处于加入状态。Save方法比较复杂。它的功能是当m_AddFlag为true时在数据表文件的最后加入一条记录,并保存。具体实现代码如下:
private void GenerateMethods() { CodeTypeReference voidReference = new CodeTypeReference("System.void"); //产生Add方法 CodeMemberMethod add = new CodeMemberMethod(); add.ReturnType = voidReference; add.Name = "add"; add.Attributes = MemberAttributes.Public | MemberAttributes.Final; CodeAssignStatement assignAddTrue = new CodeAssignStatement(m_AddFlagExpression, new CodePrimitiveExpression(true)); add.Statements.Add(assignAddTrue); m_Class.Members.Add(add); //产生Save方法 CodeMemberMethod save = new CodeMemberMethod(); save.ReturnType = voidReference; save.Name = "save"; save.Attributes = MemberAttributes.Public | MemberAttributes.Final; System.Collections.Generic.List<CodeStatement> ifStatements = new System.Collections.Generic.List<CodeStatement>(); //产生 "XmlNode xn = m_xml.CreateNode(XmlNodeType.Element, "item", "");" CodeVariableDeclarationStatement xmlNode = new CodeVariableDeclarationStatement("System.Xml.XmlNode", "xn"); CodeMethodInvokeExpression createNode = new CodeMethodInvokeExpression(m_XmlExpression, "CreateNode", new CodeExpression[] {new CodeVariableReferenceExpression("System.Xml.XmlNodeType.Element"), new CodePrimitiveExpression("item"), new CodePrimitiveExpression("") }); xmlNode.InitExpression = createNode; ifStatements.Add(xmlNode); //产生 "XmlAttribute xa = null; " CodeVariableDeclarationStatement xmlAttr = new CodeVariableDeclarationStatement("System.Xml.XmlAttribute", "xa"); xmlAttr.InitExpression = new CodePrimitiveExpression(null); ifStatements.Add(xmlAttr); //产生字段属性 CodeStatementCollection statements = new CodeStatementCollection(); foreach (XmlNode xn in m_Xml.DocumentElement.ChildNodes) { CodeMethodInvokeExpression createAttribute = new CodeMethodInvokeExpression(m_XmlExpression, "CreateAttribute", new CodePrimitiveExpression(xn.Name)); CodeAssignStatement assignxa = new CodeAssignStatement(new CodeVariableReferenceExpression("xa"), createAttribute); CodeMethodInvokeExpression invokeToString = new CodeMethodInvokeExpression(new CodeVariableReferenceExpression("m_" + xn.Name), "ToString"); CodeAssignStatement assignValue = new CodeAssignStatement(new CodeVariableReferenceExpression("xa.Value"), invokeToString); CodeMethodInvokeExpression invokeAppend = new CodeMethodInvokeExpression(new CodeVariableReferenceExpression("xn.Attributes"), "Append", new CodeVariableReferenceExpression("xa")); statements.Add(invokeAppend); ifStatements.Add(assignxa); ifStatements.Add(assignValue); ifStatements.Add(statements[0]); } // 产生 "m_xml.DocumentElement.AppendChild(xn);" CodeMethodInvokeExpression invokeAppendChild = new CodeMethodInvokeExpression(new CodeVariableReferenceExpression("m_xml.DocumentElement"), "AppendChild", new CodeVariableReferenceExpression("xn")); statements.Clear(); statements.Add(invokeAppendChild); ifStatements.Add(statements[0]); // 产生 "m_xml.Save(m_XmlFile);" CodeMethodInvokeExpression invokeSave = new CodeMethodInvokeExpression(m_XmlExpression, "Save", m_XmlFileExpression); statements.Clear(); statements.Add(invokeSave); ifStatements.Add(statements[0]); // 产生 "m_AddFlag = false;" CodeAssignStatement assignAddFalse = new CodeAssignStatement(m_AddFlagExpression, new CodePrimitiveExpression(false)); ifStatements.Add(assignAddFalse); // 产生if语句: "if (m_AddFlag)" CodeConditionStatement ifStatement = new CodeConditionStatement(m_AddFlagExpression, ifStatements.ToArray()); save.Statements.Add(ifStatement); m_Class.Members.Add(save); }

 

五、生成源代码
生成具体语言的源代码需要一个从CodeDomProvider继承的类。对于C#而言是CSharpCodeProvider类。实现代码如下:

using Microsoft.CSharp; public void SaveCSharp(String filename) { IndentedTextWriter tw = new IndentedTextWriter(new StreamWriter(filename, false), " "); CodeDomProvider provide = new CSharpCodeProvider(); provide.GenerateCodeFromCompileUnit(m_CodeCompileUnit, tw, new CodeGeneratorOptions()); tw.Close(); }

    在使用CSharpCodeProvider类时需要用到m_CodeCompileUnit这个全局变量。这样可产生一个*.cs文件。以上代码中的 IndentedTextWriter类是建立一个文件的Writer,用于向这个文件中输出源代码。但和其它的Writer不同的是它的输出是缩进的 (以四个空格进行缩进)。如是想生成VB.net的代码,只需将CSharpCodeProvider改为VBCodeProvider即可。

 
编译源代码
到 现在为止,这个数据表类的源代码已经全部生成了。你可以将这个源文件直接加入到自己的工程中。或者直接将其编译成*.dll文件,然后在程序中调用。如果 想编译,可以直接调用指定语言的编译器(如C#中的csc.exe)。但这样不是太方便。在CodeDOM中提供了一种机制,可以在程序中通过 CodeDomProvider直接调用指定语言的编译器。下面是编译C#源程序的一个例子。
 

public void CompileCSharp(String sourcefile, String targetFile) { CompilerParameters cp = new CompilerParameters(new String[] { "System.Xml.dll" }, targetFile, false); CodeDomProvider provider = new CSharpCodeProvider(); cp.GenerateExecutable = false; // 调用编译器 CompilerResults cr = provider.CompileAssemblyFromFile(cp, sourcefile); if (cr.Errors.Count > 0) { // 显示编译错误 foreach (CompilerError ce in cr.Errors) System.Windows.Forms.MessageBox.Show(ce.ToString()); } }

    对于以上代码有两点说明:

  1. 使用CodeDomProvider调用编译器时也需要传递相应的参数,如在本例中将System.Xml.dll
作为一个参数,表示目标文件需要调用这个dll中的资源。
  1. 在调用编译器后,如果出现错误,可使用cr.Errors获得错误信息。
结束语
我花了一个晚上的时间实现了这个简单的例子,并用C#2.0调试通过,只是为了抛砖引玉。自动生成源代码有很多的方法,但使用CodeDom生成源代码会有更大的灵活性,主要表现在以下三个方面:
1.     语言无关。即只要是.net framework所支持的语言,并且这种语言提供了CodeDomProvider。
就可以生成这种语言的源代码。
2.     如果所生成的语言是测试版或要将这种语言升级到下一个版本,也可以考虑使用CodeDOM。
因为当这种语言的语法有所变化时,CodeDomProvider也会随之升级。因此,使用CodeDOM的Code Wizards也会随着CodeDOM而升级,这样就不必修改Code Wizards的源代码了。
3.     如果所生成的一种语言是你所不熟悉的,如果不使用CodeDOM,必须要熟悉这种语言的语
法,才能生成它的源代码。而使用CodeDOM却可以避免这一点。因为CodeDOM是使用抽象的object graph来描述语言的。而语言的具体语法是由CodeDomProvider所决定的。
 
其实CodeDOM不仅可以用在Code Wizards上,也可以用在许多其它地方,如可以生成Web

Services的客户端代理(Client Proxies),或根据UML图生成类的构架代码。总之,使用CodeDom可以大大降低和语言的偶合度,并且很容易维护和升级系统。

[ORM]使用CodeDom提高ORM性能

mikel阅读(881)

使用CodeDom提高ORM性能

下载本文代码:http://www.cnblogs.com/Files/afritxia2008/WebTest.rar(请使用 Visual Studio 2008 打开)
  在进行讨论之前,我假设读者已经了解
.NET反射、自定义属性、CodeDom这些技术。并接触过ORM框架源码,如果对ORM并不了解,可以参考:
http://www.cnblogs.com/xdesigner/archive/2008/06/24/1228702.html。在这篇文章中,我们主要讨论通过CodeDom提高ORM读取数据的性能问题。

  ORMObject/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[0as 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, nullnull);
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

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

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

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

 1using System;
 2using System.Collections.Generic;
 3using System.Data.Common;
 4using System.Reflection;
 5
 6using Net.AfritXia.Data.Mapping;
 7
 8namespace 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:

 

 1using System;
 2using System.Collections;
 3using System.Reflection;
 4
 5namespace 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
  3using System;
  4using System.CodeDom;
  5using System.Collections.Specialized;
  6using System.CodeDom.Compiler;
  7using System.Data.Common;
  8#if _Debug
  9using System.IO;
 10#endif
 11using System.Reflection;
 12
 13using Microsoft.CSharp;
 14
 15using Net.AfritXia.Data.Mapping;
 16
 17namespace 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[0as 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(可以运行该测试文件)