[观点]RAM硬盘
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 White是Hadoop 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重写
前一段时间做一个网站项目(运行于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>
密码:<input type=password name=pass size=20>
<input type=submit value=登陆> <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,1) NOT 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的相关网站
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三大对象的属性、事件、方法
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操作过程中一个未预料到的事件被激活->
[代码]生成验证码的控件
看过网上很多关于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), 0, 0, 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网站
博客园,博客堂,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 图片切换展示效果
看到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()函数来获取步长:
用目标值减当前值再除以一个参数,得到步长,
这样取得的步长在当前值越接近目标值时会越来越小,也做成了减速的效果,
然后在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", 168, 3, {
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 <= 0) return;
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 == 0) return 0;
if (Math.abs(iStep) < 1) return (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实现语言无关的代码生成器
一、什么是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。
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方法)。下面就分别讨论 它们的实现过程。
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。
四、属性的生成
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中。
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方法生成
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); }
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即可。
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()); } }
对于以上代码有两点说明:
- 使用CodeDomProvider调用编译器时也需要传递相应的参数,如在本例中将System.Xml.dll
- 在调用编译器后,如果出现错误,可使用cr.Errors获得错误信息。
Services的客户端代理(Client Proxies),或根据UML图生成类的构架代码。总之,使用CodeDom可以大大降低和语言的偶合度,并且很容易维护和升级系统。
[ORM]使用CodeDom提高ORM性能
使用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读取数据的性能问题。
ORM(Object/Relation Mapping对象–关系数据库映射)其中的一个功能是将数据源数据赋值给实体。实现方法是利用自定义属性和.NET反射机制。例如:
public class LWordEntity2
{3
/// <summary>4
/// 获取或设置留言 ID5
/// </summary>6
[DataColumn(ColumnName = "LWordUID")]7
public int LWordUID8
{9
// 
10
}11

12
/// <summary>13
/// 获取或设置发送用户14
/// </summary>15
[DataColumn(ColumnName = "PostUser")]16
public string PostUser17
{18
// 
19
}20

21
/// <summary>22
/// 获取或设置发送时间23
/// </summary>24
[DataColumn(ColumnName = "PostTime")]25
public DateTime PostTime26
{27
// 
28
}29

30
/// <summary>31
/// 获取或设置文本内容32
/// </summary>33
[DataColumn(ColumnName = "TextContent")]34
public string TextContent35
{36
// 
37
}38
}
DataColumn是自定义的属性类,代码并不复杂所以在这里也就省略了。接下来需要通过反射读取自定义属性,并赋值。代码如下:
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
try26
{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
else45
{46
// 将值设置到属性47
propInfo.SetValue(objEntity, objValue, null);48
}49
}50
}51

以上代码实现 了读取数据源数据并向实体赋值的功能。但这样做速度非常慢,因为每读取一条数据库记录,每读取一个数据字段并向实体赋值的时候,都必须进行一次反射操作。 数据量越大,且数据字段或实体属性越多,那么速度就越慢!在以上代码中,对实体的反射操作,其目的就是赋值。可以被等价的语句所替代:
entity.Prop = dr[“Porp”];
用简单的赋值语句肯定要比反射的速度快很多,而大数据量和多数据库字段对其影响也不是很大。不过需要注意的是因为每一个实体的具体属性不相同,所以赋值过程也是不相同的。例如:
News实体赋值代码:
void PutEntityProperties(NewsEntity entity, DbDataReader dr)2
{3
// 新闻 ID4
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实体赋值代码:
void PutEntityProperties(UserEntity entity, DbDataReader dr)2
{3
// 用户 ID4
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类用白色表示。具体代码如下:
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.Data9
{10
partial class SQLHelper11
{12
public void PutEntityProperties<T>(T entity, DbDataReader dr) where T : class13
{14
// 获取设置器15
IEntityPropertyPutter<T> putter = EntityPropertyPutterFactory.Create<T>(entity, this.IncludeDebugInformation);16

17
if (putter == null)18
throw new NullReferenceException(@"设置器为空( Null Putter )");19

20
try21
{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:
using System;2
using System.Collections;3
using System.Reflection;4

5
namespace Net.AfritXia.Data6
{7
/// <summary>8
/// 实体属性设置器工厂类9
/// </summary>10
internal sealed class EntityPropertyPutterFactory11
{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 : class23
{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
else43
{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:
#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 _Debug9
using System.IO;10
#endif11
using System.Reflection;12

13
using Microsoft.CSharp;14

15
using Net.AfritXia.Data.Mapping;16

17
namespace Net.AfritXia.Data18
{19
/// <summary>20
/// 构建实体属性设置器21
/// </summary>22
internal sealed class EntityPropertyPutterMaker23
{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 IncludeDebugInformation45
{46
set47
{48
this.m_includeDebugInfo = value;49
}50

51
get52
{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 : class63
{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 _Debug97
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
#endif110

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 : class149
{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 : class173
{174
if (targetClass == null)175
throw new ArgumentNullException("targetClass");176

177
/* 178
* 以下代码将生成179
* 180
* public string EntityTypeName181
* {182
* get183
* {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
* get230
* {231
* return this.m_currPropName;232
* }233
* }234
* 235
*/236

237
// 变量名称238
const string VaribleName = "m_currPropName";239

240
// m_currPropName241
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
// CurrentPropName254
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
* get292
* {293
* return this.m_currDBColName;294
* }295
* }296
* 297
*/298

299
// 变量名称300
const string VaribleName = "m_currDBColName";301
// m_currDBColName302
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
// CurrentDBColName315
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 : class342
{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
// 添加参数 entity355
method_code.Parameters.Add(new CodeParameterDeclarationExpression(typeof(T), "entity"));356
// 添加参数 dr357
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
// 实体变量名称 entity412
string varEntityName = targetMethod.Parameters[0].Name;413
// 数据源变量名称 dr414
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.Prop430
targetMethod.Statements.Add(new CodeAssignStatement(431
new CodeVariableReferenceExpression("this.m_currPropName"),432
new CodePrimitiveExpression(prop.Name)));433

434
// this.m_currDBColName = attributeName435
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
else467
{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(可以运行该测试文件)
Mikel
