﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>IT博客-Welcome To Oliver_Yin's  Blog!-文章分类-数据库知识</title><link>http://www.cnitblog.com/oliver_yin/category/510.html</link><description>打字--&gt; 死打字--&gt;抄写程序--&gt;研究程序--&gt;分析需求 
--&gt;分析架构--&gt;项目管理--&gt;企业信息化--&gt;.........</description><language>zh-cn</language><lastBuildDate>Thu, 29 Sep 2011 23:29:24 GMT</lastBuildDate><pubDate>Thu, 29 Sep 2011 23:29:24 GMT</pubDate><ttl>60</ttl><item><title>漫谈.Net PetShop和Duwamish ADO.NET数据库编程</title><link>http://www.cnitblog.com/oliver_yin/articles/5513.html</link><dc:creator>生活像一团麻</dc:creator><author>生活像一团麻</author><pubDate>Sat, 17 Dec 2005 09:17:00 GMT</pubDate><guid>http://www.cnitblog.com/oliver_yin/articles/5513.html</guid><wfw:comment>http://www.cnitblog.com/oliver_yin/comments/5513.html</wfw:comment><comments>http://www.cnitblog.com/oliver_yin/articles/5513.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/oliver_yin/comments/commentRss/5513.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/oliver_yin/services/trackbacks/5513.html</trackback:ping><description><![CDATA[<TABLE class=partsmb cellSpacing=0 border=0>
<TBODY>
<TR>
<TD></TD>
<TD class=bvBody>
<DIV id=entrycns!1pRd_P5HlUt8TMD3Q2NITYMA!173>
<TABLE class="fixedTable blogpost" cellSpacing=0 width="100%" border=0>
<TBODY>
<TR>
<TD class=bvh8></TD></TR>
<TR>
<TD vAlign=top><SPAN class=bold id=LastMDatecns!1pRd_P5HlUt8TMD3Q2NITYMA!173><STRONG>6月27日</STRONG></SPAN></TD></TR>
<TR>
<TD height=4><STRONG></STRONG></TD></TR>
<TR>
<TD class=blackline><STRONG></STRONG></TD></TR>
<TR>
<TD height=4><STRONG></STRONG></TD></TR>
<TR>
<TD class=ellipse><SPAN class=bvTitle id=subjcns!1pRd_P5HlUt8TMD3Q2NITYMA!173><STRONG>漫谈.Net PetShop和Duwamish ADO.NET数据库编程-1</STRONG></SPAN></TD></TR>
<TR>
<TD class=bvh8><STRONG></STRONG></TD></TR>
<TR>
<TD id=msgcns!1pRd_P5HlUt8TMD3Q2NITYMA!173>
<P><STRONG>概述<BR><BR></STRONG>　　ADO.Net为我们提供了强大的数据库开发能力，它内置的多个对象为我们的数据库编程提供了不同的选择。但是在允许我们灵活选用的同时，许多初学者也很迷惑，我到底是应该使用DataReader还是应该使用DataAdapter？我只想读取一小部分数据，难道我一定要Fill满整个DataSet吗？为什么DataReader不能和RecordSet一样提供一个数据更新的方法？DataSet到底有什么好处？<BR><BR>　　在本文中，我将对.Net PetShop的数据库编程模式和Duwamish的数据库编程模式进行一些简单的分析和对比。如果您也有以上疑问的话，相信在读完本文之后，就可以根据具体的需要来制定一个最适合您应用的数据库编程模式。<BR><BR>　　<B>.Net PetShop和Duwamish简单介绍</B><BR><BR>　　相信大家一定听说过有名的"宠物店大战"，没错，本文的主角之一就是获胜方.Net PetShop，微软号称以27倍的速度和1/4的代码量遥遥领先于基于J2EE的PetStore宠物商店。虽然SUN也曾对此抱怨过不满，指责此"大战"有水分，不过无论如何，.Net PetShop绝对是一个经典的.Net实例教程，至少为我们提供了一条赶超J2EE的“捷径=" ：）,它的下载地址是：<A href="http://www.gotdotnet.com/team/compare " target=_blank><FONT color=#000000>http://www.gotdotnet.com/team/compare </FONT></A><BR></P>
<P align=center><BR><IMG height=16 src="http://www.webabc.com.cn/servers/bbs/skins/default/filetype/gif.gif" width=16 border=0>此主题相关图片如下：<BR><A id=ImgSpan href="http://www.webabc.com.cn/servers/bbs/UploadFile/2005-6/2005639241295131.gif" target=_blank><IMG height=251 alt=按此在新窗口浏览图片 src="http://www.webabc.com.cn/servers/bbs/UploadFile/2005-6/2005639241295131.gif" width=300 border=0></A></P>
<P align=center>.Net PetShop宠物网上商店首页</P>
<P>　　 而Duwamish则是一个外表简单，内部却极其复杂的一个网上书店的.Net完整应用范例，作为一个微软官方的Sample，它同时提供了C#和VB.Net两种语言版本，并且还附上了大量详尽的中文资料，如果打印出来，实在是居家旅行，临睡入厕必备之物。什么？您没听说过？呵呵，如果您装了Visual Studio .Net的话，它就在您的硬盘上静静的躺着呢，不过还没有被安装，您可以在您的vs.net 的Enterprise Samples目录下找到并安装它，例如：C:\Program Files\Microsoft Visual Studio .NET\Enterprise Samples\Duwamish 7.0 CS。</P>
<P align=center><BR><BR>Duwamish网上电子书店首页</P>
<P>　　<B>结构简述</B><BR><BR>　　两家商店都采用了n层应用结构（毫无疑问，n层结构的应用架构应该绝对是您开发.Net应用的首选，哪怕您只想做一个网页计数器），不同的是，PetShop采用的是最常见的三层应用结构，分别为表示层，中间层和数据层。而Duwamish则采用的是一个四层应用结构，并使用不同的项目分隔开，分别为表示层，业务外观层，业务规则层和数据层。至于这两种结构分别有什么优点和缺点，以及为什么要这么分层，我们不进行详细讨论，因为本文的重点不在于此。我们主要分析的是他们的数据库编程的模式。<BR><STRONG>Duwamish数据访问剖析<BR><BR></STRONG>　　首先，我们来看看Duwamish书店，它采用的是DataAdapter和DataSet配合的数据存储模式，所不同的是，它对DataSet进行子类化扩展作为数据载体，也就是采用定制的DataSet来进行层间的数据传输，下面是一个定制的DataSet示例：<BR><BR>public class BookData : DataSet<BR>&nbsp;&nbsp;&nbsp;&nbsp; {<BR>public BookData()<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Create the tables in the dataset<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; BuildDataTables();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private void BuildDataTables()<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Create the Books table<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DataTable table&nbsp;&nbsp; = new DataTable(BOOKS_TABLE);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DataColumnCollection columns = table.Columns;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp; columns.Add(PKID_FIELD, typeof(System.Int32));<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;columns.Add(TYPE_ID_FIELD, typeof(System.Int32));<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;columns.Add(PUBLISHER_ID_FIELD, typeof(System.Int32));<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;columns.Add(PUBLICATION_YEAR_FIELD, typeof(System.Int16));<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;columns.Add(ISBN_FIELD, typeof(System.String));<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;columns.Add(IMAGE_FILE_SPEC_FIELD, typeof(System.String));<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;columns.Add(TITLE_FIELD, typeof(System.String));<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;columns.Add(DESCRIPTION_FIELD, typeof(System.String));<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;columns.Add(UNIT_PRICE_FIELD, typeof(System.Decimal));<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;columns.Add(UNIT_COST_FIELD, typeof(System.Decimal));<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;columns.Add(ITEM_TYPE_FIELD, typeof(System.String));<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;columns.Add(PUBLISHER_NAME_FIELD, typeof(System.String));<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;this.Tables.Add(table);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>………<BR>}</P>
<P>　　我们可以看到它有一个BuildDataTables方法，并且在构造函数中调用，这样，定制的Books表就和这个DataSet捆绑在一起了，省得以后还要进行Column Mapping，这真是个好主意，我怎么就没有想到呢？ ：）<BR><BR>　　解决了数据结构，接下来看看数据层的代码实现，在Duwamish中，数据层中有5个类，分别是Books，Categories，Customers和Orders，每个类分别只负责有关数据的存取。下面是其中一个类的示例代码：<BR></P>
<P>private SqlDataAdapter dsCommand;<BR>public BookData GetBookById(int bookId)<BR>{<BR>&nbsp;&nbsp;&nbsp;&nbsp; return FillBookData("GetBookById", "@BookId", bookId.ToString());<BR>}<BR>private BookData FillBookData(String commandText, String paramName, String paramValue)<BR>{<BR>&nbsp;&nbsp;&nbsp;&nbsp;if (dsCommand == null )<BR>&nbsp;&nbsp;&nbsp;&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;throw new System.ObjectDisposedException( GetType().FullName );<BR>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;BookData&nbsp;&nbsp; data&nbsp;&nbsp;&nbsp;&nbsp;= new BookData();<BR>&nbsp;&nbsp;&nbsp;&nbsp;SqlCommand command = dsCommand.SelectCommand;<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;command.CommandText = commandText;<BR>&nbsp;&nbsp;&nbsp;&nbsp;command.CommandType = CommandType.StoredProcedure; // use stored proc for perf<BR>&nbsp;&nbsp;&nbsp;&nbsp;SqlParameter param = new SqlParameter(paramName, SqlDbType.NVarChar, 255);<BR>&nbsp;&nbsp;&nbsp;&nbsp;param.Value = paramValue;<BR>&nbsp;&nbsp;&nbsp;&nbsp;command.Parameters.Add(param);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;dsCommand.Fill(data);<BR>&nbsp;&nbsp;&nbsp;&nbsp;return data;<BR>}</P>
<P>　　这里就是数据层的代码了，我们在这里可以看到Duwamish采用了DataAdapter来将数据填充到定制的DataSet中，然后返回该DataSet。我感到很奇怪的是在数据存取层中竟然可以看到GetBookById这样具体的数据存取方法，虽然最后还是有一个抽象出来的FillBookData方法，但是上面还有三层啊，底层都做到这份上了，那上层都做些什么呢？答案是数据检查，上层基本上都在做一些很严密的数据合法性校验（当然也会包括一些比较复杂的事务逻辑，但是并不多），示例代码如下：<BR></P>
<P>public CustomerData GetCustomerByEmail(String emailAddress, String password)<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Check preconditions<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ApplicationAssert.CheckCondition(emailAddress != String.Empty, "Email address is required",<BR>ApplicationAssert.LineNumber);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ApplicationAssert.CheckCondition(password != String.Empty, "Password is required", <BR>ApplicationAssert.LineNumber);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Get the customer dataSet<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CustomerData dataSet;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;using (DataAccess.Customers customersDataAccess = new DataAccess.Customers())<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dataSet = customersDataAccess.LoadCustomerByEmail(emailAddress);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Verify the customer's password<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DataRowCollection rows = dataSet.Tables[CustomerData.CUSTOMERS_TABLE].Rows;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if ( ( rows.Count == 1 ) &amp;&amp; rows[0][CustomerData.PASSWORD_FIELD].Equals(password) )<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return dataSet;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return null;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} </P>
<P>　　在这个方法中，真正进行数据存取的实际上只有<BR><BR>　　dataSet = customersDataAccess.LoadCustomerByEmail(emailAddress);<BR><BR>　　这么一句，是直接调用的数据层。其它都是在进行合法性校验，我们可以感悟到，进行一个真正的企业级开发需要考虑的系统健壮性有多么重要。<BR><STRONG>.Net PetShop数据访问剖析<BR><BR></STRONG>　　OK，Duwamish看完了，下面我们来看看PetShop的数据访问机制。<BR><BR>　　PetShop只有一个项目，它采用的分层办法是将中间层和数据层都写成cs文件放在Components目录里，其中数据层就是一个名为Database的类，它封装了所有对数据库的底层操作。下面是示例代码段：<BR><BR>public void RunProc(string procName, out SqlDataReader dataReader) {<BR>SqlCommand cmd = CreateCommand(procName, null);<BR>dataReader = cmd.ExecuteReader(System.Data.CommandBehavior.CloseConnection);<BR>}</P>
<P>　　我们看到了一个跟Duwamish截然不同的另一种数据访问方式，它将所有的数据访问方法抽象出来做成一个RunProc方法，至于返回数据呢，呵呵，它有点偷懒，直接返回一个DataReader给你，你自己去读吧。还记得Duwamish采用的层间数据传输载体是什么吗？对了，是DataSet，它被数据层填充后返回给了中间层。但是这里，数据层和传输层的数据传输载体变成了DataReader，实际上，还不能称它为数据载体，因为数据还没开始读呢，在这里，DataReader的作用和指针有点类似，也许我们应该称它为“数据引用”：）<BR><BR>　　接着往下看，DataReader被怎么“处理”的：<BR></P>
<P>public ProductResults[] GetList(string catid, int currentPage, int pageSize, ref int numResults) <BR>{<BR>numResults = 0;<BR>int index=0;<BR>SqlDataReader reader = GetList(catid);<BR>ProductResults[] results = new ProductResults[pageSize];<BR><BR>// now loop through the list and pull out items of the specified page<BR>int start = (int)((currentPage - 1) * pageSize);<BR>if (start &lt;= 0) start = 1;<BR><BR>// skip <BR>for (int i = 0; i &lt; start - 1; i++) {<BR>if (reader.Read()) numResults++;<BR>}<BR>if (start &gt; 1) reader.Read();<BR><BR>// read the data we are interested in<BR>while (reader.Read()) {<BR>if (index &lt; pageSize) {<BR>results[index] = new ProductResults();<BR>results[index].productid = reader.GetString(0);<BR>results[index].name =&nbsp;&nbsp;reader.GetString(1);<BR>index++;<BR>}<BR>numResults++;&nbsp;&nbsp; <BR>}<BR><BR>reader.Close();<BR><BR>// see if need to redim array<BR>if (index == pageSize)<BR>return results;<BR>else {<BR>// not a full page, redim array<BR>ProductResults[] results2 = new ProductResults[index];<BR>Array.Copy(results, results2, index);<BR>return results2;<BR>} <BR>}</P>
<P>　　注意到currentPage和pageSize了吗？原来在这里就进行了数据分页，只返回满足需要的最少的数据量，而不是象我们很多喜欢偷懒的人一样，简单的将整个DataTable一股脑的绑定到DataGrid，造成大量的数据冗余。<BR><BR>　　在这里，数据被真正的读出来，并且被手动填充到一个自定义的对象数组中，我们来看看这个数组的定义：<BR></P>
<P>public class ProductResults <BR>{<BR>private string m_productid;<BR>private string m_name;<BR><BR>// product props<BR>public string productid {<BR>get { return m_productid; }<BR>set { m_productid = value; }&nbsp;&nbsp;<BR>}<BR><BR>public string name {<BR>get { return m_name; }<BR>set { m_name = value; }&nbsp;&nbsp;<BR>}<BR>}</P>
<P>　　非常之简单，不过我有点奇怪为什么不使用struct呢？是不是.Net中struct和class的性能差距已经可以忽略不计了？<BR><BR>　　<B>分析总结</B><BR><BR>　　通过观察这两个商店的具体实现，我们得到了两个不同的数据访问模式，Duwamish采用的是以DataSet为核心，因为DataSet提供了这方面大量的相关方法，所以整个应用的数据传输，数据格式定义，数据校验都围绕着DataSet来进行，整个架构定义非常清晰和严谨，但是却显得有些庞大。PetShop在整个程序中没有采用一个DataSet，程序非常的简洁，轻灵，但是没有Duwamish那么强的健壮性。这两个程序是Microsoft公司不同的小组写出来的代码，所以有着不同风格。不过都应该能代表.Net的标准模式。看到这里，你应该对文章开头提出的那些疑问有一个比较形象的认识了吧。<BR><BR>　　另外，请再次注意，PetShop在打开数据连接之后，并没有马上读取数据，而是将DataReader传递给另外的对象来执行数据读的操作，然后才关闭连接。这样，数据连接的时间加长了，而数据库连接是一项非常宝贵的服务器资源，相比之下，Dawamish在连接数据库之后马上进行填充，然后迅速释放掉数据库连接的方式更加有利于大量用户的并发访问。<BR><BR>　　再一点，上文的程序中没有提到更新操作，PetShop采用的是使用Command对象执行单个存储过程的方式来进行更新操作，是属于一种在线即时数据更新模式。而Dawamish采用的是DataAdapter的Update方法，将DataSet的改变一次性的提交到数据库中，属于离线数据更新模式。这种模式的好处是可以一次性更新大批量数据，减少数据库的连接次数。缺点是如果数据库在改动非常频繁的情况下需要实时的跟踪数据变化就不合适了。需要根据具体的情况采用具体的数据更新办法。<BR><BR>　　总的来说，如果您只需要快速的读取数据并显示出来，推荐您采用DataReader，如果您需要对数据进行大量的修改，还有大量并发访问的可能，而且不需要实时的跟踪数据库的变化，推荐您使用DataSet。当然，这两种情况有点极端了，实际的应用环境也许有着很复杂的条件，具体需要您自己审时度势，综合采用，不过我个人还是比较喜欢PetShop那种轻灵的风格 ：）<BR><BR>　　本文只尝试对以上两个典型的.Net应用例程的数据访问机制做了一个简单的追踪分析。</P></TD></TR></TBODY></TABLE></DIV></TD></TR></TBODY></TABLE><img src ="http://www.cnitblog.com/oliver_yin/aggbug/5513.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/oliver_yin/" target="_blank">生活像一团麻</a> 2005-12-17 17:17 <a href="http://www.cnitblog.com/oliver_yin/articles/5513.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>sql 多条件查询的一种简单的方法 </title><link>http://www.cnitblog.com/oliver_yin/articles/1420.html</link><dc:creator>生活像一团麻</dc:creator><author>生活像一团麻</author><pubDate>Mon, 08 Aug 2005 08:57:00 GMT</pubDate><guid>http://www.cnitblog.com/oliver_yin/articles/1420.html</guid><wfw:comment>http://www.cnitblog.com/oliver_yin/comments/1420.html</wfw:comment><comments>http://www.cnitblog.com/oliver_yin/articles/1420.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/oliver_yin/comments/commentRss/1420.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/oliver_yin/services/trackbacks/1420.html</trackback:ping><description><![CDATA[以后我们做多条件查询,一种是排列结合,另一种是动态拼接SQL<BR>如:我们要有两个条件,一个日期@addDate,一个是@name<BR>第一种写法是<BR><FONT style="BACKGROUND-COLOR: #ffff99">if (@addDate&nbsp;is not&nbsp;null) and (@name &lt;&gt; '')<BR>select&nbsp;* from table where addDate = @addDate and name = @name<BR>else if&nbsp;(@addDate&nbsp;is not&nbsp;null) and (@name ='')<BR>select&nbsp;* from table where addDate = @addDate <BR>else if(@addDate&nbsp;is&nbsp;&nbsp;null) and (@name&nbsp;&lt;&gt; '')<BR>select&nbsp;* from table where and name = @name<BR>else if(@addDate&nbsp;is&nbsp;&nbsp;null) and (@name&nbsp;= '')<BR>select&nbsp;* from table </FONT><BR>第二种就是动态组成SQL,通过exec来执行，我就不写，<BR>昨天我想到一种办法<BR><FONT style="BACKGROUND-COLOR: #66ff66">select * from table where (addDate = @addDate or @addDate is null)&nbsp;and (name = @name or @name = '')</FONT><BR>结果一调试，成功.<BR><BR><BR>转自： <A href="http://www.cnblogs.com/edobnet/archive/2005/08/07/24781.html#209403">http://www.cnblogs.com/edobnet/archive/2005/08/07/24781.html#209403</A><img src ="http://www.cnitblog.com/oliver_yin/aggbug/1420.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/oliver_yin/" target="_blank">生活像一团麻</a> 2005-08-08 16:57 <a href="http://www.cnitblog.com/oliver_yin/articles/1420.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>怎样设置SQL Server单步Debug方式</title><link>http://www.cnitblog.com/oliver_yin/articles/1366.html</link><dc:creator>生活像一团麻</dc:creator><author>生活像一团麻</author><pubDate>Sat, 06 Aug 2005 01:37:00 GMT</pubDate><guid>http://www.cnitblog.com/oliver_yin/articles/1366.html</guid><wfw:comment>http://www.cnitblog.com/oliver_yin/comments/1366.html</wfw:comment><comments>http://www.cnitblog.com/oliver_yin/articles/1366.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/oliver_yin/comments/commentRss/1366.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/oliver_yin/services/trackbacks/1366.html</trackback:ping><description><![CDATA[1、将服务器【身份验证】属性设置成【混合模式】（window与sql身份验证） <BR>2、在【控制面板】中打开【服务】将【MSSQLSERVER】服务打开【属性】，选择【登录】页面，将登录身份设置成服务器本地帐号和该帐号密码，如administrator，密码123； <BR>3、重新启动sqlserver服务，此时的服务指的是【SQL服务管理器】中的SQL SERVER服务； <BR>假设【帐号】设置为administrator <BR>此时达到的效果是：服务器本地帐号administrator与客户端上的administrator（并且该帐号的密码要与服务器密码相同）可以通过【查询分析器】进行调试； <BR>如果想让【其他帐号】也能够调试，那么还需要如下设置： <BR>1、在【服务器】上运行dcomcnfg.exe； <BR>2、在【默认安全机制】中【默认访问权限】右边点击【编辑默认值】选择允许调试的帐号类型，如users用户类型，sample帐号有包含users组； <BR>3、重新启动sqlserver服务； <BR>3、在客户端上创建与服务帐号密码一样的用户，如sample； <BR>做到这步就可以通过查询分析器的调试功能进行单步调试了。<img src ="http://www.cnitblog.com/oliver_yin/aggbug/1366.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/oliver_yin/" target="_blank">生活像一团麻</a> 2005-08-06 09:37 <a href="http://www.cnitblog.com/oliver_yin/articles/1366.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>查看SQL SERVER 加密存储过程,函数,触发器,视图 j9988（原作）－－转贴</title><link>http://www.cnitblog.com/oliver_yin/articles/1365.html</link><dc:creator>生活像一团麻</dc:creator><author>生活像一团麻</author><pubDate>Sat, 06 Aug 2005 01:33:00 GMT</pubDate><guid>http://www.cnitblog.com/oliver_yin/articles/1365.html</guid><wfw:comment>http://www.cnitblog.com/oliver_yin/comments/1365.html</wfw:comment><comments>http://www.cnitblog.com/oliver_yin/articles/1365.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/oliver_yin/comments/commentRss/1365.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/oliver_yin/services/trackbacks/1365.html</trackback:ping><description><![CDATA[<FONT face=Verdana>关键字 字节不受限制,比SQLexplorer安全,查看所有SYSCOMMENTS内加密的代码 <BR><BR><BR><BR>create PROCEDURE sp_decrypt(@objectName varchar(50))<BR>AS<BR>begin<BR>set nocount on<BR>--CSDN：j9988 copyright:2004.01.05 <BR>--V3.1<BR>--破解字节不受限制，适用于SQLSERVER2000存储过程，函数，视图，触发器<BR>--发现有错，请E_MAIL：CSDNj9988@tom.com<BR>begin tran<BR>declare @objectname1 varchar(100),@orgvarbin varbinary(8000)<BR>declare @sql1 nvarchar(4000),@sql2 varchar(8000),@sql3 nvarchar(4000),@sql4 nvarchar(4000)<BR>DECLARE @OrigSpText1 nvarchar(4000), @OrigSpText2 nvarchar(4000) , @OrigSpText3 nvarchar(4000), @resultsp nvarchar(4000)<BR>declare @i int,@status int,@type varchar(10),@parentid int<BR>declare @colid int,@n int,@q int,@j int,@k int,@encrypted int,@number int<BR>select @type=xtype,@parentid=parent_obj from sysobjects where id=object_id(@ObjectName)<BR><BR>create table #temp(number int,colid int,ctext varbinary(8000),encrypted int,status int)<BR>insert #temp SELECT number,colid,ctext,encrypted,status FROM syscomments WHERE id = object_id(@objectName)<BR>select @number=max(number) from #temp<BR>set @k=0<BR><BR>while @k&lt;=@number <BR>begin<BR>if exists(select 1 from syscomments where id=object_id(@objectname) and number=@k)<BR>begin<BR>if @type='P'<BR>set @sql1=(case when @number&gt;1 then 'ALTER PROCEDURE '+ @objectName +';'+rtrim(@k)+' WITH ENCRYPTION AS '<BR>else 'ALTER PROCEDURE '+ @objectName+' WITH ENCRYPTION AS '<BR>end)<BR><BR>if @type='TR'<BR>set @sql1='ALTER TRIGGER '+@objectname+' ON '+OBJECT_NAME(@parentid)+' WITH ENCRYPTION FOR INSERT AS PRINT 1 '<BR><BR>if @type='FN' or @type='TF' or @type='IF'<BR>set @sql1=(case @type when 'TF' then <BR>'ALTER FUNCTION '+ @objectName+'(@a char(1)) returns @b table(a varchar(10)) with encryption as begin insert @b select @a return end '<BR>when 'FN' then<BR>'ALTER FUNCTION '+ @objectName+'(@a char(1)) returns char(1) with encryption as begin return @a end'<BR>when 'IF' then<BR>'ALTER FUNCTION '+ @objectName+'(@a char(1)) returns table with encryption as return select @a as a'<BR>end)<BR><BR>if @type='V'<BR>set @sql1='ALTER VIEW '+@objectname+' WITH ENCRYPTION AS SELECT 1 as f'<BR><BR>set @q=len(@sql1)<BR>set @sql1=@sql1+REPLICATE('-',4000-@q)<BR>select @sql2=REPLICATE('-',8000)<BR>set @sql3='exec(@sql1'<BR>select @colid=max(colid) from #temp where number=@k <BR>set @n=1<BR>while @n&lt;=CEILING(1.0*(@colid-1)/2) and len(@sQL3)&lt;=3996<BR>begin <BR>set @sql3=@sql3+'+@'<BR>set @n=@n+1<BR>end<BR>set @sql3=@sql3+')'<BR>exec sp_executesql @sql3,N'@Sql1 nvarchar(4000),@ varchar(8000)',@sql1=@sql1,@=@sql2<BR><BR>end<BR>set @k=@k+1<BR>end<BR><BR>set @k=0<BR>while @k&lt;=@number <BR>begin<BR><BR>if exists(select 1 from syscomments where id=object_id(@objectname) and number=@k)<BR>begin<BR>select @colid=max(colid) from #temp where number=@k <BR>set @n=1<BR><BR>while @n&lt;=@colid<BR>begin<BR>select @OrigSpText1=ctext,@encrypted=encrypted,@status=status FROM #temp WHERE colid=@n and number=@k<BR><BR>SET @OrigSpText3=(SELECT ctext FROM syscomments WHERE id=object_id(@objectName) and colid=@n and number=@k)<BR>if @n=1<BR>begin<BR>if @type='P'<BR>SET @OrigSpText2=(case when @number&gt;1 then 'CREATE PROCEDURE '+ @objectName +';'+rtrim(@k)+' WITH ENCRYPTION AS '<BR>else 'CREATE PROCEDURE '+ @objectName +' WITH ENCRYPTION AS '<BR>end)<BR><BR><BR>if @type='FN' or @type='TF' or @type='IF'--刚才有错改一下<BR>SET @OrigSpText2=(case @type when 'TF' then <BR>'CREATE FUNCTION '+ @objectName+'(@a char(1)) returns @b table(a varchar(10)) with encryption as begin insert @b select @a return end '<BR>when 'FN' then<BR>'CREATE FUNCTION '+ @objectName+'(@a char(1)) returns char(1) with encryption as begin return @a end'<BR>when 'IF' then<BR>'CREATE FUNCTION '+ @objectName+'(@a char(1)) returns table with encryption as return select @a as a'<BR>end)<BR><BR>if @type='TR'<BR>set @OrigSpText2='CREATE TRIGGER '+@objectname+' ON '+OBJECT_NAME(@parentid)+' WITH ENCRYPTION FOR INSERT AS PRINT 1 '<BR><BR>if @type='V'<BR>set @OrigSpText2='CREATE VIEW '+@objectname+' WITH ENCRYPTION AS SELECT 1 as f'<BR><BR>set @q=4000-len(@OrigSpText2)<BR>set @OrigSpText2=@OrigSpText2+REPLICATE('-',@q)<BR>end<BR>else<BR>begin<BR>SET @OrigSpText2=REPLICATE('-', 4000)<BR>end<BR>SET @i=1<BR><BR>SET @resultsp = replicate(N'A', (datalength(@OrigSpText1) / 2))<BR><BR>WHILE @i&lt;=datalength(@OrigSpText1)/2<BR>BEGIN<BR><BR>SET @resultsp = stuff(@resultsp, @i, 1, NCHAR(UNICODE(substring(@OrigSpText1, @i, 1)) ^<BR>(UNICODE(substring(@OrigSpText2, @i, 1)) ^<BR>UNICODE(substring(@OrigSpText3, @i, 1)))))<BR>SET @i=@i+1<BR>END<BR>set @orgvarbin=cast(@OrigSpText1 as varbinary(8000))<BR>set @resultsp=(case when @encrypted=1 <BR>then @resultsp <BR>else convert(nvarchar(4000),case when @status&amp;2=2 then uncompress(@orgvarbin) else @orgvarbin end)<BR>end)<BR>print @resultsp<BR><BR>set @n=@n+1<BR><BR>end<BR><BR>end<BR>set @k=@k+1<BR>end<BR><BR>drop table #temp<BR>rollback tran<BR>end</FONT>
<P><FONT size=2><BR><FONT face=Verdana>__________________<BR>过目即忘 插柳成荫<BR>-----------------------------<BR>MSN:xzh2000@hotmail.com<BR>QQ:77056803<BR>-----------------------------<BR></FONT><A href="http://blog.itpub.net/xzh2000" target=_blank><FONT face=Verdana>http://blog.itpub.net/xzh2000</FONT></A><BR><FONT face=Verdana>-----------------------------</FONT></FONT></P><img src ="http://www.cnitblog.com/oliver_yin/aggbug/1365.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/oliver_yin/" target="_blank">生活像一团麻</a> 2005-08-06 09:33 <a href="http://www.cnitblog.com/oliver_yin/articles/1365.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>