﻿<?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>BlogJava-jinfeng_wang-随笔分类-.Net</title><link>http://www.blogjava.net/jinfeng_wang/category/6905.html</link><description>G-G-S,D-D-U!</description><language>zh-cn</language><lastBuildDate>Tue, 03 Jul 2007 20:50:48 GMT</lastBuildDate><pubDate>Tue, 03 Jul 2007 20:50:48 GMT</pubDate><ttl>60</ttl><item><title>how to write __FILE__ and __LINE__ in C#</title><link>http://www.blogjava.net/jinfeng_wang/archive/2007/07/03/127829.html</link><dc:creator>jinfeng_wang</dc:creator><author>jinfeng_wang</author><pubDate>Tue, 03 Jul 2007 06:16:00 GMT</pubDate><guid>http://www.blogjava.net/jinfeng_wang/archive/2007/07/03/127829.html</guid><wfw:comment>http://www.blogjava.net/jinfeng_wang/comments/127829.html</wfw:comment><comments>http://www.blogjava.net/jinfeng_wang/archive/2007/07/03/127829.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jinfeng_wang/comments/commentRss/127829.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jinfeng_wang/services/trackbacks/127829.html</trackback:ping><description><![CDATA[class SomeClass<br>{<br>public int DoSomething()<br>{<br>ReportError("Here's an error message");<br>return 0;<br>}<br><br>private void ReportError(string Message)<br>{<br>// Get the frame one step up the call tree<br><a href="http://www.dotnet247.com/247reference/System/Diagnostics/StackFrame.aspx"><u><font color=#0000ff>StackFrame</font></u></a> CallStack = new <a href="http://www.dotnet247.com/247reference/System/Diagnostics/StackFrame.aspx"><u><font color=#0000ff>StackFrame</font></u></a>(1, true);<br><br>// These will now show the file and line number of the ReportError<br>call in the DoSomething() method<br>string SourceFile = CallStack.GetFileName(),<br>int SourceLine = CallStack.GetFileLineNumber(),<br>MyWriteToFile("Error: " + Message + " - File: " + SourceFile + "<br>Line: " + SourceLine.ToString());<br>}<br>}<br>
<img src ="http://www.blogjava.net/jinfeng_wang/aggbug/127829.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jinfeng_wang/" target="_blank">jinfeng_wang</a> 2007-07-03 14:16 <a href="http://www.blogjava.net/jinfeng_wang/archive/2007/07/03/127829.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>VSS 分支管理 （很恶心的操作顺序）</title><link>http://www.blogjava.net/jinfeng_wang/archive/2007/01/29/96523.html</link><dc:creator>jinfeng_wang</dc:creator><author>jinfeng_wang</author><pubDate>Mon, 29 Jan 2007 07:58:00 GMT</pubDate><guid>http://www.blogjava.net/jinfeng_wang/archive/2007/01/29/96523.html</guid><wfw:comment>http://www.blogjava.net/jinfeng_wang/comments/96523.html</wfw:comment><comments>http://www.blogjava.net/jinfeng_wang/archive/2007/01/29/96523.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jinfeng_wang/comments/commentRss/96523.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jinfeng_wang/services/trackbacks/96523.html</trackback:ping><description><![CDATA[Sharing/Branching in VSS（zz)<br /><a href="http://blogs.vertigosoftware.com/ericc/archive/2005/12/05/1716.aspx">http://blogs.vertigosoftware.com/ericc/archive/2005/12/05/1716.aspx</a><br /><br /><br /><p><font face="Verdana" size="2">Recently I was tasked to share/branch a complex source tree in <a href="http://msdn.microsoft.com/vstudio/previous/ssafe/">Visual SourceSafe 6.0</a> to do some changes with our project. For some reason, I found it very hard to do and I kept getting this error:</font></p><p><font face="Verdana" size="2"><img src="http://blogs.vertigosoftware.com/photos/ericc/images/1717/original.aspx" /></font></p><p><font face="Verdana" size="2">What the heck does "A project cannot be shared under a descendant" mean? After some testing, it turns out the reason for this error is because VSS attempts to add the folder you specified <strong>to itself</strong>. That is why this is a recursive error: you are adding a folder to the folder you are recursively scanning. I tried specifying a path (ie. <font face="Courier New">$/ProjectA-Copied</font>), but it refuses to recognize the full path and keeps wanting to add to itself (<font face="Courier New">$/ProjectA/ProjectA-Copied</font>). </font><font face="Verdana" size="2">For the life of me, I just couldn't get VSS to share to another location. </font></p><p><font face="Verdana" size="2">After asking around some people, <a href="http://blogs.vertigosoftware.com/paul/default.aspx">Paul </a>(thanks!) told me that the problem is when you select Share on a project (aka folder) in VSS Explorer, <strong>you are selecting the destination project, not the source</strong>. Boy... great user experience isn't it?</font></p><p><font face="Verdana" size="2">Armed with this key piece of knowledge, I created my destination project, selected Share on the destination project, in the share dialog selected the source project, and finally was able to successfully share that source project to the destination project.</font></p><p><font face="Verdana" size="2">Also if you are branching after sharing and you have a project with lots of files, I recommend you use the <strong>Branch after share</strong> check box: </font></p><p><font face="Verdana" size="2"></font><img src="http://blogs.vertigosoftware.com/photos/ericc/images/1718/original.aspx" /></p><p><font face="Verdana" size="2">If you share without branching in one step, then you will have to manually go into the destination project and <strong>branch all the files individually</strong>. Nope, you cannot branch on the project level!</font></p><p><font face="Verdana" size="2">And finally, yes we're using Visual SourceSafe, yes I know it sucks, and finally (what I'm most happy about) yes we will be moving to <a href="http://msdn.microsoft.com/vstudio/teamsystem/team/default.aspx">Team Foundation Server </a>and <a href="http://msdn.microsoft.com/vstudio/products/vsts/suite/default.aspx">Visual Studio Team Suite</a>!<br /><br /><br /></font></p><img src ="http://www.blogjava.net/jinfeng_wang/aggbug/96523.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jinfeng_wang/" target="_blank">jinfeng_wang</a> 2007-01-29 15:58 <a href="http://www.blogjava.net/jinfeng_wang/archive/2007/01/29/96523.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>ADO.NET Connection Status Map</title><link>http://www.blogjava.net/jinfeng_wang/archive/2006/06/03/50173.html</link><dc:creator>jinfeng_wang</dc:creator><author>jinfeng_wang</author><pubDate>Sat, 03 Jun 2006 13:35:00 GMT</pubDate><guid>http://www.blogjava.net/jinfeng_wang/archive/2006/06/03/50173.html</guid><wfw:comment>http://www.blogjava.net/jinfeng_wang/comments/50173.html</wfw:comment><comments>http://www.blogjava.net/jinfeng_wang/archive/2006/06/03/50173.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jinfeng_wang/comments/commentRss/50173.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jinfeng_wang/services/trackbacks/50173.html</trackback:ping><description><![CDATA[
		<img src="http://www.blogjava.net/images/blogjava_net/jinfeng_wang/6626/r_ConnectionStatus.JPG" />
<img src ="http://www.blogjava.net/jinfeng_wang/aggbug/50173.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jinfeng_wang/" target="_blank">jinfeng_wang</a> 2006-06-03 21:35 <a href="http://www.blogjava.net/jinfeng_wang/archive/2006/06/03/50173.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Checked Exception VS UnChecked Excetion (续2)</title><link>http://www.blogjava.net/jinfeng_wang/archive/2006/06/03/50170.html</link><dc:creator>jinfeng_wang</dc:creator><author>jinfeng_wang</author><pubDate>Sat, 03 Jun 2006 13:22:00 GMT</pubDate><guid>http://www.blogjava.net/jinfeng_wang/archive/2006/06/03/50170.html</guid><wfw:comment>http://www.blogjava.net/jinfeng_wang/comments/50170.html</wfw:comment><comments>http://www.blogjava.net/jinfeng_wang/archive/2006/06/03/50170.html#Feedback</comments><slash:comments>4</slash:comments><wfw:commentRss>http://www.blogjava.net/jinfeng_wang/comments/commentRss/50170.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jinfeng_wang/services/trackbacks/50170.html</trackback:ping><description><![CDATA[
		<p class="MsoNormal">
				<span style="font-family: &quot;ＭＳ 明朝&quot;;" lang="ZH-CN">　　</span>
				<span style="font-family: SimSun;" lang="ZH-CN">假设我的团队正在开发一个库程序，由于某种原因，现在希望能够得到所有最外层</span>
				<span style="" lang="EN-US">API</span>
				<span style="font-family: SimSun;" lang="ZH-CN">所抛出的所有异常的类型、各自的信息、并且能够附上各种异常所出现的原因和解决办法。如果开发过程中内部使用的是</span>
				<span style="" lang="EN-US">Unchecked
Exception</span>
				<span style="font-family: SimSun;" lang="ZH-CN">，那么对于这个任务简直就麻烦了。没办法，开发人员一个个的自我进行检查然后统计吧，但是往往这样的统计总会有漏网之鱼。现在的</span>
				<span style="" lang="EN-US">MSDN</span>
				<span style="font-family: SimSun;" lang="ZH-CN">中的异常信息糟糕的很，最底层的公有库的异常信息内容可能完整一点，但是上层的库的异常信息糟糕的不行，根本不能完整的报全所有出错的可能性。</span>
				<span style="" lang="EN-US">
						<o:p>
						</o:p>
				</span>
		</p>
		<p class="MsoNormal" style="text-indent: 21pt;">
				<span style="font-family: SimSun;" lang="ZH-CN">你可以想象的出，一个库函数的异常信息不完整，对于他的用户来说，是多么不友好的事情。假设</span>
				<span style="" lang="EN-US">MSDN</span>
				<span style="font-family: SimSun;" lang="ZH-CN">中</span>
				<span style="" lang="EN-US">File.Open()</span>
				<span style="font-family: SimSun;" lang="ZH-CN">会抛出</span>
				<span style="" lang="EN-US">IOException</span>
				<span style="font-family: SimSun;" lang="ZH-CN">，你的程序就很难想到很规矩的对</span>
				<span style="" lang="EN-US">IOException</span>
				<span style="font-family: SimSun;" lang="ZH-CN">做了</span>
				<span style="" lang="EN-US">catch</span>
				<span style="font-family: SimSun;" lang="ZH-CN">，然后提示用户检查相应位置的文件是否存在</span>
				<span style="" lang="EN-US">/</span>
				<span style="font-family: SimSun;" lang="ZH-CN">被打开等。别指望</span>
				<span style="" lang="EN-US">MSDN</span>
				<span style="font-family: SimSun;" lang="ZH-CN">的那些信息能够对终端用户有多大的帮助，太多不懂计算机的人在傻呆呆的握着鼠标了。再加上</span>
				<span style="" lang="EN-US">MicroSoft</span>
				<span style="font-family: SimSun;" lang="ZH-CN">的提示信息本身就存在着“答非所问”，“莫名其妙”的事情，用户看到这些情况就更不知道怎么解决问题了。再加上自己的应用程序中会弹出一个个“蹩脚”的运行时错误的错误框，真的不是一个很如意的创作。</span>
				<span style="" lang="EN-US">
						<o:p>
						</o:p>
				</span>
		</p>
		<p class="MsoNormal">
				<span style="" lang="EN-US">
						<span style=""> </span>
						<span style=""> </span>
						<span style=""> </span>
						<span style=""> </span>
				</span>
				<span style="font-family: SimSun;" lang="ZH-CN">但是，如果使用的是</span>
				<span style="" lang="EN-US">Checked Exception</span>
				<span style="font-family: SimSun;" lang="ZH-CN">，这时候编译器会强迫你的外部接口的异常相当的完整，最起码可以做到比</span>
				<span style="" lang="EN-US">MSDN</span>
				<span style="font-family: SimSun;" lang="ZH-CN">的异常类型齐整。</span>
				<span style="" lang="EN-US">
						<o:p>
						</o:p>
				</span>
		</p>
		<p class="MsoNormal">
				<span style="" lang="EN-US">
						<o:p> </o:p>
				</span>
		</p>
		<p class="MsoNormal">
				<span style="" lang="EN-US">
						<o:p> </o:p>
				</span>
		</p>
		<p class="MsoNormal">
				<span style="" lang="EN-US">
						<o:p> </o:p>
				</span>
		</p>
		<p class="MsoNormal">
				<span style="" lang="EN-US">
						<o:p> </o:p>
				</span>
		</p>
<img src ="http://www.blogjava.net/jinfeng_wang/aggbug/50170.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jinfeng_wang/" target="_blank">jinfeng_wang</a> 2006-06-03 21:22 <a href="http://www.blogjava.net/jinfeng_wang/archive/2006/06/03/50170.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>数据库驱动程序的测试需要注意的问题</title><link>http://www.blogjava.net/jinfeng_wang/archive/2006/05/28/48597.html</link><dc:creator>jinfeng_wang</dc:creator><author>jinfeng_wang</author><pubDate>Sun, 28 May 2006 07:52:00 GMT</pubDate><guid>http://www.blogjava.net/jinfeng_wang/archive/2006/05/28/48597.html</guid><wfw:comment>http://www.blogjava.net/jinfeng_wang/comments/48597.html</wfw:comment><comments>http://www.blogjava.net/jinfeng_wang/archive/2006/05/28/48597.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jinfeng_wang/comments/commentRss/48597.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jinfeng_wang/services/trackbacks/48597.html</trackback:ping><description><![CDATA[
		<div>
				<font face="MS UI Gothic" size="2">
						<font color="#ff0000">1. 不要用TestCase的构造函数初始化Fixture，而要用<br />setUp()和tearDown()方法。</font>
				</font>
		</div>
		<div>
				<font face="MS UI Gothic" size="2">
						<font color="#ff0000">
								<br />
						</font>2. 不要依赖或假定测试运行的顺序，因为JUnit利用<br />Vector保存测试方法。所以不同的平台会按不同的<br />顺序从Vector中取出测试方法。 <br /><br /><font color="#ff0000"></font></font>
		</div>
		<div>
				<font face="MS UI Gothic" size="2">
						<font color="#ff0000">3. 避免编写有副作用的TestCase。例如：如果随后的<br />测试依赖于某些特定的交易数据，就不要提交交易数<br />据。简单的会滚就可以了。</font>
						<br />
				</font>
		</div>
		<div>
				<font face="MS UI Gothic" size="2">
						<font color="#ff0000">
								<br />  对于我们来说，有时是必须要提交，以至于有副作用的。</font>
				</font>
		</div>
		<div>
				<font face="MS UI Gothic" color="#ff0000" size="2">  例如：在执行“插入“后，数据库显然会多出一条数<br />据来。那么必须在随后每个测试自己消除自己的副<br />作用。</font>
		</div>
		<div>
				<font face="MS UI Gothic" color="#ff0000" size="2">  在这里，就是自己“再删除刚插入的数据”。（这时候<br />需要考虑到这个善后的工作不能自己就不能有副作用，</font>
		</div>
		<div>
				<font face="MS UI Gothic" color="#ff0000" size="2">  删除多了其他的数据）。</font>
		</div>
		<div>
				<font face="MS UI Gothic" color="#ff0000" size="2">
				</font> </div>
		<div>
				<font face="MS UI Gothic" color="#ff0000" size="2">  这里的副作用还指“影响到周围环境”，因为我们现<br />在工作的人比较多，所以最好大家的测试服务器能够<br />分开来，</font>
		</div>
		<div>
				<font face="MS UI Gothic" color="#ff0000" size="2"> 例如一个人一个Database实例（可以建得稍微小一<br />点）或者一个人一个数据库，</font>
				<font face="MS UI Gothic" color="#ff0000" size="2">  注意将这些个人之间<br />有区别的内容用常量在每个人自己的所有程序中公<br />用。而不是分布在各个位置。</font>
				<font face="MS UI Gothic" color="#ff0000" size="2">  否则以后要改换测试<br />服务器，所有的程序都需要改动。</font>
		</div>
		<div>
				<font face="MS UI Gothic" color="#ff0000" size="2">
				</font> </div>
		<div>
				<font face="MS UI Gothic" color="#ff0000" size="2">  为了保证测试程序能够很容易的到处执行，请保证<br />大家的数据库服务器的测试数据全部一致。</font>
				<font face="MS UI Gothic" color="#ff0000" size="2"> 否则，<br />就不能做到很容易得拿到FJ也可以很容易的运行，<br />所以需要准备“测试数据集“。</font>
				<font face="MS UI Gothic" color="#ff0000" size="2">包括：Schema ,table ，<br />stored procedure等数据库对象的结构一致，</font>
				<font face="MS UI Gothic" color="#ff0000" size="2"> 还包<br />括数据库的数据内容保持一致。</font>
		</div>
		<font face="MS UI Gothic" size="2">
				<div>
						<br />4. 当继承一个测试类时，记得调用父类的setUp()和<br />tearDown()方法。 </div>
				<div>
						<br />5. 将测试代码和工作代码放在一起，一边同步编译<br />和更新。（使用Ant中有支持junit的task.） </div>
				<div>
						<br />6. 测试类和测试方法应该有一致的命名方案。如在<br />工作类名前加上test从而形成测试类名。 </div>
				<div>
						<font color="#ff0000">可能这里我们需要改动，将函数名和我们的测试用<br />例的编号一致起来。</font>
				</div>
				<div>
						<br />7. 确保测试与时间无关，不要依赖使用过期的数据<br />进行测试。导致在随后的维护过程中很难重现测试。 </div>
				<div>
						<br />8. 如果你编写的软件面向国际市场，编写测试时要<br />考虑国际化的因素。不要仅用母语的Locale进行测试。 </div>
				<div>
						<br />9. 尽可能地利用JUnit提供地assert/fail方法以及<br />异常处理的方法，可以使代码更为简洁。 </div>
				<div> </div>
				<div>
						<font color="#ff0000">这个内容有其关键，assert语句的好坏直接影响<br />到测试的正确性。</font>
						<font color="#ff0000">因为assert就是用于当前测试<br />项的正确性的。</font>
				</div>
				<div>
						<br />10.测试要尽可能地小，执行速度快。 <br /><br /><br />==========<br />1）将所有的数据库的测试数据用ODBC程序自动<br />生成的。 <font face="MS UI Gothic" size="2">用户可以简单的</font><font face="MS UI Gothic" size="2">修改ConnectionString，<br />然后运行程序，就可以创建生成数</font><font face="MS UI Gothic" size="2">据库/数据库<br />表/存储结构</font><font face="MS UI Gothic" size="2">，并且自动插入数据。<br /></font><div><font face="MS UI Gothic" size="2"></font> </div><div><font face="MS UI Gothic" size="2">   2）为了保证多个测试人员的不干扰，建议分别<br />各自单独使用自己的数据</font><font face="MS UI Gothic" size="2">库。否则会因为一个自<br />己的错误，影响别人的工作。</font></div><div><font face="MS UI Gothic" size="2"></font> </div><div><font face="MS UI Gothic" size="2">   3）在自己的程序中，所有涉及环境的内容都用<br />单独放到一个类中，用static</font><font face="MS UI Gothic" size="2">常量共享使用（这样<br />就便于很容易的更换环境再进行测试，做到很容<br />易的移植</font><font face="MS UI Gothic" size="2">测试环境）。</font></div><div><font face="MS UI Gothic" size="2"></font> </div><div><font face="MS UI Gothic" size="2">   4）关于数据库表结构，我建议测试表中含有一<br />个主键，我们在插入数据的时</font><font face="MS UI Gothic" size="2">候，保证测试用例，<br />测试用例程序，测试用例程序中的数据，这三者<br />的编号一</font><font face="MS UI Gothic" size="2">致起来。便于出现问题时，可以排除数据。</font></div></div>
		</font>
<img src ="http://www.blogjava.net/jinfeng_wang/aggbug/48597.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jinfeng_wang/" target="_blank">jinfeng_wang</a> 2006-05-28 15:52 <a href="http://www.blogjava.net/jinfeng_wang/archive/2006/05/28/48597.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>数据库驱动程序测试的建议</title><link>http://www.blogjava.net/jinfeng_wang/archive/2006/05/28/48596.html</link><dc:creator>jinfeng_wang</dc:creator><author>jinfeng_wang</author><pubDate>Sun, 28 May 2006 07:32:00 GMT</pubDate><guid>http://www.blogjava.net/jinfeng_wang/archive/2006/05/28/48596.html</guid><wfw:comment>http://www.blogjava.net/jinfeng_wang/comments/48596.html</wfw:comment><comments>http://www.blogjava.net/jinfeng_wang/archive/2006/05/28/48596.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jinfeng_wang/comments/commentRss/48596.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jinfeng_wang/services/trackbacks/48596.html</trackback:ping><description><![CDATA[
		<div>
				<font face="MS UI Gothic" size="2">
						<font color="#ff0000">1. 不要用TestCase的构造函数初始化Fixture，而要用<br />setUp()和tearDown()方法。</font>
				</font>
		</div>
		<div>
				<font face="MS UI Gothic" size="2">
						<font color="#ff0000">
								<br />
						</font>2. 不要依赖或假定测试运行的顺序，因为JUnit利用<br />Vector保存测试方法。所以不同的平台会按不同的<br />顺序从Vector中取出测试方法。 <br /><br /><font color="#ff0000"></font></font>
		</div>
		<div>
				<font face="MS UI Gothic" size="2">
						<font color="#ff0000">3. 避免编写有副作用的TestCase。例如：如果随后的<br />测试依赖于某些特定的交易数据，就不要提交交易<br />数据。简单的会滚就可以了。<br /></font>
						<br />
				</font>
		</div>
		<div>
				<font face="MS UI Gothic" size="2">
						<font color="#ff0000">  对于我们来说，有时是必须要提交，以至于有副作用的。</font>
				</font>
		</div>
		<div>
				<font face="MS UI Gothic" color="#ff0000" size="2">  例如：在执行“插入“后，数据库显然会多出一条数据来。<br />那么必须在随后每个测试自己消除自己的副作用。</font>
		</div>
		<div>
				<font face="MS UI Gothic" color="#ff0000" size="2">  在这里，就是自己“再删除刚插入的数据”。（这时候需要<br />考虑到这个善后的工作不能自己就不能有副作用，</font>
				<font face="MS UI Gothic" color="#ff0000" size="2">  删除<br />多了其他的数据）。</font>
		</div>
		<div>
				<font face="MS UI Gothic" color="#ff0000" size="2">
				</font> </div>
		<div>
				<font face="MS UI Gothic" color="#ff0000" size="2">  这里的副作用还指“影响到周围环境”，因为我们现在工<br />作的人比较多，所以最好大家的测试服务器能够分开来，</font>
		</div>
		<div>
				<font face="MS UI Gothic" color="#ff0000" size="2"> 例如一个人一个Database实例（可以建得稍微小一点）或<br />者一个人一个数据库，</font>
				<font face="MS UI Gothic" color="#ff0000" size="2">  注意将这些个人之间有区别的内<br />容用常量在每个人自己的所有程序中公用。而不是分布在<br />各个位置。</font>
				<font face="MS UI Gothic" color="#ff0000" size="2">  否则以后要改换测试服务器，所有的程序都需<br />要改动。</font>
		</div>
		<div>
				<font face="MS UI Gothic" color="#ff0000" size="2">
				</font> </div>
		<div>
				<font face="MS UI Gothic" color="#ff0000" size="2">  为了保证测试程序能够很容易的到处执行，请保证大家<br />的数据库服务器的测试数据全部一致。</font>
				<font face="MS UI Gothic" color="#ff0000" size="2"> 否则，就不能做到<br />很容易得拿到FJ也可以很容易的运行，所以需要准备“测<br />试数据集“。</font>
				<font face="MS UI Gothic" color="#ff0000" size="2">包括：Schema ,table ，stored procedure等数据<br />库对象的结构一致，</font>
				<font face="MS UI Gothic" color="#ff0000" size="2"> 还包括数据库的数据内容保持一致。</font>
		</div>
		<font face="MS UI Gothic" size="2">
				<div>
						<br />4. 当继承一个测试类时，记得调用父类的setUp()和tearDown()方法。 </div>
				<div>
						<br />5. 将测试代码和工作代码放在一起，一边同步编译和更新。<br />（使用Ant中有支持junit的task.） </div>
				<div>
						<br />6. 测试类和测试方法应该有一致的命名方案。如在工作类<br />名前加上test从而形成测试类名。 </div>
				<div>
						<font color="#ff0000">可能这里我们需要改动，将函数名和我们的测试用例的编号一致起来。</font>
				</div>
				<div>
						<br />7. 确保测试与时间无关，不要依赖使用过期的数据进行测试。<br />导致在随后的维护过程中很难重现测试。 </div>
				<div>
						<br />8. 如果你编写的软件面向国际市场，编写测试时要考虑国际<br />化的因素。不要仅用母语的Locale进行测试。 </div>
				<div>
						<br />9. 尽可能地利用JUnit提供地assert/fail方法以及异常处理的<br />方法，可以使代码更为简洁。 </div>
				<div> </div>
				<div>
						<font color="#ff0000">这个内容有其关键，assert语句的好坏直接影响到测试的正确性。</font>
				</div>
				<div>
						<font color="#ff0000">因为assert就是用于当前测试项的正确性的。</font>
				</div>
				<div>
						<br />10.测试要尽可能地小，执行速度快。 <br /><br />=============<br /> 1）将所有的数据库的测试数据程序自动生成的。 
<div><font face="MS UI Gothic" size="2">用户可以简单的</font><font face="MS UI Gothic" size="2">修改ConnectionString，然后运行程序，<br />就可以创建生成数</font><font face="MS UI Gothic" size="2">据库/数据库表/存储结构</font><font face="MS UI Gothic" size="2">，并且自动<br />插入数据。 <br /></font><font face="MS UI Gothic" size="2"></font></div><div><font face="MS UI Gothic" size="2"><br />   2）为了保证多个测试人员的不干扰，建议分别各自<br />单独使用自己的数据</font><font face="MS UI Gothic" size="2">库。否则会因为一个自己的错误，<br />影响别人的工作。</font></div><div><font face="MS UI Gothic" size="2"></font> </div><div><font face="MS UI Gothic" size="2">   3）在自己的程序中，所有涉及环境的内容都用单独<br />放到一个类中，用static</font><font face="MS UI Gothic" size="2">常量共享使用（这样就便于很<br />容易的更换环境再进行测试，做到很容易的移植</font><font face="MS UI Gothic" size="2">测试<br />环境）。</font></div><div><font face="MS UI Gothic" size="2"></font> </div><div><font face="MS UI Gothic" size="2">   4）关于数据库表结构，我建议测试表中含有一个主键，<br />我们在插入数据的时</font><font face="MS UI Gothic" size="2">候，保证测试用例，测试用例程序，<br />测试用例程序中的数据，这三者的编号一</font><font face="MS UI Gothic" size="2">致起来。便于<br />出现问题时，可以排除数据。</font></div></div>
		</font>
<img src ="http://www.blogjava.net/jinfeng_wang/aggbug/48596.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jinfeng_wang/" target="_blank">jinfeng_wang</a> 2006-05-28 15:32 <a href="http://www.blogjava.net/jinfeng_wang/archive/2006/05/28/48596.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Checked Exception VS UnChecked Excetion (续)</title><link>http://www.blogjava.net/jinfeng_wang/archive/2006/05/12/45911.html</link><dc:creator>jinfeng_wang</dc:creator><author>jinfeng_wang</author><pubDate>Fri, 12 May 2006 12:21:00 GMT</pubDate><guid>http://www.blogjava.net/jinfeng_wang/archive/2006/05/12/45911.html</guid><wfw:comment>http://www.blogjava.net/jinfeng_wang/comments/45911.html</wfw:comment><comments>http://www.blogjava.net/jinfeng_wang/archive/2006/05/12/45911.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.blogjava.net/jinfeng_wang/comments/commentRss/45911.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jinfeng_wang/services/trackbacks/45911.html</trackback:ping><description><![CDATA[
		<p>上篇：<a href="/jinfeng_wang/archive/2006/05/11/45726.html">http://www.blogjava.net/jinfeng_wang/archive/2006/05/11/45726.html</a><br /><br />先来回复两个回帖：<br />"你可以把IO等异常转化成你类库中中定义的异常。当然你定义的异常是Checked Exception 还是 UnChecked Excetion 那你自己定了。 "<br /><br />假设我按照这条建议去做了，那么我可以抛出XXXLibException。假设我的异常采用的是CheckedException，那么客户程序员可以捕捉它。但是对于不同的出错原因（例如不同的原因，包括无权限、硬盘满、U盘被拔了等等，导致无法正常写日志文件），程序都是捕捉XXXLibException，那么客户程序员就无法根据不同的异常去做相应的处理（例如提示用户修改权限、直接启用备用目录，直接退出程序）。总之，如果异常类型不同，客户程序可以根据“异常类型”做不同的工作（编译器＋工具可以实现自动代码生成）；但是如果全部合并为一个异常，那么客户程序只能根据“ExceptionMessage”尝试着做不同的工作（恰恰目前没有办法完成自动编码）。<br /><br /><br /><br />"要知道 JDK 的 FileWriter 是可以抛出 IOException 的子类的，只要文档足够详细，Java代码中也可以只去捕捉更明细的异常， 也可以就捕捉并重新抛出包装过的异常（甚至是RuntimException），除非呆板地去捕获每个异常，不然代码怎么会丑陋呢？"<br /><br />RuntiomeException，也就是UncheckedException（类于.NET的Exception），它不会要求客户代码强行捕捉异常，据此我写了下面的一个程序：<br />private void button1_Click(object sender, System.EventArgs e)<br />  {<br />   throw new Exception("s");<br />  }<br />     我只试了.NET的程序，出现的界面大家自己试 。因为UncheckedException没有要求客户程序去捕捉（客户程序员不能自觉的发现库中抛出的各种异常），让最终用户直接面对这样的异常，是一个道德的事情么？<br /></p>
<img src ="http://www.blogjava.net/jinfeng_wang/aggbug/45911.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jinfeng_wang/" target="_blank">jinfeng_wang</a> 2006-05-12 20:21 <a href="http://www.blogjava.net/jinfeng_wang/archive/2006/05/12/45911.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Checked Exception  VS UnChecked  Excetion</title><link>http://www.blogjava.net/jinfeng_wang/archive/2006/05/11/45726.html</link><dc:creator>jinfeng_wang</dc:creator><author>jinfeng_wang</author><pubDate>Thu, 11 May 2006 13:18:00 GMT</pubDate><guid>http://www.blogjava.net/jinfeng_wang/archive/2006/05/11/45726.html</guid><wfw:comment>http://www.blogjava.net/jinfeng_wang/comments/45726.html</wfw:comment><comments>http://www.blogjava.net/jinfeng_wang/archive/2006/05/11/45726.html#Feedback</comments><slash:comments>4</slash:comments><wfw:commentRss>http://www.blogjava.net/jinfeng_wang/comments/commentRss/45726.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jinfeng_wang/services/trackbacks/45726.html</trackback:ping><description><![CDATA[
		<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
				<b>
						<span lang="EN-US">1</span>
				</b>
				<b>
						<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">、</span>
						<span lang="EN-US">.NET Framework IO Interface<span style="mso-spacerun: yes">  </span>and<span style="mso-spacerun: yes">  </span>JDK IO Interface<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" /?><o:p></o:p></span>
				</b>
		</p>
		<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
				<b>
						<span lang="EN-US">1.1</span>
				</b>
				<b>
						<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">．</span>
						<span lang="EN-US">.NET Framework 1.1<o:p></o:p></span>
				</b>
		</p>
		<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
				<span lang="EN-US">
						<span style="mso-spacerun: yes">   </span>public StreamWriter(<span style="mso-spacerun: yes">    </span>string path<span style="mso-spacerun: yes">  </span>);</span>
		</p>
		<h4 style="MARGIN: 12pt 0cm 4.8pt -13.5pt">
				<span style="FONT-SIZE: 8pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">异常</span>
				<span lang="EN-US" style="FONT-SIZE: 8pt">
						<o:p>
						</o:p>
				</span>
		</h4>
		<p>
		</p>
		<table style="BORDER-RIGHT: #999999 0.75pt solid; BORDER-TOP: #999999 0.75pt solid; BACKGROUND: #999999; BORDER-LEFT: #999999 0.25pt solid; WIDTH: 105.8%; BORDER-BOTTOM: #999999 0.25pt solid; mso-cellspacing: 0cm" cellspacing="0" cellpadding="0" width="105%" bgcolor="#999999" border="1">
				<tbody>
						<tr style="HEIGHT: 19.3pt">
								<td style="BORDER-RIGHT: #999999; PADDING-RIGHT: 0.75pt; BORDER-TOP: #999999; PADDING-LEFT: 0.75pt; PADDING-BOTTOM: 0.75pt; BORDER-LEFT: #999999; WIDTH: 38.9%; PADDING-TOP: 0.75pt; BORDER-BOTTOM: #999999; HEIGHT: 19.3pt; BACKGROUND-COLOR: transparent" valign="top" width="38%">
										<p class="MsoNormal" style="MARGIN: 7.2pt 0cm 3.6pt; TEXT-ALIGN: center" align="center">
												<b>
														<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">异常类型</span>
												</b>
												<b>
														<span lang="EN-US" style="FONT-SIZE: 12pt; COLOR: black; FONT-FAMILY: Verdana">
																<o:p>
																</o:p>
														</span>
												</b>
										</p>
								</td>
								<td style="BORDER-RIGHT: #999999; PADDING-RIGHT: 0.75pt; BORDER-TOP: #999999; PADDING-LEFT: 0.75pt; PADDING-BOTTOM: 0.75pt; BORDER-LEFT: #999999; WIDTH: 61.1%; PADDING-TOP: 0.75pt; BORDER-BOTTOM: #999999; HEIGHT: 19.3pt; BACKGROUND-COLOR: transparent" valign="top" width="61%">
										<p class="MsoNormal" style="MARGIN: 7.2pt 0cm 3.6pt; TEXT-ALIGN: center" align="center">
												<b>
														<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">条件</span>
												</b>
												<b>
														<span lang="EN-US" style="FONT-SIZE: 12pt; COLOR: black; FONT-FAMILY: Verdana">
																<o:p>
																</o:p>
														</span>
												</b>
										</p>
								</td>
						</tr>
						<tr style="HEIGHT: 13.35pt">
								<td style="BORDER-RIGHT: #999999; PADDING-RIGHT: 0.75pt; BORDER-TOP: #999999; PADDING-LEFT: 0.75pt; PADDING-BOTTOM: 0.75pt; BORDER-LEFT: #999999; WIDTH: 38.9%; PADDING-TOP: 0.75pt; BORDER-BOTTOM: #999999; HEIGHT: 13.35pt; BACKGROUND-COLOR: transparent" valign="top" width="38%">
										<p class="MsoNormal" style="MARGIN: 7.2pt 0cm 3.6pt">
												<span lang="EN-US" style="FONT-FAMILY: Verdana">
														<a href="http://msdn.microsoft.com/library/CHS/cpref/html/frlrfsystemunauthorizedaccessexceptionclasstopic.asp">UnauthorizedAccessException</a>
												</span>
												<span lang="EN-US" style="FONT-SIZE: 12pt; COLOR: black; FONT-FAMILY: Verdana">
														<o:p>
														</o:p>
												</span>
										</p>
								</td>
								<td style="BORDER-RIGHT: #999999; PADDING-RIGHT: 0.75pt; BORDER-TOP: #999999; PADDING-LEFT: 0.75pt; PADDING-BOTTOM: 0.75pt; BORDER-LEFT: #999999; WIDTH: 61.1%; PADDING-TOP: 0.75pt; BORDER-BOTTOM: #999999; HEIGHT: 13.35pt; BACKGROUND-COLOR: transparent" valign="top" width="61%">
										<p class="MsoNormal" style="MARGIN: 7.2pt 0cm 3.6pt">
												<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">访问被拒绝。</span>
												<span lang="EN-US" style="FONT-SIZE: 12pt; COLOR: black; FONT-FAMILY: Verdana">
														<o:p>
														</o:p>
												</span>
										</p>
								</td>
						</tr>
						<tr style="HEIGHT: 15.15pt">
								<td style="BORDER-RIGHT: #999999; PADDING-RIGHT: 0.75pt; BORDER-TOP: #999999; PADDING-LEFT: 0.75pt; PADDING-BOTTOM: 0.75pt; BORDER-LEFT: #999999; WIDTH: 38.9%; PADDING-TOP: 0.75pt; BORDER-BOTTOM: #999999; HEIGHT: 15.15pt; BACKGROUND-COLOR: transparent" valign="top" width="38%">
										<p class="MsoNormal" style="MARGIN: 7.2pt 0cm 3.6pt">
												<span lang="EN-US" style="FONT-FAMILY: Verdana">
														<a href="http://msdn.microsoft.com/library/CHS/cpref/html/frlrfsystemargumentexceptionclasstopic.asp">ArgumentException</a>
												</span>
												<span lang="EN-US" style="FONT-SIZE: 12pt; COLOR: black; FONT-FAMILY: Verdana">
														<o:p>
														</o:p>
												</span>
										</p>
								</td>
								<td style="BORDER-RIGHT: #999999; PADDING-RIGHT: 0.75pt; BORDER-TOP: #999999; PADDING-LEFT: 0.75pt; PADDING-BOTTOM: 0.75pt; BORDER-LEFT: #999999; WIDTH: 61.1%; PADDING-TOP: 0.75pt; BORDER-BOTTOM: #999999; HEIGHT: 15.15pt; BACKGROUND-COLOR: transparent" valign="top" width="61%">
										<p class="MsoNormal" style="MARGIN: 7.2pt 0cm 3.6pt">
												<i>
														<span lang="EN-US" style="FONT-FAMILY: Verdana">path</span>
												</i>
												<span lang="EN-US" style="FONT-FAMILY: Verdana">
												</span>
												<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">为空字符串</span>
												<span lang="EN-US" style="FONT-FAMILY: Verdana">("")</span>
												<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">。</span>
												<span lang="EN-US" style="FONT-SIZE: 12pt; COLOR: black; FONT-FAMILY: Verdana">
														<o:p>
														</o:p>
												</span>
										</p>
								</td>
						</tr>
						<tr>
								<td style="BORDER-RIGHT: #999999; PADDING-RIGHT: 0.75pt; BORDER-TOP: #999999; PADDING-LEFT: 0.75pt; PADDING-BOTTOM: 0.75pt; BORDER-LEFT: #999999; WIDTH: 38.9%; PADDING-TOP: 0.75pt; BORDER-BOTTOM: #999999; BACKGROUND-COLOR: transparent" valign="top" width="38%">
										<p class="MsoNormal" style="MARGIN: 7.2pt 0cm 3.6pt">
												<span lang="EN-US" style="FONT-FAMILY: Verdana">
														<a href="http://msdn.microsoft.com/library/CHS/cpref/html/frlrfsystemargumentnullexceptionclasstopic.asp">ArgumentNullException</a>
												</span>
												<span lang="EN-US" style="FONT-SIZE: 12pt; COLOR: black; FONT-FAMILY: Verdana">
														<o:p>
														</o:p>
												</span>
										</p>
								</td>
								<td style="BORDER-RIGHT: #999999; PADDING-RIGHT: 0.75pt; BORDER-TOP: #999999; PADDING-LEFT: 0.75pt; PADDING-BOTTOM: 0.75pt; BORDER-LEFT: #999999; WIDTH: 61.1%; PADDING-TOP: 0.75pt; BORDER-BOTTOM: #999999; BACKGROUND-COLOR: transparent" valign="top" width="61%">
										<p class="MsoNormal" style="MARGIN: 7.2pt 0cm 3.6pt">
												<i>
														<span lang="EN-US" style="FONT-FAMILY: Verdana">path</span>
												</i>
												<span lang="EN-US" style="FONT-FAMILY: Verdana">
												</span>
												<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">为空引用（</span>
												<span lang="EN-US" style="FONT-FAMILY: Verdana">Visual Basic </span>
												<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">中为</span>
												<span style="FONT-FAMILY: Verdana">
														<b>
																<span lang="EN-US">Nothing</span>
														</b>
												</span>
												<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">）。</span>
												<span lang="EN-US" style="FONT-SIZE: 12pt; COLOR: black; FONT-FAMILY: Verdana">
														<o:p>
														</o:p>
												</span>
										</p>
								</td>
						</tr>
						<tr>
								<td style="BORDER-RIGHT: #999999; PADDING-RIGHT: 0.75pt; BORDER-TOP: #999999; PADDING-LEFT: 0.75pt; PADDING-BOTTOM: 0.75pt; BORDER-LEFT: #999999; WIDTH: 38.9%; PADDING-TOP: 0.75pt; BORDER-BOTTOM: #999999; BACKGROUND-COLOR: transparent" valign="top" width="38%">
										<p class="MsoNormal" style="MARGIN: 7.2pt 0cm 3.6pt">
												<span lang="EN-US" style="FONT-FAMILY: Verdana">
														<a href="http://msdn.microsoft.com/library/CHS/cpref/html/frlrfsystemiodirectorynotfoundexceptionclasstopic.asp">DirectoryNotFoundException</a>
												</span>
												<span lang="EN-US" style="FONT-SIZE: 12pt; COLOR: black; FONT-FAMILY: Verdana">
														<o:p>
														</o:p>
												</span>
										</p>
								</td>
								<td style="BORDER-RIGHT: #999999; PADDING-RIGHT: 0.75pt; BORDER-TOP: #999999; PADDING-LEFT: 0.75pt; PADDING-BOTTOM: 0.75pt; BORDER-LEFT: #999999; WIDTH: 61.1%; PADDING-TOP: 0.75pt; BORDER-BOTTOM: #999999; BACKGROUND-COLOR: transparent" valign="top" width="61%">
										<p class="MsoNormal" style="MARGIN: 7.2pt 0cm 3.6pt">
												<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">指定的路径无效，比如在未映射的驱动器上。</span>
												<span lang="EN-US" style="FONT-SIZE: 12pt; COLOR: black; FONT-FAMILY: Verdana">
														<o:p>
														</o:p>
												</span>
										</p>
								</td>
						</tr>
						<tr>
								<td style="BORDER-RIGHT: #999999; PADDING-RIGHT: 0.75pt; BORDER-TOP: #999999; PADDING-LEFT: 0.75pt; PADDING-BOTTOM: 0.75pt; BORDER-LEFT: #999999; WIDTH: 38.9%; PADDING-TOP: 0.75pt; BORDER-BOTTOM: #999999; BACKGROUND-COLOR: transparent" valign="top" width="38%">
										<p class="MsoNormal" style="MARGIN: 7.2pt 0cm 3.6pt">
												<span lang="EN-US" style="FONT-FAMILY: Verdana">
														<a href="http://msdn.microsoft.com/library/CHS/cpref/html/frlrfsystemiopathtoolongexceptionclasstopic.asp">PathTooLongException</a>
												</span>
												<span lang="EN-US" style="FONT-SIZE: 12pt; COLOR: black; FONT-FAMILY: Verdana">
														<o:p>
														</o:p>
												</span>
										</p>
								</td>
								<td style="BORDER-RIGHT: #999999; PADDING-RIGHT: 0.75pt; BORDER-TOP: #999999; PADDING-LEFT: 0.75pt; PADDING-BOTTOM: 0.75pt; BORDER-LEFT: #999999; WIDTH: 61.1%; PADDING-TOP: 0.75pt; BORDER-BOTTOM: #999999; BACKGROUND-COLOR: transparent" valign="top" width="61%">
										<p class="MsoNormal" style="MARGIN: 7.2pt 0cm 3.6pt">
												<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">指定的路径、文件名或者两者都超出了系统定义的最大长度。例如，在基于</span>
												<span lang="EN-US" style="FONT-FAMILY: Verdana">Windows </span>
												<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">的平台上，路径必须小于</span>
												<span lang="EN-US" style="FONT-FAMILY: Verdana">248 </span>
												<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">个字符，文件名必须小于</span>
												<span lang="EN-US" style="FONT-FAMILY: Verdana">260 </span>
												<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">个字符。</span>
												<span lang="EN-US" style="FONT-SIZE: 12pt; COLOR: black; FONT-FAMILY: Verdana">
														<o:p>
														</o:p>
												</span>
										</p>
								</td>
						</tr>
						<tr>
								<td style="BORDER-RIGHT: #999999; PADDING-RIGHT: 0.75pt; BORDER-TOP: #999999; PADDING-LEFT: 0.75pt; PADDING-BOTTOM: 0.75pt; BORDER-LEFT: #999999; WIDTH: 38.9%; PADDING-TOP: 0.75pt; BORDER-BOTTOM: #999999; BACKGROUND-COLOR: transparent" valign="top" width="38%">
										<p class="MsoNormal" style="MARGIN: 7.2pt 0cm 3.6pt">
												<span lang="EN-US" style="FONT-FAMILY: Verdana">
														<a href="http://msdn.microsoft.com/library/CHS/cpref/html/frlrfsystemioioexceptionclasstopic.asp">IOException</a>
												</span>
												<span lang="EN-US" style="FONT-SIZE: 12pt; COLOR: black; FONT-FAMILY: Verdana">
														<o:p>
														</o:p>
												</span>
										</p>
								</td>
								<td style="BORDER-RIGHT: #999999; PADDING-RIGHT: 0.75pt; BORDER-TOP: #999999; PADDING-LEFT: 0.75pt; PADDING-BOTTOM: 0.75pt; BORDER-LEFT: #999999; WIDTH: 61.1%; PADDING-TOP: 0.75pt; BORDER-BOTTOM: #999999; BACKGROUND-COLOR: transparent" valign="top" width="61%">
										<p class="MsoNormal" style="MARGIN: 7.2pt 0cm 3.6pt">
												<i>
														<span lang="EN-US" style="FONT-FAMILY: Verdana">path</span>
												</i>
												<span lang="EN-US" style="FONT-FAMILY: Verdana">
												</span>
												<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">包含不正确或无效的文件名、目录名或卷标的语法。</span>
												<span lang="EN-US" style="FONT-SIZE: 12pt; COLOR: black; FONT-FAMILY: Verdana">
														<o:p>
														</o:p>
												</span>
										</p>
								</td>
						</tr>
						<tr>
								<td style="BORDER-RIGHT: #999999; PADDING-RIGHT: 0.75pt; BORDER-TOP: #999999; PADDING-LEFT: 0.75pt; PADDING-BOTTOM: 0.75pt; BORDER-LEFT: #999999; WIDTH: 38.9%; PADDING-TOP: 0.75pt; BORDER-BOTTOM: #999999; BACKGROUND-COLOR: transparent" valign="top" width="38%">
										<p class="MsoNormal" style="MARGIN: 7.2pt 0cm 3.6pt">
												<span lang="EN-US" style="FONT-FAMILY: Verdana">
														<a href="http://msdn.microsoft.com/library/CHS/cpref/html/frlrfsystemsecuritysecurityexceptionclasstopic.asp">SecurityException</a>
												</span>
												<span lang="EN-US" style="FONT-SIZE: 12pt; COLOR: black; FONT-FAMILY: Verdana">
														<o:p>
														</o:p>
												</span>
										</p>
								</td>
								<td style="BORDER-RIGHT: #999999; PADDING-RIGHT: 0.75pt; BORDER-TOP: #999999; PADDING-LEFT: 0.75pt; PADDING-BOTTOM: 0.75pt; BORDER-LEFT: #999999; WIDTH: 61.1%; PADDING-TOP: 0.75pt; BORDER-BOTTOM: #999999; BACKGROUND-COLOR: transparent" valign="top" width="61%">
										<p class="MsoNormal" style="MARGIN: 7.2pt 0cm 3.6pt">
												<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">调用方没有所要求的权限。</span>
												<span lang="EN-US" style="FONT-SIZE: 12pt; COLOR: black; FONT-FAMILY: Verdana">
														<o:p>
														</o:p>
												</span>
										</p>
								</td>
						</tr>
				</tbody>
		</table>
		<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
				<span lang="EN-US"> <o:p></o:p></span>
		</p>
		<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
				<b>
						<span lang="EN-US">1.2 JDK 1.4.2 :<o:p></o:p></span>
				</b>
		</p>
		<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
				<span lang="EN-US">public FileWriter(String fileName)<span style="mso-spacerun: yes">  </span>throws IOException</span>
		</p>
		<pre>
				<span lang="EN-US">Constructs a FileWriter object given a file name. </span>
		</pre>
		<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
				<b>
						<span lang="EN-US">Parameters:</span>
				</b>
				<span lang="EN-US">
				</span>
		</p>
		<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt 36pt">
				<code>
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 黑体">fileName</span>
				</code>
				<span lang="EN-US">- String The system-dependent filename. </span>
		</p>
		<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
				<b>
						<span lang="EN-US">Throws:</span>
				</b>
				<span lang="EN-US">
				</span>
		</p>
		<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt 36pt">
				<code>
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 黑体">
								<a title="class in java.io" href="file:///D:JDK_DOC%25201.4docsapijavaioIOException.html">IOException</a>
						</span>
				</code>
				<span lang="EN-US">- if the named file exists but is a directory rather than a regular file, does not exist but cannot be created, or cannot be opened for any other reason</span>
		</p>
		<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
				<span lang="EN-US"> <o:p></o:p></span>
		</p>
		<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
				<b>
						<span lang="EN-US">2</span>
				</b>
				<b>
						<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">、解析</span>
						<span lang="EN-US">
								<o:p>
								</o:p>
						</span>
				</b>
		</p>
		<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 21.75pt">
				<span lang="EN-US">.NET</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">的</span>
				<span lang="EN-US">Excetpion</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">是</span>
				<span lang="EN-US">Unchecked</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">异常，客户端不要求去</span>
				<span lang="EN-US">Check</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">代码，但是</span>
				<span lang="EN-US">JAVA</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">的绝大部分</span>
				<span lang="EN-US">Checked</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">异常，它要求客户端的代码检测异常。</span>
		</p>
		<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 21.75pt; mso-char-indent-count: 2.07; mso-char-indent-size: 10.5pt">
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">假设一个这样的场景，方法</span>
				<span lang="EN-US">OutMethod</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">调用</span>
				<span lang="EN-US">InnerMethod</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">，而内部方法</span>
				<span lang="EN-US">InnerMethod</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">抛出的异常</span>
				<span lang="EN-US">InnerException</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">。</span>
		</p>
		<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 21.75pt; mso-char-indent-count: 2.07; mso-char-indent-size: 10.5pt">
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">对于</span>
				<span lang="EN-US">Java</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">的</span>
				<span lang="EN-US">CheckedException</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">，或者</span>
				<span lang="EN-US">OutMethod</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">去抛出</span>
				<span lang="EN-US">InnerException</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">，或者</span>
				<span lang="EN-US">OutMethod</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">捕捉</span>
				<span lang="EN-US">InnerException</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">（然后做处理）。</span>
		</p>
		<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
				<span lang="EN-US"> <o:p></o:p></span>
		</p>
		<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 21.75pt">
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">再来观察一下</span>
				<span lang="EN-US">JDK</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">的</span>
				<span lang="EN-US">FileWriter</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">的异常声明，我没有详细测试其在各种可能出错情况下抛出的</span>
				<span lang="EN-US">IOException</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">的消息，但是其分类远远不如</span>
				<span lang="EN-US">.NET</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">的</span>
				<span lang="EN-US">StreamWriter</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">。假设</span>
				<span lang="EN-US">Java</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">想照抄</span>
				<span lang="EN-US">.NET</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">的</span>
				<span lang="EN-US">StreamWriter</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">，对于</span>
				<span lang="EN-US">Java</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">的使用者来说，无异于恶梦。外部的代码需要捕获如此多的异常消息（不捕捉就会在</span>
				<span lang="EN-US">OutMethod</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">抛出一大堆的异常，问题继续传播下去，这是</span>
				<span lang="EN-US">CheckException</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">的一个弱点）。也许正是出于这样的问题，所以此处</span>
				<span lang="EN-US">Java</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">接口的异常声明比较简单。</span>
		</p>
		<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 21.75pt">
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">那么假设我是一个库设计者，正在用到了</span>
				<span lang="EN-US">IO</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">。如果我使用</span>
				<span lang="EN-US">.NET</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">进行开发，对于</span>
				<span lang="EN-US">IOException</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">来说，我是否有必要捕捉呢？捕捉的目的是为了“处理”，那么对于库设计者，显然这时候需要通知其“客户程序员”出错的原因，所以这里的库设计者的行为最好就是“不处理”。如果处理，那只能是“</span>
				<span lang="EN-US">catch</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">、再</span>
				<span lang="EN-US">throw</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">”。那么这样的处理显然是无意义的，因为原始异常已经足以提醒客户程序员出错的原因了。如果捕捉，那代码会特别的丑陋（直接</span>
				<span lang="EN-US">catch Exception</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">的行为是不可取的）。</span>
		</p>
		<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 21.75pt">
				<span lang="EN-US"> <o:p></o:p></span>
		</p>
		<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 21.75pt">
				<span lang="EN-US">CheckedException</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">的另外一个缺点就是“将</span>
				<span lang="EN-US">Exceotion</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">加入了</span>
				<span lang="EN-US">Interface</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">的规格声明“。假设</span>
				<span lang="EN-US">OutMethod</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">调用了</span>
				<span lang="EN-US">InnerMethod</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">，此时</span>
				<span lang="EN-US">InnerMethod</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">的设计者需要增加一个异常，那么会直接影响到</span>
				<span lang="EN-US">OutMethod</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">。当然这里的</span>
				<span lang="EN-US">InnerMethod</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">的设计者此时已经做了“修改接口声明“的行为。</span>
		</p>
		<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 21.75pt">
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'"> </span>
		</p>
		<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 21.75pt"> </p>
		<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 21.75pt">
				<span lang="EN-US">
						<br />   随后待续...... <o:p></o:p></span>
		</p>
		<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 21.75pt">
				<span lang="EN-US"> <o:p></o:p></span>
		</p>
		<p> </p>
<img src ="http://www.blogjava.net/jinfeng_wang/aggbug/45726.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jinfeng_wang/" target="_blank">jinfeng_wang</a> 2006-05-11 21:18 <a href="http://www.blogjava.net/jinfeng_wang/archive/2006/05/11/45726.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>测试、构建和重构(zz)</title><link>http://www.blogjava.net/jinfeng_wang/archive/2006/04/26/43358.html</link><dc:creator>jinfeng_wang</dc:creator><author>jinfeng_wang</author><pubDate>Wed, 26 Apr 2006 08:52:00 GMT</pubDate><guid>http://www.blogjava.net/jinfeng_wang/archive/2006/04/26/43358.html</guid><wfw:comment>http://www.blogjava.net/jinfeng_wang/comments/43358.html</wfw:comment><comments>http://www.blogjava.net/jinfeng_wang/archive/2006/04/26/43358.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jinfeng_wang/comments/commentRss/43358.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jinfeng_wang/services/trackbacks/43358.html</trackback:ping><description><![CDATA[
		<div class="entrylistTitle">测试、构建和重构</div>
		<div class="entrylistDescription">
		</div>
		<div class="post">
				<h5>
						<a id="FavoriteList1_Favorites_ctl00_TitleUrl" href="http://www.cnblogs.com/confach/archive/2005/06/20/177817.html" target="_blank">NUnit2.0详细使用方法</a>
				</h5>
		</div>
		<div class="post">
				<h5>
						<a id="FavoriteList1_Favorites_ctl01_TitleUrl" href="http://www.cnblogs.com/excel/archive/2005/06/17/176100.html" target="_blank">重构啊重构</a>
				</h5>
		</div>
		<div class="post">
				<h5>
						<a id="FavoriteList1_Favorites_ctl02_TitleUrl" href="http://www.cnblogs.com/snowwolf/archive/2004/06/18/16644.html" target="_blank">.Net Code Cover</a>
				</h5>
		</div>
		<div class="post">
				<h5>
						<a id="FavoriteList1_Favorites_ctl03_TitleUrl" href="http://www.cnblogs.com/william_fire/archive/2004/11/15/63867.html" target="_blank">让代码更具有可测试性</a>
				</h5>
		</div>
		<div class="post">
				<h5>
						<a id="FavoriteList1_Favorites_ctl04_TitleUrl" href="http://www.cnblogs.com/anf/archive/2005/03/20/122342.html" target="_blank">NMock --- 从零开始</a>
				</h5>
		</div>
		<div class="post">
				<h5>
						<a id="FavoriteList1_Favorites_ctl05_TitleUrl" href="http://www.cnblogs.com/jiezhi/archive/2005/04/28/146779.html" target="_blank">Unit Test : rules,design and strategy</a>
				</h5>
		</div>
		<div class="post">
				<h5>
						<a id="FavoriteList1_Favorites_ctl06_TitleUrl" href="http://blog.joycode.com/musicland/archive/2005/04/16/48690.aspx" target="_blank">Concurrent connection limit</a>
				</h5>
		</div>
		<div class="post">
				<h5>
						<a id="FavoriteList1_Favorites_ctl07_TitleUrl" href="http://www.cnblogs.com/iaxes/articles/121158.html" target="_blank">Log4Net Appender 之 ADONetAppender</a>
				</h5>
		</div>
		<div class="post">
				<h5>
						<a id="FavoriteList1_Favorites_ctl08_TitleUrl" href="http://www.cnblogs.com/dahuzizyd/archive/2005/03/04/112566.html" target="_blank">Bug管理的流程和几个重点</a>
				</h5>
		</div>
		<div class="post">
				<h5>
						<a id="FavoriteList1_Favorites_ctl09_TitleUrl" href="http://blog.joycode.com/ghj/articles/44720.aspx" target="_blank">把单元测试代码跟项目代码放在一个工程中</a>
				</h5>
		</div>
		<div class="post">
				<h5>
						<a id="FavoriteList1_Favorites_ctl10_TitleUrl" href="http://www.cnblogs.com/java_aix/archive/2005/02/10/103717.html" target="_blank">Subversion配置安装教程（三） </a>
				</h5>
		</div>
		<div class="post">
				<h5>
						<a id="FavoriteList1_Favorites_ctl11_TitleUrl" href="http://www.cnblogs.com/java_aix/archive/2005/02/09/103500.html" target="_blank">Subversion配置安装教程（二） </a>
				</h5>
		</div>
		<div class="post">
				<h5>
						<a id="FavoriteList1_Favorites_ctl12_TitleUrl" href="http://www.cnblogs.com/java_aix/archive/2005/02/08/103399.html" target="_blank">Subversion配置安装教程（一）</a>
				</h5>
		</div>
		<div class="post">
				<h5>
						<a id="FavoriteList1_Favorites_ctl13_TitleUrl" href="http://www.cnblogs.com/william_fire/articles/102245.html" target="_blank">敏捷（AM）：TDD(Test Driven Development）实践与变化--&gt;TAD（Test Assist Development)</a>
				</h5>
		</div>
		<div class="post">
				<h5>
						<a id="FavoriteList1_Favorites_ctl14_TitleUrl" href="http://blog.joycode.com/uestc95/archive/2005/01/14/43057.aspx" target="_blank">如何在*.CS文件中加入版本跟踪信息？</a>
				</h5>
		</div>
		<div class="post">
				<h5>
						<a id="FavoriteList1_Favorites_ctl15_TitleUrl" href="http://www.cnblogs.com/xchunyu/archive/2005/01/12/90425.html" target="_blank">推荐一个关于"架构"的演示文稿(PPT)</a>
				</h5>
		</div>
		<div class="post">
				<h5>
						<a id="FavoriteList1_Favorites_ctl16_TitleUrl" href="http://blog.joycode.com/oldsidney/archive/2005/01/06/42468.aspx" target="_blank">什麼是 LoadRunner？ </a>
				</h5>
		</div>
		<div class="post">
				<h5>
						<a id="FavoriteList1_Favorites_ctl17_TitleUrl" href="http://www.cnblogs.com/dudu/archive/2004/12/18/78838.html" target="_blank">推荐一个不错的VS.NET集成单元测试工具TestDriven.NET</a>
				</h5>
		</div>
		<div class="post">
				<h5>
						<a id="FavoriteList1_Favorites_ctl18_TitleUrl" href="http://www.cnblogs.com/bigtall/archive/2004/11/24/68126.html" target="_blank">实战dailybuild-cc.net的配置</a>
				</h5>
		</div>
		<div class="post">
				<h5>
						<a id="FavoriteList1_Favorites_ctl19_TitleUrl" href="http://www.cnblogs.com/netcobra/archive/2004/11/24/68204.html" target="_blank">[翻译] NMock 两分钟教程</a>
				</h5>
		</div>
		<div class="post">
				<h5>
						<a id="FavoriteList1_Favorites_ctl20_TitleUrl" href="http://www.cnblogs.com/netcobra/archive/2004/11/24/68199.html" target="_blank">[翻译] NMock 简介</a>
				</h5>
		</div>
		<div class="post">
				<h5>
						<a id="FavoriteList1_Favorites_ctl21_TitleUrl" href="http://www.cnblogs.com/william_fire/archive/2004/11/15/64111.html" target="_blank">测试驱动开的的实例</a>
				</h5>
		</div>
		<div class="post">
				<h5>
						<a id="FavoriteList1_Favorites_ctl22_TitleUrl" href="http://www.cnblogs.com/William_Fire/archive/2004/11/15/63867.html" target="_blank">让代码更具有可测试性</a>
				</h5>
		</div>
		<div class="post">
				<h5>
						<a id="FavoriteList1_Favorites_ctl23_TitleUrl" href="http://www.cnblogs.com/rickie/archive/2004/11/08/61311.html" target="_blank">重构（Refactoring）技巧读书笔记 之三 </a>
				</h5>
		</div>
		<div class="post">
				<h5>
						<a id="FavoriteList1_Favorites_ctl24_TitleUrl" href="http://pfzhou.cnblogs.com/pvistely/archive/2004/11/11/62600.html" target="_blank">第一次用CVS后的流程小结（其实VSS也一样）</a>
				</h5>
		</div>
		<div class="post">
				<h5>
						<a id="FavoriteList1_Favorites_ctl25_TitleUrl" href="http://pfzhou.cnblogs.com/airforce1st/archive/2004/10/22/55303.html" target="_blank">我也再补充一个NANT使用注意事项</a>
				</h5>
		</div>
		<div class="post">
				<h5>
						<a id="FavoriteList1_Favorites_ctl26_TitleUrl" href="http://pfzhou.cnblogs.com/rickie/archive/2004/10/04/48859.aspx" target="_blank">重构（Refactoring）技巧读书笔记 之二</a>
				</h5>
		</div>
		<div class="post">
				<h5>
						<a id="FavoriteList1_Favorites_ctl27_TitleUrl" href="http://pfzhou.cnblogs.com/rickie/archive/2004/09/25/46577.aspx" target="_blank">重构（Refactoring）技巧读书笔记 之一</a>
				</h5>
		</div>
		<div class="post">
				<h5>
						<a id="FavoriteList1_Favorites_ctl28_TitleUrl" href="http://blog.csdn.net/ycw/archive/2004/09/05/95076.aspx" target="_blank">Bugzilla简明使用手则</a>
				</h5>
		</div>
		<div class="post">
				<h5>
						<a id="FavoriteList1_Favorites_ctl29_TitleUrl" href="http://pfzhou.cnblogs.com/ccBoy/archive/2004/08/31/38140.aspx" target="_blank">Daily Build 的链接</a>
				</h5>
		</div>
		<div class="post">
				<h5>
						<a id="FavoriteList1_Favorites_ctl30_TitleUrl" href="http://pfzhou.cnblogs.com/muddle/archive/2004/04/20/6673.aspx" target="_self">Test-Driven Development In .NET 部分译文</a>
				</h5>
		</div>
		<div class="post">
				<h5>
						<a id="FavoriteList1_Favorites_ctl31_TitleUrl" href="http://pfzhou.cnblogs.com/feidao/archive/2004/06/19/17050.aspx" target="_blank">有关于Refactor(重构)与Source(源)的比较</a>
				</h5>
		</div>
		<div class="post">
				<h5>
						<a id="FavoriteList1_Favorites_ctl32_TitleUrl" href="http://pfzhou.cnblogs.com/coolbug/archive/2004/07/26/27367.aspx" target="_blank">终于完成了DailyBuild</a>
				</h5>
		</div>
		<div class="post">
				<h5>
						<a id="FavoriteList1_Favorites_ctl33_TitleUrl" href="http://pfzhou.cnblogs.com/ccBoy/archive/2004/07/26/27363.aspx" target="_blank">用MSBuild.... DailyBuild和软件开发流程的东东</a>
				</h5>
		</div>
		<div class="post">
				<h5>
						<a id="FavoriteList1_Favorites_ctl34_TitleUrl" href="http://pfzhou.cnblogs.com/coolbug/archive/2004/07/27/27735.aspx" target="_blank">DailyBuild全攻略</a>
				</h5>
		</div>
		<div class="post">
				<h5>
						<a id="FavoriteList1_Favorites_ctl35_TitleUrl" href="http://pfzhou.cnblogs.com/unruledboy/archive/2004/08/13/33227.aspx" target="_blank">关于Peer Review、代码评审和测试驱动等</a>
				</h5>
		</div>
		<div class="post">
				<h5>
						<a id="FavoriteList1_Favorites_ctl36_TitleUrl" href="http://pfzhou.cnblogs.com/jobs/archive/2004/08/13/33065.aspx" target="_blank">测试开发驱动实践</a>
				</h5>
		</div>
<img src ="http://www.blogjava.net/jinfeng_wang/aggbug/43358.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jinfeng_wang/" target="_blank">jinfeng_wang</a> 2006-04-26 16:52 <a href="http://www.blogjava.net/jinfeng_wang/archive/2006/04/26/43358.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title> the difference between SqlConnection.IDisposable.Disp­­ose() and SqlConnection.Dispose()</title><link>http://www.blogjava.net/jinfeng_wang/archive/2006/01/23/28985.html</link><dc:creator>jinfeng_wang</dc:creator><author>jinfeng_wang</author><pubDate>Mon, 23 Jan 2006 01:35:00 GMT</pubDate><guid>http://www.blogjava.net/jinfeng_wang/archive/2006/01/23/28985.html</guid><wfw:comment>http://www.blogjava.net/jinfeng_wang/comments/28985.html</wfw:comment><comments>http://www.blogjava.net/jinfeng_wang/archive/2006/01/23/28985.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jinfeng_wang/comments/commentRss/28985.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jinfeng_wang/services/trackbacks/28985.html</trackback:ping><description><![CDATA[<P><A href="http://groups.google.com/group/microsoft.public.dotnet.framework.adonet/browse_frm/thread/eca73047adca7edb/12f1ae6ae176afe1?lnk=raot#12f1ae6ae176afe1">http://groups.google.com/group/microsoft.public.dotnet.framework.adonet/browse_frm/thread/eca73047adca7edb/12f1ae6ae176afe1?lnk=raot#12f1ae6ae176afe1</A><BR><BR><BR><BR>1. <A href="mailto:jinfeng_W...@msn.com">jinfeng_W...@msn.com</A> <BR>&nbsp;1月20日 上午9时56分&nbsp;&nbsp; 显示选项 </P>
<P>新闻论坛：microsoft.public.dotnet.framework.adonet <BR>发件人： <A href="mailto:jinfeng_W...@msn.com">jinfeng_W...@msn.com</A> - 查找此作者的帖子&nbsp; <BR>日期：19 Jan 2006 17:56:57 -0800 <BR>当地时间：2006年1月20日(星期五) 上午9时56分&nbsp; <BR>主题：the difference between SqlConnection.IDisposable.Disp&shy;ose() and SqlConnection.Dispose() <BR>回复 | 答复作者 | 转发 | 打印 | 显示个别帖子 | 显示原始邮件 | 删除 | 报告滥用行为&nbsp; </P>
<P>hi, I have a question about the difference between <BR>SqlConnection.IDisposable.Dispose()&nbsp; and&nbsp; SqlConnection.Dispose(). Both <BR>&nbsp;of them realize the function of releasing the connection to the <BR>ConnectionPool?&nbsp; Do they have the same effection source code?&nbsp; If they <BR>are different, who can tell me the differences? If they are same, why <BR>MS gives the SqlConnection.IDisposable.Dispose, but&nbsp; only <BR>SqlConnection.Dispose() method? </P>
<P><BR>In the MSDN, there are following description about the <BR>SqlConnection.IDisposable.Dispose Method: <BR>&nbsp; "This member supports the .NET Framework infrastructure and is not <BR>intended to be used directly from your code."&nbsp; what's the meaning of <BR>it? </P>
<P><BR>If the user has called the SqlConnection.IDisposable.Dispose() in the <BR>client application, what probem results in?&nbsp; and if there are some <BR>problem becomes, then why did MS give us such a method? </P>
<P><BR>in the same, who can tell me the using of <BR>"SqlConnection.ICloneable.Clone ", <BR>"SqlConnection.IDbConnection.BeginTransaction" and <BR>"SqlConnection.IDbConnection.CreateCommand"? </P>
<P><BR>Anybody can help me to solve my question? thanks a lot. </P>
<P><BR>回复 <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </P>
<P><BR>&nbsp;&nbsp; 2. Cor Ligthert [MVP] <BR>&nbsp;1月20日 下午3时10分&nbsp;&nbsp; 显示选项 </P>
<P>新闻论坛：microsoft.public.dotnet.framework.adonet <BR>发件人： "Cor Ligthert [MVP]" &lt;<A href="mailto:notmyfirstn...@planet.nl">notmyfirstn...@planet.nl</A>&gt; - 查找此作者的帖子&nbsp; <BR>日期：Fri, 20 Jan 2006 08:10:00 +0100 <BR>主题：Re: the difference between SqlConnection.IDisposable.Disp&shy;ose() and SqlConnection.Dispose() <BR>回复 | 答复作者 | 转发 | 打印 | 显示个别帖子 | 显示原始邮件 | 报告滥用行为&nbsp; </P>
<P>JinFeng, </P>
<P><BR>Both of them are removing the connectionstring from the connectionobject. <BR>They both have nothing to do direct with the ConnectionPool, although you <BR>should close a connection either by close or dispose to let the <BR>ConnectionPool do its job. </P>
<P><BR>Every Interface can be used to get its members (in the implementing <BR>contract) from the implementing class. Therefore it is an interface. A good <BR>programmer start the name of his interfaces all with a capital I. </P>
<P><BR>I hope that this gives an idea </P>
<P><BR>Cor </P>
<P><BR>回复 <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </P>
<P><BR>&nbsp;&nbsp; 3. <A href="mailto:jinfeng_Wang@msn.com">jinfeng_Wang@msn.com</A> <BR>&nbsp;1月20日 下午5时52分&nbsp;&nbsp; 显示选项 </P>
<P>新闻论坛：microsoft.public.dotnet.framework.adonet <BR>发件人： "<A href="mailto:jinfeng_W...@msn.com">jinfeng_W...@msn.com</A>" &lt;<A href="mailto:jinfeng_W...@msn.com">jinfeng_W...@msn.com</A>&gt; - 查找此作者的帖子&nbsp; <BR>日期：20 Jan 2006 01:52:12 -0800 <BR>当地时间：2006年1月20日(星期五) 下午5时52分&nbsp; <BR>主题：Re: the difference between SqlConnection.IDisposable.Disp&shy;ose() and SqlConnection.Dispose() <BR>回复 | 答复作者 | 转发 | 打印 | 显示个别帖子 | 显示原始邮件 | 删除 | 报告滥用行为&nbsp; </P>
<P>hello, Cor . thanks for you answer. but it's not what i want. </P>
<P><BR>please read the following URL and look at the left frame: <BR><A href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpre">http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpre</A>... </P>
<P><BR>there are two methods in the SQLConnection: <BR>&nbsp;"Dispose"&nbsp; and&nbsp; "SqlConnection.IDisposable.Dispose" Method. </P>
<P><BR>and in MSDN , the description about the <BR>"SqlConnection.IDisposable.Dispose" is:&nbsp; "This member supports the .NET <BR>Framework infrastructure and is not intended to be used directly from <BR>your code". <BR>can you help to tell me, what's the meaning of the above setence? MS <BR>advice me that i should not call the <BR>"SqlConnection.IDisposable.Dispose", yeah? </P>
<P><BR>if there is a client code, like this&nbsp; (copied from&nbsp; MSDN, and i have <BR>modified it): <BR>public void ReadMyData() <BR>{ <BR>&nbsp;&nbsp;&nbsp; String&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; myConnString = "Persist Security Info=False;User <BR>ID=sa;Initial Catalog=Northwind;Data Source=DTK-S-SVR;User <BR>ID=sa;Pwd=sa"; <BR>&nbsp;&nbsp; string mySelectQuery = "SELECT OrderID, CustomerID FROM Orders"; <BR>&nbsp;&nbsp; SqlConnection myConnection = new SqlConnection(myConnString); <BR>&nbsp;&nbsp; SqlCommand myCommand = new SqlCommand(mySelectQuery,myConnection); <BR>&nbsp;&nbsp; myConnection.Open(); <BR>&nbsp;&nbsp; SqlDataReader myReader; <BR>&nbsp;&nbsp; myReader = myCommand.ExecuteReader(); <BR>&nbsp;&nbsp; while (myReader.Read()) <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; { <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Console.WriteLine(myReader.GetInt32(0) + ", " + <BR>myReader.GetString(1)); <BR>&nbsp;&nbsp;&nbsp;&nbsp; } <BR>&nbsp;&nbsp;&nbsp; myReader.Close(); </P>
<P><BR>&nbsp;&nbsp;&nbsp;&nbsp; //myConnection.Close();&nbsp; // old source code. <BR>&nbsp;&nbsp;&nbsp;&nbsp; IDisposable disposable =myConnection as IDisposable;&nbsp;&nbsp; // new <BR>source code. <BR>&nbsp;&nbsp;&nbsp;&nbsp; disposable.Dispose();&nbsp;&nbsp; // new source code. </P>
<P><BR>} </P>
<P><BR>will it cause some problem when cast the myConnection&nbsp; to IDisposable <BR>and call&nbsp; disposable.Dispose() ? <BR>Does the "new souce code" have the same effect as the "old source code" <BR>?? <BR>if they are, why MS implements the method of "IDisposable.Disp&shy;ose()" <BR>explicity. I means: there is only the SqlConnection.Dispose() method. </P>
<P>if there is no problem here, then can you why MS said that "This member <BR>supports the .NET Framework infrastructure and is not intended to be <BR>used directly from your code."?? <BR>if there is some problem, then why MS does not make the methods of <BR>SqlConnection.IDisposable.Disp&shy;&shy;ose() and SqlConnection.Dispose() <BR>shared the same source code? and why MS tell us that there exists a <BR>"SqlConnection.IDisposable.Disp&shy;&shy;ose()" method, but warn us not to <BR>call it?? </P>
<P><BR>in the same, how about the "SqlConnection.ICloneable.Clone ", <BR>"SqlConnection.IDbConnection.BeginTransaction" and <BR>"SqlConnection.IDbConnection.CreateCommand"? </P>
<P><BR>i do not whether you have understand my question for my poor english <BR>and expression. <BR>can you help me? anyway, thanks to all of you. </P>
<P><BR>回复 <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </P>
<P><BR>&nbsp;&nbsp; 4. Cor Ligthert [MVP] <BR>&nbsp;1月20日 下午6时29分&nbsp;&nbsp; 显示选项 </P>
<P>新闻论坛：microsoft.public.dotnet.framework.adonet <BR>发件人： "Cor Ligthert [MVP]" &lt;<A href="mailto:notmyfirstn...@planet.nl">notmyfirstn...@planet.nl</A>&gt; - 查找此作者的帖子&nbsp; <BR>日期：Fri, 20 Jan 2006 11:29:36 +0100 <BR>当地时间：2006年1月20日(星期五) 下午6时29分&nbsp; <BR>主题：Re: the difference between SqlConnection.IDisposable.Disp&shy;ose() and SqlConnection.Dispose() <BR>回复 | 答复作者 | 转发 | 打印 | 显示个别帖子 | 显示原始邮件 | 报告滥用行为&nbsp; </P>
<P>JinFeng, </P>
<P><BR>Both Frans and me are not first language English speakers (AFAIK is Frans <BR>born where the native language is Fries and I where it is Dutch). In my <BR>opinion should you never excuse you for your English in these newsgroups. <BR>Almost everybody, no matter how good he can speak a language, will make <BR>errors in email messages (even in his own). I assume that you are using the <BR>English version of Visual Studio so that tells enough. </P>
<P><BR>I thought that the protected dispose is used by component (don't mix this up <BR>with the rest from what I write about component). If you open a form or a <BR>component (by the designer), than you see the implementation of IDisposable <BR>direct. That part is used to do the most of the disposing. </P>
<P><BR>Nice pages about Idisposable are these new ones </P>
<P><BR><A href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpre">http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpre</A>... </P>
<P><BR><A href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpre">http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpre</A>... </P>
<P><BR>As I said, be aware that if you use a form or a component the code is <BR>already in the designer created part. </P>
<P><BR>However as long discussions has be done in this newsgroup. Disposing and <BR>Closing have the same effect on the connection pool. </P>
<P><BR>I hope this gives some information. </P>
<P><BR>Cor </P>
<P><BR>回复 <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </P>
<P><BR>&nbsp;&nbsp; 5. Frans Bouma [C# MVP] <BR>&nbsp;1月20日 下午4时22分&nbsp;&nbsp; 显示选项 </P>
<P>新闻论坛：microsoft.public.dotnet.framework.adonet <BR>发件人： "Frans Bouma [C# MVP]" &lt;<A href="mailto:perseus.usenetNOS...@xs4all.nl">perseus.usenetNOS...@xs4all.nl</A>&gt; - 查找此作者的帖子&nbsp; <BR>日期：Fri, 20 Jan 2006 00:22:50 -0800 <BR>当地时间：2006年1月20日(星期五) 下午4时22分&nbsp; <BR>主题：Re: the difference between SqlConnection.IDisposable.Disp&shy;ose() and SqlConnection.Dispose() <BR>回复 | 答复作者 | 转发 | 打印 | 显示个别帖子 | 显示原始邮件 | 报告滥用行为&nbsp; </P>
<P>&nbsp;</P>
<P><BR>- 隐藏被引用文字 -<BR>- 显示引用的文字 -</P>
<P><A href="mailto:jinfeng_W...@msn.com">jinfeng_W...@msn.com</A> wrote: <BR>&gt; hi, I have a question about the difference between <BR>&gt; SqlConnection.IDisposable.Dispose()&nbsp; and&nbsp; SqlConnection.Dispose(). <BR>&gt; Both&nbsp; of them realize the function of releasing the connection to the <BR>&gt; ConnectionPool?&nbsp; Do they have the same effection source code?&nbsp; If they <BR>&gt; are different, who can tell me the differences? If they are same, why <BR>&gt; MS gives the SqlConnection.IDisposable.Dispose, but&nbsp; only <BR>&gt; SqlConnection.Dispose() method? </P>
<P>&gt; In the MSDN, there are following description about the <BR>&gt; SqlConnection.IDisposable.Dispose Method: <BR>&gt;&nbsp;&nbsp; "This member supports the .NET Framework infrastructure and is not <BR>&gt; intended to be used directly from your code."&nbsp; what's the meaning of <BR>&gt; it? </P>
<P><BR>&gt; If the user has called the SqlConnection.IDisposable.Dispose() in the <BR>&gt; client application, what probem results in?&nbsp; and if there are some <BR>&gt; problem becomes, then why did MS give us such a method? </P>
<P><BR>&gt; in the same, who can tell me the using of <BR>&gt; "SqlConnection.ICloneable.Clone ", <BR>&gt; "SqlConnection.IDbConnection.BeginTransaction" and <BR>&gt; "SqlConnection.IDbConnection.CreateCommand"? </P>
<P><BR>&gt; Anybody can help me to solve my question? thanks a lot. </P>
<P>&nbsp;</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; what does 'SqlConnection.IDisposable.Dispose' mean? 'IDisposable' <BR>isn't a property or something of SqlConnection. 'Dispose()' is a method <BR>in Component, the base class of SqlConnection. SqlConnection overrides <BR>Dispose(true), which is called from Dispose(), and therefore whatever <BR>Dispose() you call, it doesnt matter. </P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; FB </P>
<P><BR>-- <BR>------------------------------------------------------------------------ <BR>Get LLBLGen Pro, productive O/R mapping for .NET: <A href="http://www.llblgen.com">http://www.llblgen.com</A> <BR>My .NET blog: <A href="http://weblogs.asp.net/fbouma">http://weblogs.asp.net/fbouma</A> <BR>Microsoft MVP (C#) <BR>------------------------------------------------------------------------ </P>
<P><BR>回复 <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </P>
<P><BR>&nbsp;&nbsp; 6. <A href="mailto:jinfeng_Wang@msn.com">jinfeng_Wang@msn.com</A> <BR>&nbsp;1月20日 下午5时55分&nbsp;&nbsp; 显示选项 </P>
<P>新闻论坛：microsoft.public.dotnet.framework.adonet <BR>发件人： "<A href="mailto:jinfeng_W...@msn.com">jinfeng_W...@msn.com</A>" &lt;<A href="mailto:jinfeng_W...@msn.com">jinfeng_W...@msn.com</A>&gt; - 查找此作者的帖子&nbsp; <BR>日期：20 Jan 2006 01:55:46 -0800 <BR>当地时间：2006年1月20日(星期五) 下午5时55分&nbsp; <BR>主题：Re: the difference between SqlConnection.IDisposable.Disp&shy;ose() and SqlConnection.Dispose() <BR>回复 | 答复作者 | 转发 | 打印 | 显示个别帖子 | 显示原始邮件 | 删除 | 报告滥用行为&nbsp; </P>
<P>FB, i know that 'IDisposable'&nbsp; is not one property of the <BR>SQLConnection. please read my answer to Cor. <BR>'SqlConnection.IDisposable.Dispose'&nbsp; is copied from MSDN.&nbsp;&nbsp;&nbsp; :-) <BR>I think it means that SQLConnection has implemnt the Dispose() method <BR>of the IDisposable explicity. </P>
<P><BR>I want to know the difference between the two method of <BR>SqlConnection.IDisposable.Disp&shy;&shy;ose() and SqlConnection.Dispose(). </P>
<P><BR>Thanks to you!!! thanks! </P>
<P><BR>回复 <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </P>
<P><BR>&nbsp;&nbsp; 7. Frans Bouma [C# MVP] <BR>&nbsp;1月21日 下午6时54分&nbsp;&nbsp; 显示选项 </P>
<P>新闻论坛：microsoft.public.dotnet.framework.adonet <BR>发件人： "Frans Bouma [C# MVP]" &lt;<A href="mailto:perseus.usenetNOS...@xs4all.nl">perseus.usenetNOS...@xs4all.nl</A>&gt; - 查找此作者的帖子&nbsp; <BR>日期：Sat, 21 Jan 2006 02:54:24 -0800 <BR>当地时间：2006年1月21日(星期六) 下午6时54分&nbsp; <BR>主题：Re: the difference between SqlConnection.IDisposable.Disp&shy;ose() and SqlConnection.Dispose() <BR>回复 | 答复作者 | 转发 | 打印 | 显示个别帖子 | 显示原始邮件 | 报告滥用行为&nbsp; </P>
<P>&nbsp;</P>
<P><A href="mailto:jinfeng_W...@msn.com">jinfeng_W...@msn.com</A> wrote: <BR>&gt; FB, i know that 'IDisposable'&nbsp; is not one property of the <BR>&gt; SQLConnection. please read my answer to Cor. <BR>&gt; 'SqlConnection.IDisposable.Dispose'&nbsp; is copied from MSDN.&nbsp;&nbsp;&nbsp; :-) <BR>&gt; I think it means that SQLConnection has implemnt the Dispose() method <BR>&gt; of the IDisposable explicity. </P>
<P><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; I thought that it meant that, but checking SqlConnection in reflector <BR>I couldn't find IDisposable explicit implementations :D Hence my <BR>question :) </P>
<P><BR>&gt; I want to know the difference between the two method of <BR>&gt; SqlConnection.IDisposable.Disp&shy;&shy;ose() and SqlConnection.Dispose(). </P>
<P><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; I have no idea. </P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; FB </P>
<P><BR>-- <BR>------------------------------------------------------------------------ <BR>Get LLBLGen Pro, productive O/R mapping for .NET: <A href="http://www.llblgen.com">http://www.llblgen.com</A> <BR>My .NET blog: <A href="http://weblogs.asp.net/fbouma">http://weblogs.asp.net/fbouma</A> <BR>Microsoft MVP (C#) <BR>------------------------------------------------------------------------ </P>
<P><BR>回复 <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </P>
<P><BR>&nbsp;&nbsp; 8. <A href="mailto:jinfeng_Wang@msn.com">jinfeng_Wang@msn.com</A> <BR>&nbsp;1月23日 上午9时17分&nbsp;&nbsp; 显示选项 </P>
<P>新闻论坛：microsoft.public.dotnet.framework.adonet <BR>发件人： "<A href="mailto:jinfeng_W...@msn.com">jinfeng_W...@msn.com</A>" &lt;<A href="mailto:jinfeng_W...@msn.com">jinfeng_W...@msn.com</A>&gt; - 查找此作者的帖子&nbsp; <BR>日期：22 Jan 2006 17:17:00 -0800 <BR>主题：Re: the difference between SqlConnection.IDisposable.Disp&shy;ose() and SqlConnection.Dispose() <BR>回复 | 答复作者 | 转发 | 打印 | 显示个别帖子 | 显示原始邮件 | 删除 | 报告滥用行为&nbsp; </P>
<P>:-) </P>
<P><BR>copied from Reflector: </P>
<P><BR>System.Data.SqlClient.SqlConnection.System.Data.IDbConnection.BeginTransact&shy;ion() <BR>: IDbTransaction <BR>IDbTransaction IDbConnection.BeginTransaction() <BR>{ <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return this.BeginTransaction(); </P>
<P>&nbsp;</P>
<P>} </P>
<P><BR>I think that the disposable is same as here . <BR>but, but&nbsp; why MS does such a foolish action :-( </P>
<P>this folliwing is copied from MSDN&nbsp; in the <BR>SqlConnection.IDbConnection.BeginTransaction Method (): <BR>"This member supports the .NET Framework infrastructure and is not <BR>intended to be used directly from your code." <BR>faint to death. ~ </P>
<P><BR>回复 <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </P>
<P><BR>&nbsp;&nbsp; 9. William (Bill) Vaughn <BR>&nbsp;1月22日 上午2时55分&nbsp;&nbsp; 显示选项 </P>
<P>新闻论坛：microsoft.public.dotnet.framework.adonet <BR>发件人： "William \(Bill\) Vaughn" &lt;<A href="mailto:billvaRemoveT...@nwlink.com">billvaRemoveT...@nwlink.com</A>&gt; - 查找此作者的帖子&nbsp; <BR>日期：Sat, 21 Jan 2006 10:55:46 -0800 <BR>当地时间：2006年1月22日(星期日) 上午2时55分&nbsp; <BR>主题：Re: the difference between SqlConnection.IDisposable.Disp&shy;ose() and SqlConnection.Dispose() <BR>回复 | 答复作者 | 转发 | 打印 | 显示个别帖子 | 显示原始邮件 | 报告滥用行为&nbsp; </P>
<P>Ok... if you're that curious, use Reflector to walk through the .Net <BR>Framework code to see what it does. You must have a lot more time on your <BR>hands that we do. </P>
<P><BR>Frankly, it does not matter what they do. You don't need to call <BR>them--either of them. As long as you use Close on the Connection you're <BR>fine. Sure, you can call Dispose if you want to, but it won't help the <BR>problem you're trying to solve. </P>
<P><BR>-- <BR>____________________________________ <BR>William (Bill) Vaughn <BR>Author, Mentor, Consultant <BR>Microsoft MVP <BR>INETA Speaker <BR><A href="http://www.betav.com/blog/billva">www.betav.com/blog/billva</A> <BR><A href="http://www.betav.com">www.betav.com</A> <BR>Please reply only to the newsgroup so that others can benefit. <BR>This posting is provided "AS IS" with no warranties, and confers no rights. <BR>__________________________________ </P>
<P><BR>"Frans Bouma [C# MVP]" &lt;<A href="mailto:perseus.usenetNOS...@xs4all.nl">perseus.usenetNOS...@xs4all.nl</A>&gt; wrote in message <BR><A href="news:xn0ehgc512v14b001@news.microsoft.com">news:xn0ehgc512v14b001@news.microsoft.com</A>... </P>
<P>&nbsp;</P>
<P>- 隐藏被引用文字 -<BR>- 显示引用的文字 -</P>
<P>&gt; <A href="mailto:jinfeng_W...@msn.com">jinfeng_W...@msn.com</A> wrote: </P>
<P>&gt;&gt; hi, I have a question about the difference between <BR>&gt;&gt; SqlConnection.IDisposable.Dispose()&nbsp; and&nbsp; SqlConnection.Dispose(). <BR>&gt;&gt; Both&nbsp; of them realize the function of releasing the connection to the <BR>&gt;&gt; ConnectionPool?&nbsp; Do they have the same effection source code?&nbsp; If they <BR>&gt;&gt; are different, who can tell me the differences? If they are same, why <BR>&gt;&gt; MS gives the SqlConnection.IDisposable.Dispose, but&nbsp; only <BR>&gt;&gt; SqlConnection.Dispose() method? </P>
<P><BR>&gt;&gt; In the MSDN, there are following description about the <BR>&gt;&gt; SqlConnection.IDisposable.Dispose Method: <BR>&gt;&gt;&nbsp;&nbsp; "This member supports the .NET Framework infrastructure and is not <BR>&gt;&gt; intended to be used directly from your code."&nbsp; what's the meaning of <BR>&gt;&gt; it? </P>
<P><BR>&gt;&gt; If the user has called the SqlConnection.IDisposable.Dispose() in the <BR>&gt;&gt; client application, what probem results in?&nbsp; and if there are some <BR>&gt;&gt; problem becomes, then why did MS give us such a method? </P>
<P><BR>&gt;&gt; in the same, who can tell me the using of <BR>&gt;&gt; "SqlConnection.ICloneable.Clone ", <BR>&gt;&gt; "SqlConnection.IDbConnection.BeginTransaction" and <BR>&gt;&gt; "SqlConnection.IDbConnection.CreateCommand"? </P>
<P><BR>&gt;&gt; Anybody can help me to solve my question? thanks a lot. </P>
<P><BR>&gt; what does 'SqlConnection.IDisposable.Dispose' mean? 'IDisposable' <BR>&gt; isn't a property or something of SqlConnection. 'Dispose()' is a method <BR>&gt; in Component, the base class of SqlConnection. SqlConnection overrides <BR>&gt; Dispose(true), which is called from Dispose(), and therefore whatever <BR>&gt; Dispose() you call, it doesnt matter. </P>
<P><BR>&gt; FB </P>
<P><BR>&gt; -- <BR>&gt; ------------------------------------------------------------------------ <BR>&gt; Get LLBLGen Pro, productive O/R mapping for .NET: <A href="http://www.llblgen.com">http://www.llblgen.com</A> <BR>&gt; My .NET blog: <A href="http://weblogs.asp.net/fbouma">http://weblogs.asp.net/fbouma</A> <BR>&gt; Microsoft MVP (C#) <BR>&gt; ------------------------------------------------------------------------ </P>
<P>&nbsp;</P>
<P>回复 <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </P>
<P><BR>&nbsp;&nbsp; 10. Frans Bouma [C# MVP] <BR>&nbsp;1月22日 下午6时52分&nbsp;&nbsp; 显示选项 </P>
<P>新闻论坛：microsoft.public.dotnet.framework.adonet <BR>发件人： "Frans Bouma [C# MVP]" &lt;<A href="mailto:perseus.usenetNOS...@xs4all.nl">perseus.usenetNOS...@xs4all.nl</A>&gt; - 查找此作者的帖子&nbsp; <BR>日期：Sun, 22 Jan 2006 02:52:11 -0800 <BR>当地时间：2006年1月22日(星期日) 下午6时52分&nbsp; <BR>主题：Re: the difference between SqlConnection.IDisposable.Disp&shy;ose() and SqlConnection.Dispose() <BR>回复 | 答复作者 | 转发 | 打印 | 显示个别帖子 | 显示原始邮件 | 报告滥用行为&nbsp; </P>
<P>&nbsp;</P>
<P>William (Bill) Vaughn wrote: <BR>&gt; Ok... if you're that curious, use Reflector to walk through the .Net <BR>&gt; Framework code to see what it does. You must have a lot more time on <BR>&gt; your hands that we do. </P>
<P>&gt; Frankly, it does not matter what they do. You don't need to call <BR>&gt; them--either of them. As long as you use Close on the Connection <BR>&gt; you're fine. Sure, you can call Dispose if you want to, but it won't <BR>&gt; help the problem you're trying to solve. </P>
<P>&nbsp;</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; On a side note: not all ADO.NET providers' connection objects can <BR>live without a Dispose call. For example the Firebird .NET provider and <BR>the ODP.NET providers do need a call to Dispose to properly clean up. <BR>(especially firebird, for cleaning up on the server side!). </P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; FB, who still couldn't find an explicit IDisposable implementation on <BR>SqlConnection... </P>
<P><BR>-- <BR>------------------------------------------------------------------------ <BR>Get LLBLGen Pro, productive O/R mapping for .NET: <A href="http://www.llblgen.com">http://www.llblgen.com</A> <BR>My .NET blog: <A href="http://weblogs.asp.net/fbouma">http://weblogs.asp.net/fbouma</A> <BR>Microsoft MVP (C#) <BR>------------------------------------------------------------------------ </P>
<P><BR>回复 <BR>&nbsp;<BR>11. <A href="mailto:jinfeng_Wang@msn.com">jinfeng_Wang@msn.com</A> <BR>&nbsp;1月23日 上午8时55分&nbsp;&nbsp; 显示选项 </P>
<P>新闻论坛：microsoft.public.dotnet.framework.adonet <BR>发件人： "<A href="mailto:jinfeng_W...@msn.com">jinfeng_W...@msn.com</A>" &lt;<A href="mailto:jinfeng_W...@msn.com">jinfeng_W...@msn.com</A>&gt; - 查找此作者的帖子&nbsp; <BR>日期：22 Jan 2006 16:55:26 -0800 <BR>当地时间：2006年1月23日(星期一) 上午8时55分&nbsp; <BR>主题：Re: the difference between SqlConnection.IDisposable.Disp&shy;ose() and SqlConnection.Dispose() <BR>回复 | 答复作者 | 转发 | 打印 | 显示个别帖子 | 显示原始邮件 | 删除 | 报告滥用行为&nbsp; </P>
<P>thanks all of you. </P>
<P><BR>If i was just a client programmer, i think all the information from you <BR>and MSDN is enough. <BR>but,...&nbsp; now i am trying&nbsp; to develop one new .NET Data Provider for own <BR>database. <BR>so i want&nbsp; to know what has happened in SQL .NET Data Provider. <BR>it give me a dozens of puzzling interface. <BR>here is "SqlConnection.IDisposable.Disp&shy;&shy;ose() " and <BR>"SqlConnection.Dispose()". </P>
<P><BR>In fact, the SQLConnection is inherited from Compnent, which has <BR>implemented the IDisposable. <BR>This is very like the question of "deadly diamond", that one Class <BR>inherits one Interface from TWO path. <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; IDisposable <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&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;&nbsp; |&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; | <BR>Component&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; IDBConnection <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&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;&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; SQLConnection </P>
<P><BR>if there is no difference between the <BR>SqlConnection.IDisposable.Disp&shy;&shy;ose() and SqlConnection.Dispose(), <BR>then MS has no need to implement the <BR>SqlConnection.IDisposable.Disp&shy;&shy;ose() explicitly. But, it has done <BR>it, that means there are some differences between them. What's that? <BR>MSDN has not told us, it just warn us not to call&nbsp; the <BR>SqlConnection.IDisposable.Disp&shy;&shy;ose() in the client program.&nbsp;&nbsp;&nbsp; :( </P>
<P><BR>In the&nbsp; Oracle® Data Provider for .NET, <BR>&nbsp;&nbsp;&nbsp;&nbsp; public sealed class OracleConnection : Component, IDbConnection, <BR>ICloneable <BR>but, OracleConnection has not implement the <BR>IDbConnection.IDisposable.Disposable() explicity. </P>
<P><BR>MS, :-( </P>
<P><BR>&nbsp;14. Frans Bouma [C# MVP] <BR>&nbsp;1月23日 下午5时54分&nbsp;&nbsp; 显示选项 </P>
<P>新闻论坛：microsoft.public.dotnet.framework.adonet <BR>发件人： "Frans Bouma [C# MVP]" &lt;<A href="mailto:perseus.usenetNOS...@xs4all.nl">perseus.usenetNOS...@xs4all.nl</A>&gt; - 查找此作者的帖子&nbsp; <BR>日期：Mon, 23 Jan 2006 01:54:56 -0800 <BR>当地时间：2006年1月23日(星期一) 下午5时54分&nbsp; <BR>主题：Re: the difference between SqlConnection.IDisposable.Disp&shy;ose() and SqlConnection.Dispose() <BR>回复 | 答复作者 | 转发 | 打印 | 显示个别帖子 | 显示原始邮件 | 报告滥用行为&nbsp; </P>
<P>&nbsp;</P>
<P><A href="mailto:jinfeng_W...@msn.com">jinfeng_W...@msn.com</A> wrote: <BR>&gt; If i was just a client programmer, i think all the information from <BR>&gt; you and MSDN is enough. <BR>&gt; but,...&nbsp; now i am trying&nbsp; to develop one new .NET Data Provider for <BR>&gt; own database. </P>
<P><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; there are guidelines for that if I'm not mistaken. And I think you <BR>should relax a little. I work with a lot of ADO.NET providers and none <BR>of them works the same as the others. So 'what should be done' is what <BR>you think is the easiest for your users :). </P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; So, 'Close()' should clean up, and for example connection.Dispose() <BR>should also dispose commands, parameters etc. </P>
<P><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; In general derive a class from DbConnection, and override the specific <BR>methods to add your own code. </P>
<P>&nbsp;</P>
<P>&gt; so i want&nbsp; to know what has happened in SQL .NET Data Provider. <BR>&gt; it give me a dozens of puzzling interface. <BR>&gt; here is "SqlConnection.IDisposable.Disp&shy;&shy;ose() " and <BR>&gt; "SqlConnection.Dispose()". </P>
<P><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Have you looked into the code with reflector ? I think you should do <BR>that. :) </P>
<P>&nbsp;</P>
<P>- 隐藏被引用文字 -<BR>- 显示引用的文字 -</P>
<P>&gt; In fact, the SQLConnection is inherited from Compnent, which has <BR>&gt; implemented the IDisposable. <BR>&gt; This is very like the question of "deadly diamond", that one Class <BR>&gt; inherits one Interface from TWO path. <BR>&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; IDisposable <BR>&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \ <BR>&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; | <BR>&gt; Component&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; IDBConnection <BR>&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; | <BR>&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; / <BR>&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SQLConnection </P>
<P>&gt; if there is no difference between the <BR>&gt; SqlConnection.IDisposable.Disp&shy;&shy;ose() and SqlConnection.Dispose(), <BR>&gt; then MS has no need to implement the <BR>&gt; SqlConnection.IDisposable.Disp&shy;&shy;ose() explicitly. But, it has done <BR>&gt; it, that means there are some differences between them. What's that? <BR>&gt; MSDN has not told us, it just warn us not to call&nbsp; the <BR>&gt; SqlConnection.IDisposable.Disp&shy;&shy;ose() in the client program.&nbsp;&nbsp;&nbsp; :( </P>
<P>&nbsp;</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; I looked up the page, and I can only get to that page through the <BR>index. So I think it's a mistake in the MSDN. As said by others in this <BR>thread, look at the code through reflector first, then come back here <BR>with questions. </P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Also, inherited interfaces are simply type definitions, not <BR>implementations. So 1 routine can serve the Dispose() method of <BR>multiple interfaces. </P>
<P>&nbsp;</P>
<P>&gt; In the&nbsp; Oracle® Data Provider for .NET, <BR>&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public sealed class OracleConnection : Component, IDbConnection, <BR>&gt; ICloneable <BR>&gt; but, OracleConnection has not implement the <BR>&gt; IDbConnection.IDisposable.Disposable() explicity. </P>
<P>&gt; MS, :-( </P>
<P>&nbsp;</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Neither has sqlconnection!!! Look into the code! Just because it's an <BR>error in the MSDN doesn't mean it's true. Why do you ignore what we <BR>said and keep believing an errorous page in the msdn? </P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; FB </P>
<P><BR>-- <BR>------------------------------------------------------------------------ <BR>Get LLBLGen Pro, productive O/R mapping for .NET: <A href="http://www.llblgen.com">http://www.llblgen.com</A> <BR>My .NET blog: <A href="http://weblogs.asp.net/fbouma">http://weblogs.asp.net/fbouma</A> <BR>Microsoft MVP (C#) <BR>------------------------------------------------------------------------ </P>
<P><BR>回复 <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </P>
<P>&nbsp;<BR>&nbsp;&nbsp; 15. <A href="mailto:jinfeng_Wang@msn.com">jinfeng_Wang@msn.com</A> <BR>&nbsp;1月23日 下午10时15分&nbsp;&nbsp; 显示选项 </P>
<P>新闻论坛：microsoft.public.dotnet.framework.adonet <BR>发件人： "<A href="mailto:jinfeng_W...@msn.com">jinfeng_W...@msn.com</A>" &lt;<A href="mailto:jinfeng_W...@msn.com">jinfeng_W...@msn.com</A>&gt; - 查找此作者的帖子&nbsp; <BR>日期：23 Jan 2006 06:15:59 -0800 <BR>当地时间：2006年1月23日(星期一) 下午10时15分&nbsp; <BR>主题：Re: the difference between SqlConnection.IDisposable.Disp&shy;ose() and SqlConnection.Dispose() <BR>回复 | 答复作者 | 转发 | 打印 | 显示个别帖子 | 显示原始邮件 | 删除 | 报告滥用行为&nbsp; </P>
<P>:-) </P>
<P><BR>MSDN Error？ <BR>why the MS make such an Error . <BR>I have said that&nbsp; now i am developing one .NET Data Provider for my own <BR>database, so i should know the everything what have happened in the SQL <BR>.NET&nbsp; Data Provider, and learning something from the MS's code. </P>
<P><BR>now i have realized why MS&nbsp; give us such a SQL .NET&nbsp; Data Provider. <BR>wait a minute, and i&nbsp; will write it clearly. </P>
<P><BR>回复 <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </P>
<P>&nbsp;<BR>&nbsp;&nbsp; 16. <A href="mailto:jinfeng_Wang@msn.com">jinfeng_Wang@msn.com</A> <BR>&nbsp;1月23日 下午11时10分&nbsp;&nbsp; 显示选项 </P>
<P>新闻论坛：microsoft.public.dotnet.framework.adonet <BR>发件人： "<A href="mailto:jinfeng_W...@msn.com">jinfeng_W...@msn.com</A>" &lt;<A href="mailto:jinfeng_W...@msn.com">jinfeng_W...@msn.com</A>&gt; - 查找此作者的帖子&nbsp; <BR>日期：23 Jan 2006 07:10:37 -0800 <BR>当地时间：2006年1月23日(星期一) 下午11时10分&nbsp; <BR>主题：Re: the difference between SqlConnection.IDisposable.Disp&shy;ose() and SqlConnection.Dispose() <BR>回复 | 答复作者 | 转发 | 打印 | 显示个别帖子 | 显示原始邮件 | 删除 | 报告滥用行为&nbsp; </P>
<P>firstly, let's take a look at the following code. </P>
<P><BR>///-------------- <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public interface myInterface <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; { <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Object getobject(); <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } </P>
<P><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public class MyImplement : myInterface <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; { <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public String getobject()&nbsp;&nbsp;&nbsp; //override. <BR>&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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return "str"; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }///--------------- <BR>the code above can not be compiled, the compiler will have give us an <BR>error.&nbsp; Because the method of MyImplement.getobject() has a wrong <BR>return type, which is "String", but the method of <BR>MyInterface.getobject() declared that the return type is "Object:. Here <BR>the compiler take the "return type" into the "override" compile <BR>progregess. If MyImplement.getobject() returns "String",&nbsp; this is not <BR>the implementation of MyInterface.getobject(). </P>
<P><BR>now let's take a look at another code. <BR>///--------------- <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public class AnotherImplement <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; { <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public object getobject() <BR>&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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return new object(); <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } </P>
<P><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public string getobject()&nbsp; //overload <BR>&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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return new string(""); <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } </P>
<P><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } <BR>///--------------- <BR>Here I want to overload the method of "getobject()", but the compiler <BR>give us an error. Here the compiler DOES NOT take the "return type" <BR>into the "overload" compile progregess (DIFFERENCE WITH THE OVERRIDE). </P>
<P><BR>Now let's go back to the IDBConnection and SQLConnection. <BR>In the interface of IDbConnection, it declares one method of <BR>"createcomand". <BR>///---------- <BR>IDbConnection { <BR>&nbsp;&nbsp;&nbsp; .... <BR>&nbsp;&nbsp;&nbsp; IDbCommand CreateCommand(); <BR>&nbsp;&nbsp; .... </P>
<P><BR>} </P>
<P><BR>///---------- <BR>When design the interface of IDBConnection, the designer does not <BR>what's kind of "Command" will be returned, for example <BR>"SQLCommand","OracleCommand". so in the IDbConnection.CreateCommand(), <BR>it only return an interface of "IDbCommand". </P>
<P>Now we are design the SQLConnection which implements the interface of <BR>"IDbConnection". if in the SQLConnection, it only write : <BR>///--------- <BR>&nbsp; SQLConnection:IDbConnection { <BR>&nbsp;&nbsp; .... <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SQLCommand&nbsp; CreateCommand() { <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ........ <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } <BR>&nbsp;&nbsp; .... <BR>&nbsp;} <BR>///--------- <BR>the compiler will give us an error, because&nbsp; the "return type" is <BR>SQLCommand, but not IDbCommand(defined in the IDbConnection). THE <BR>COMPILER TAKE THE RETURN TYPE INTO THE COMPILE PROGRESS. so we must <BR>give another definition of "IDbConnection.CreateCommand()". </P>
<P><BR>if we write the code as follows: <BR>///--------- <BR>&nbsp; SQLConnection:IDbConnection { <BR>&nbsp;&nbsp; .... <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SQLCommand&nbsp; CreateCommand() { <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ........ <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } </P>
<P><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; IDBCommand CreateCommand() { <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ....... <BR>&nbsp;&nbsp;&nbsp;&nbsp; } <BR>&nbsp;&nbsp; .... <BR>&nbsp;} <BR>///--------- <BR>The compiler will give us an error. THE COMPILER DOES NOT TAKE THE <BR>RETURN TYPE INTO THE COMPILE PROGRESS. </P>
<P><BR>so the SQLConnection have to implement&nbsp; the <BR>IDbConncection.CreateCommand explicyly. <BR>///-------- <BR>&nbsp;&nbsp; SQLConnection:IDbConnection { <BR>&nbsp;&nbsp; .... <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SQLCommand&nbsp; CreateCommand() { <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ........ <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } </P>
<P><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; IDBCommand&nbsp; IDbConnection.CreateCommand() { <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return this.CreateCommand(); <BR>&nbsp;&nbsp;&nbsp;&nbsp; } <BR>&nbsp;&nbsp; .... <BR>&nbsp;} <BR>///--------- <BR>The above is the&nbsp; SQLConnection. </P>
<P><BR>============BUT============ <BR>The MSDN told us the "we should call the <BR>SQLConnection.IDbConnection.CreateCommand() in the client program". <BR>that is, "we should use the SQLConnection.CreateCommand() in the client <BR>program." <BR>THIS will result a foolish result. In the client pogram, we will write <BR>such a code: <BR>///------------ <BR>&nbsp;&nbsp; void doSomething() { <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SqlConnection&nbsp; conn = new SqlConnection(connectionString); <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SqlCommand command = conn.CreateCommand(); <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //&nbsp; We MUST have call <BR>SqlConnection.CreateCommand(), <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //&nbsp; not <BR>SqlConnection.IDbConnection.CreateCommand(), <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //&nbsp; which is suggested by MSDN. <BR>&nbsp;&nbsp; } <BR>///------------ <BR>The above client code has violated the programming rule: "PROGRAMMING <BR>TO INTERFACE". <BR>According to the MSDN, we can not "PROGRAM TO IDbCommand". <BR>The action of&nbsp; "SqlConnection.CreateCommand()"&nbsp; is difference with&nbsp; the <BR>action of"IDbConnection.CreeateCommand()". </P>
<P><BR>Now if i want to shift to the Oracle database.&nbsp;&nbsp; All of the client code <BR>must be modified, because the above is "PROGRAM TO SqlCommand".&nbsp; :-( <BR>What a foolish ADO.NET FrameWork. </P>
<P><BR>I am a java programmer. If i design the SQLConnection, i will give a <BR>such a implemention: <BR>///-------- <BR>&nbsp;&nbsp; SQLConnection:IDbConnection { <BR>&nbsp;&nbsp; .... <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; IDBCommand&nbsp; CreateCommand() { <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ........ <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } </P>
<P><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SqlCommand&nbsp; CreateSqlCommand() { <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ........ <BR>&nbsp;&nbsp;&nbsp;&nbsp; } <BR>&nbsp;&nbsp; .... <BR>&nbsp;} <BR>///--------- <BR>If the client programmer want to "PROGRAM TO SqlCommand",&nbsp; he will call <BR>the method of SqlConnection.CreateSqlCommand().&nbsp; If the client <BR>programmer want to "PROGRAMM TO IDbConnection", he will call the <BR>SqlConnection.CreateCommand(). Now The action of <BR>"SqlConnection.CreateCommand()"&nbsp; is same as&nbsp;&nbsp; the action <BR>of"IDbConnection.CreeateCommand()". <BR>Because the client code is "PROGRAM TO IDbCommand", so if the client <BR>program shifts to Oracle database, the client code will not have to <BR>modify all the code. </P>
<P><BR>回复 <BR>&nbsp;<BR></P><img src ="http://www.blogjava.net/jinfeng_wang/aggbug/28985.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jinfeng_wang/" target="_blank">jinfeng_wang</a> 2006-01-23 09:35 <a href="http://www.blogjava.net/jinfeng_wang/archive/2006/01/23/28985.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>EBCDIC Encoding with .NET (zz)</title><link>http://www.blogjava.net/jinfeng_wang/archive/2006/01/18/28559.html</link><dc:creator>jinfeng_wang</dc:creator><author>jinfeng_wang</author><pubDate>Wed, 18 Jan 2006 11:58:00 GMT</pubDate><guid>http://www.blogjava.net/jinfeng_wang/archive/2006/01/18/28559.html</guid><wfw:comment>http://www.blogjava.net/jinfeng_wang/comments/28559.html</wfw:comment><comments>http://www.blogjava.net/jinfeng_wang/archive/2006/01/18/28559.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jinfeng_wang/comments/commentRss/28559.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jinfeng_wang/services/trackbacks/28559.html</trackback:ping><description><![CDATA[<H1>EBCDIC Encoding with .NET</H1>
<P>After reading a post on the C# newsgroup asking for a EBCDIC to ASCII converter, and seeing one solution, I decided to write my own implementation. This page describes the implementation and its limitations, and a bit about EBCDIC itself. </P>
<H2>EBCDIC</H2>
<P>Unfortunately it appears to be fairly tricky to get hold of many concrete specifications of EBCDIC. This is what I've managed to glean from various websites: </P>
<UL>
<LI>Introduced by IBM, EBCDIC is an encoding mostly used on mainframes. 
<LI>Like "OEM", EBCDIC isn't a single character encoding: there are many EBCDIC encodings, suited to different cultures. 
<LI>It is primarily a single-byte encoding, ie each character is encoded as a single byte. However, there are two characters, "shift out" and "shift in" (0x0e and 0x0f respectively) which are used to change between this an a double-byte character set (DBCS). As far as I can tell, a single EBCDIC encoding doesn't specify which DBCS is to be used - in other words, you really need even more information before you can tell what's going on. Presumably the DBCS in question can't have any pairs beginning with byte 0x0f, as otherwise it would be confused with the "shift in" flag. </LI></UL>
<P>If you have any more information, particularly about the DBCS aspect, please mail me at <A href="mailto:skeet@pobox.com">skeet@pobox.com</A>. </P>
<H2>My EBCDIC Encoding implementation</H2>
<P>I managed to get hold of details of 47 EBCDIC encodings from <A href="http://std.dkuug.dk/i18n/charmaps/">http://std.dkuug.dk/i18n/charmaps/</A>. To be honest, I don't really know what DKUUG is, so I'm really just hoping that the maps are accurate - they seem to be quite reasonable though. Each encoding has a name and several have aliases, although I currently ignore this aliasing. </P>
<P>My implementation consists of three projects, described below, of which only the middle one is of any interest to most people. 
<DL>
<DT>A character map reader 
<DD>This simply finds all of the files whose names begin with "EBCDIC-" in the current directory, reads them all in (warning of any oddities in the encoding, such as any non-zero byte having two distinct meanings) and writes a resource file out, <CODE>ebcdic.dat</CODE>. This is a console applicion built from a single C# source file. 
<DT>An encoding library 
<DD>This is a library built from two C# source files and the <CODE>ebcdic.dat</CODE> file generated by the reader. This library is all most users will need. More details are provided below. 
<DT>A test program 
<DD>This is a console application built from a single C# source file and requiring the library described above. Currently it just displays the encoded version of "hello" and then decodes it. </DD></DL>
<P></P>
<H2>Using The Encoding Library</H2>
<P>The encoding library is very simple to use, as the encoding class (<CODE>JonSkeet.Ebcdic.EbcdicEncoding</CODE>) is a subclass of the standard .NET <CODE>System.Text.Encoding</CODE> class. To obtain an instance of the appropriate encoding, use <CODE>EbcdicEncoding.GetEncoding (String)</CODE> passing it the name of the encoding you wish to use (eg <CODE>EBCDIC-US</CODE>). You can find out the list of names of available encodings using the <CODE>EbcdicEncoding.AllNames</CODE> property, which returns the names as an array of strings. </P>
<P>Once you have obtained an <CODE>EbcdicEncoding</CODE> instance, use it like any other <CODE>Encoding</CODE>: call <CODE>GetString</CODE>, <CODE>GetBytes</CODE> etc. The encoding does not save any state between requests, and can safely be used by many threads simultaneously. There is no need (or indeed facility) to release encoding resources when it is no longer needed. All encodings are created on the first use of the <CODE>EbcdicEncoding</CODE> class, and maintained until the application domain is unloaded. </P>
<H2>Sample Code</H2>
<P>The following is a sample program to convert a file from EBCDIC-US to ASCII. It should be easy to see how to modify it to convert the other way, or to use a different encoding (eg from EBCDIC-UK, or to UTF-8). </P>
<TABLE class=code>
<TBODY>
<TR>
<TD><PRE><SPAN class=Namespace>using</SPAN> System;
<SPAN class=Namespace>using</SPAN> System.IO;
<SPAN class=Namespace>using</SPAN> System.Text;
<SPAN class=Namespace>using</SPAN> JonSkeet.Ebcdic;

<SPAN class=Modifier>public</SPAN> <SPAN class=ReferenceType>class</SPAN> ConvertFile
{
    <SPAN class=Modifier>public</SPAN> <SPAN class=Modifier>static</SPAN> <SPAN class=ValueType>void</SPAN> Main(<SPAN class=ReferenceType>string</SPAN>[] args)
    {
        <SPAN class=Statement>if</SPAN> (args.Length != 2)
        {
            Console.WriteLine 
                (<SPAN class=String>"Usage: ConvertFile &lt;ebcdic file (input)&gt; &lt;ascii file (output)&gt;"</SPAN>);
            <SPAN class=Statement>return</SPAN>;
        }
        
        <SPAN class=ReferenceType>string</SPAN> inputFile = args[0];
        <SPAN class=ReferenceType>string</SPAN> outputFile = args[1];

        Encoding inputEncoding = EbcdicEncoding.GetEncoding (<SPAN class=String>"EBCDIC-US"</SPAN>);
        Encoding outputEncoding = Encoding.ASCII;
        
        <SPAN class=Statement>try</SPAN>
        {
            <SPAN class=InlineComment>// Create the reader and writer with appropriate encodings.</SPAN>
            <SPAN class=Namespace>using</SPAN> (StreamReader inputReader = 
                      <SPAN class=Keyword>new</SPAN> StreamReader (inputFile, inputEncoding))
            {
                <SPAN class=Namespace>using</SPAN> (StreamWriter outputWriter = 
                           <SPAN class=Keyword>new</SPAN> StreamWriter (outputFile, <SPAN class=Keyword>false</SPAN>, outputEncoding))
                {
                    <SPAN class=InlineComment>// Create an 8K-char buffer</SPAN>
                    <SPAN class=ValueType>char</SPAN>[] buffer = <SPAN class=Keyword>new</SPAN> <SPAN class=ValueType>char</SPAN>[8192];
                    <SPAN class=ValueType>int</SPAN> len=0;
                    
                    <SPAN class=InlineComment>// Repeatedly read into the buffer and then write it out</SPAN>
                    <SPAN class=InlineComment>// until the reader has been exhausted.</SPAN>
                    <SPAN class=Statement>while</SPAN> ( (len=inputReader.Read (buffer, 0, buffer.Length)) &gt; 0)
                    {
                        outputWriter.Write (buffer, 0, len);
                    }
                }
            }
        }
        <SPAN class=InlineComment>// Not much in the way of error handling here - you may well want</SPAN>
        <SPAN class=InlineComment>// to do better handling yourself!</SPAN>
        <SPAN class=Statement>catch</SPAN> (IOException e)
        {
            Console.WriteLine (<SPAN class=String>"Exception during processing: {0}"</SPAN>, e.Message);
        }
    }
}    
</PRE></TD></TR></TBODY></TABLE>
<H2>Limitations</H2>
<P>Due to the lack of available information about the DBCS aspect of EBCDIC, this encoding class makes no effort whatsoever to simulate proper shifting. Shift out and shift in are merely encoded/decoded to/from their equivalent Unicode characters, and bytes between them are treated as if the shift had not taken place. (This means that a decoded byte array is always a string of the same length as the byte array, and vice versa). </P>
<P>Any byte not recognised to be from the specific encoding being used is decoded to the question mark character, '?'. Any character not recognised to be in the set of characters encoded by the specific encoding being used is encoded to the byte representing the question mark character, or to byte zero if the question mark character is not in the character set either. </P>
<P>The library doesn't currently have a strong-name, so can't be placed in the GAC. You may, however, download the source and modify </P>
<H2>Licence</H2>
<P>This was just an interesting half-day project. I have no desire to make any money out of this code whatsoever, but I hope it's interesting and useful to others. So, feel free to use it. If you have any questions about it, or just find it useful and wish to let me know, please mail me at <A href="mailto:skeet@pobox.com">skeet@pobox.com</A>. You may use this code in commercial projects, either in binary or source form. You may change the namespace and the class names to suit your company, and modify the code if you wish. I'd rather you didn't try to pass it off as your own work, and specifically you may not sell just this code - at least not without asking me first. I make no claims whatsoever about this code - it comes with no warranty, not even the implied warranty of fitness for purpose, so don't sue me if it breaks something. (Mail me instead, so we can try to stop it from happening again.) </P>
<H2>Downloads</H2>
<UL>
<LI><A href="http://www.yoda.arachsys.com/csharp/ebcdic/source.zip">Source code and VS.NET 2003 solution</A> (zip file, 20K, also contains instructions for building from the command line. 
<LI><A href="http://www.yoda.arachsys.com/csharp/ebcdic/debug.zip">Debug version of the encoding library</A> as built by VS.NET 2003 (zip file, 11K, contains the DLL and PDB files) 
<LI><A href="http://www.yoda.arachsys.com/csharp/ebcdic/release.zip">Release version of the encoding library</A> as built by VS.NET 2003 (zip file, 6K, contains just the DLL) </LI></UL>
<H2>History </H2>
<UL>
<LI>August 31st 2003, v1.0.0.1 - no in-code changes, just made the XML documentation build correctly. 
<LI>August 28th 2003, v1.0.0.1 - slight tweaking to remove unnecessary (and probably counterproductive) efficiency measure. No functional changes. 
<LI>May 21st 2003, v1.0.0.0 - initial implementation. </LI></UL><img src ="http://www.blogjava.net/jinfeng_wang/aggbug/28559.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jinfeng_wang/" target="_blank">jinfeng_wang</a> 2006-01-18 19:58 <a href="http://www.blogjava.net/jinfeng_wang/archive/2006/01/18/28559.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Code-Page Identifiers(zz from msdn)</title><link>http://www.blogjava.net/jinfeng_wang/archive/2006/01/18/28540.html</link><dc:creator>jinfeng_wang</dc:creator><author>jinfeng_wang</author><pubDate>Wed, 18 Jan 2006 09:59:00 GMT</pubDate><guid>http://www.blogjava.net/jinfeng_wang/archive/2006/01/18/28540.html</guid><wfw:comment>http://www.blogjava.net/jinfeng_wang/comments/28540.html</wfw:comment><comments>http://www.blogjava.net/jinfeng_wang/archive/2006/01/18/28540.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jinfeng_wang/comments/commentRss/28540.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jinfeng_wang/services/trackbacks/28540.html</trackback:ping><description><![CDATA[<H3><A name=_win32_code_page_identifiers></A>Code-Page Identifiers</H3>
<TABLE class=data>
<TBODY>
<TR vAlign=top>
<TH class=data align=left width="17%">Identifier</TH>
<TH class=data align=left width="83%">Name</TH></TR>
<TR vAlign=top>
<TD class=data width="17%">037</TD>
<TD class=data width="83%">IBM EBCDIC - U.S./Canada</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">437</TD>
<TD class=data width="83%">OEM - United States</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">500</TD>
<TD class=data width="83%">IBM EBCDIC - International </TD></TR>
<TR vAlign=top>
<TD class=data width="17%">708</TD>
<TD class=data width="83%">Arabic - ASMO 708</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">709</TD>
<TD class=data width="83%">Arabic - ASMO 449+, BCON V4</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">710</TD>
<TD class=data width="83%">Arabic - Transparent Arabic</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">720</TD>
<TD class=data width="83%">Arabic - Transparent ASMO</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">737</TD>
<TD class=data width="83%">OEM - Greek (formerly 437G)</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">775</TD>
<TD class=data width="83%">OEM - Baltic</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">850</TD>
<TD class=data width="83%">OEM - Multilingual Latin I</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">852</TD>
<TD class=data width="83%">OEM - Latin II</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">855</TD>
<TD class=data width="83%">OEM - Cyrillic (primarily Russian)</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">857</TD>
<TD class=data width="83%">OEM - Turkish</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">858</TD>
<TD class=data width="83%">OEM - Multilingual Latin I + Euro symbol</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">860</TD>
<TD class=data width="83%">OEM - Portuguese</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">861</TD>
<TD class=data width="83%">OEM - Icelandic</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">862</TD>
<TD class=data width="83%">OEM - Hebrew</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">863</TD>
<TD class=data width="83%">OEM - Canadian-French</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">864</TD>
<TD class=data width="83%">OEM - Arabic</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">865</TD>
<TD class=data width="83%">OEM - Nordic</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">866</TD>
<TD class=data width="83%">OEM - Russian</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">869</TD>
<TD class=data width="83%">OEM - Modern Greek</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">870</TD>
<TD class=data width="83%">IBM EBCDIC - Multilingual/ROECE (Latin-2)</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">874</TD>
<TD class=data width="83%">ANSI/OEM - Thai (same as 28605, ISO 8859-15)</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">875</TD>
<TD class=data width="83%">IBM EBCDIC - Modern Greek</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">932</TD>
<TD class=data width="83%">ANSI/OEM - Japanese, Shift-JIS</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">936</TD>
<TD class=data width="83%">ANSI/OEM - Simplified Chinese (PRC, Singapore)</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">949</TD>
<TD class=data width="83%">ANSI/OEM - Korean (Unified Hangul Code)</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">950</TD>
<TD class=data width="83%">ANSI/OEM - Traditional Chinese (Taiwan; Hong Kong SAR, PRC) </TD></TR>
<TR vAlign=top>
<TD class=data width="17%">1026</TD>
<TD class=data width="83%">IBM EBCDIC - Turkish (Latin-5)</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">1047</TD>
<TD class=data width="83%">IBM EBCDIC - Latin 1/Open System</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">1140</TD>
<TD class=data width="83%">IBM EBCDIC - U.S./Canada (037 + Euro symbol)</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">1141</TD>
<TD class=data width="83%">IBM EBCDIC - Germany (20273 + Euro symbol)</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">1142</TD>
<TD class=data width="83%">IBM EBCDIC - Denmark/Norway (20277 + Euro symbol)</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">1143</TD>
<TD class=data width="83%">IBM EBCDIC - Finland/Sweden (20278 + Euro symbol)</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">1144</TD>
<TD class=data width="83%">IBM EBCDIC - Italy (20280 + Euro symbol)</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">1145</TD>
<TD class=data width="83%">IBM EBCDIC - Latin America/Spain (20284 + Euro symbol)</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">1146</TD>
<TD class=data width="83%">IBM EBCDIC - United Kingdom (20285 + Euro symbol)</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">1147</TD>
<TD class=data width="83%">IBM EBCDIC - France (20297 + Euro symbol)</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">1148</TD>
<TD class=data width="83%">IBM EBCDIC - International (500 + Euro symbol)</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">1149</TD>
<TD class=data width="83%">IBM EBCDIC - Icelandic (20871 + Euro symbol)</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">1200</TD>
<TD class=data width="83%">Unicode UCS-2 Little-Endian (BMP of ISO 10646)</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">1201</TD>
<TD class=data width="83%">Unicode UCS-2 Big-Endian </TD></TR>
<TR vAlign=top>
<TD class=data width="17%">1250</TD>
<TD class=data width="83%">ANSI - Central European </TD></TR>
<TR vAlign=top>
<TD class=data width="17%">1251</TD>
<TD class=data width="83%">ANSI - Cyrillic</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">1252</TD>
<TD class=data width="83%">ANSI - Latin I </TD></TR>
<TR vAlign=top>
<TD class=data width="17%">1253</TD>
<TD class=data width="83%">ANSI - Greek</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">1254</TD>
<TD class=data width="83%">ANSI - Turkish</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">1255</TD>
<TD class=data width="83%">ANSI - Hebrew</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">1256</TD>
<TD class=data width="83%">ANSI - Arabic</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">1257</TD>
<TD class=data width="83%">ANSI - Baltic</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">1258</TD>
<TD class=data width="83%">ANSI/OEM - Vietnamese</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">1361</TD>
<TD class=data width="83%">Korean (Johab)</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">10000</TD>
<TD class=data width="83%">MAC - Roman</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">10001</TD>
<TD class=data width="83%">MAC - Japanese</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">10002</TD>
<TD class=data width="83%">MAC - Traditional Chinese (Big5)</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">10003</TD>
<TD class=data width="83%">MAC - Korean</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">10004</TD>
<TD class=data width="83%">MAC - Arabic</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">10005</TD>
<TD class=data width="83%">MAC - Hebrew</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">10006</TD>
<TD class=data width="83%">MAC - Greek I</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">10007</TD>
<TD class=data width="83%">MAC - Cyrillic</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">10008</TD>
<TD class=data width="83%">MAC - Simplified Chinese (GB 2312)</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">10010</TD>
<TD class=data width="83%">MAC - Romania</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">10017</TD>
<TD class=data width="83%">MAC - Ukraine</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">10021</TD>
<TD class=data width="83%">MAC - Thai</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">10029</TD>
<TD class=data width="83%">MAC - Latin II</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">10079</TD>
<TD class=data width="83%">MAC - Icelandic</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">10081</TD>
<TD class=data width="83%">MAC - Turkish</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">10082</TD>
<TD class=data width="83%">MAC - Croatia</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">12000</TD>
<TD class=data width="83%">Unicode UCS-4 Little-Endian</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">12001</TD>
<TD class=data width="83%">Unicode UCS-4 Big-Endian</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">20000</TD>
<TD class=data width="83%">CNS - Taiwan </TD></TR>
<TR vAlign=top>
<TD class=data width="17%">20001</TD>
<TD class=data width="83%">TCA - Taiwan </TD></TR>
<TR vAlign=top>
<TD class=data width="17%">20002</TD>
<TD class=data width="83%">Eten - Taiwan </TD></TR>
<TR vAlign=top>
<TD class=data width="17%">20003</TD>
<TD class=data width="83%">IBM5550 - Taiwan </TD></TR>
<TR vAlign=top>
<TD class=data width="17%">20004</TD>
<TD class=data width="83%">TeleText - Taiwan </TD></TR>
<TR vAlign=top>
<TD class=data width="17%">20005</TD>
<TD class=data width="83%">Wang - Taiwan </TD></TR>
<TR vAlign=top>
<TD class=data width="17%">20105</TD>
<TD class=data width="83%">IA5 IRV International Alphabet No. 5 (7-bit)</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">20106</TD>
<TD class=data width="83%">IA5 German (7-bit)</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">20107</TD>
<TD class=data width="83%">IA5 Swedish (7-bit)</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">20108</TD>
<TD class=data width="83%">IA5 Norwegian (7-bit)</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">20127</TD>
<TD class=data width="83%">US-ASCII (7-bit)</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">20261</TD>
<TD class=data width="83%">T.61</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">20269</TD>
<TD class=data width="83%">ISO 6937 Non-Spacing Accent</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">20273</TD>
<TD class=data width="83%">IBM EBCDIC - Germany</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">20277</TD>
<TD class=data width="83%">IBM EBCDIC - Denmark/Norway</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">20278</TD>
<TD class=data width="83%">IBM EBCDIC - Finland/Sweden</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">20280</TD>
<TD class=data width="83%">IBM EBCDIC - Italy</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">20284</TD>
<TD class=data width="83%">IBM EBCDIC - Latin America/Spain</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">20285</TD>
<TD class=data width="83%">IBM EBCDIC - United Kingdom</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">20290</TD>
<TD class=data width="83%">IBM EBCDIC - Japanese Katakana Extended</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">20297</TD>
<TD class=data width="83%">IBM EBCDIC - France</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">20420</TD>
<TD class=data width="83%">IBM EBCDIC - Arabic</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">20423</TD>
<TD class=data width="83%">IBM EBCDIC - Greek</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">20424</TD>
<TD class=data width="83%">IBM EBCDIC - Hebrew</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">20833</TD>
<TD class=data width="83%">IBM EBCDIC - Korean Extended</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">20838</TD>
<TD class=data width="83%">IBM EBCDIC - Thai</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">20866</TD>
<TD class=data width="83%">Russian - KOI8-R</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">20871</TD>
<TD class=data width="83%">IBM EBCDIC - Icelandic</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">20880</TD>
<TD class=data width="83%">IBM EBCDIC - Cyrillic (Russian)</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">20905</TD>
<TD class=data width="83%">IBM EBCDIC - Turkish</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">20924</TD>
<TD class=data width="83%">IBM EBCDIC - Latin-1/Open System (1047 + Euro symbol)</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">20932</TD>
<TD class=data width="83%">JIS X 0208-1990 &amp; 0121-1990</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">20936</TD>
<TD class=data width="83%">Simplified Chinese (GB2312)</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">21025</TD>
<TD class=data width="83%">IBM EBCDIC - Cyrillic (Serbian, Bulgarian)</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">21027</TD>
<TD class=data width="83%">(deprecated)</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">21866</TD>
<TD class=data width="83%">Ukrainian (KOI8-U)</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">28591</TD>
<TD class=data width="83%">ISO 8859-1 Latin I</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">28592</TD>
<TD class=data width="83%">ISO 8859-2 Central Europe</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">28593</TD>
<TD class=data width="83%">ISO 8859-3 Latin 3 </TD></TR>
<TR vAlign=top>
<TD class=data width="17%">28594</TD>
<TD class=data width="83%">ISO 8859-4 Baltic</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">28595</TD>
<TD class=data width="83%">ISO 8859-5 Cyrillic</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">28596</TD>
<TD class=data width="83%">ISO 8859-6 Arabic</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">28597</TD>
<TD class=data width="83%">ISO 8859-7 Greek</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">28598</TD>
<TD class=data width="83%">ISO 8859-8 Hebrew</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">28599</TD>
<TD class=data width="83%">ISO 8859-9 Latin 5</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">28605</TD>
<TD class=data width="83%">ISO 8859-15 Latin 9</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">29001</TD>
<TD class=data width="83%">Europa 3</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">38598</TD>
<TD class=data width="83%">ISO 8859-8 Hebrew</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">50220</TD>
<TD class=data width="83%">ISO 2022 Japanese with no halfwidth Katakana</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">50221</TD>
<TD class=data width="83%">ISO 2022 Japanese with halfwidth Katakana</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">50222</TD>
<TD class=data width="83%">ISO 2022 Japanese JIS X 0201-1989</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">50225</TD>
<TD class=data width="83%">ISO 2022 Korean </TD></TR>
<TR vAlign=top>
<TD class=data width="17%">50227</TD>
<TD class=data width="83%">ISO 2022 Simplified Chinese</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">50229</TD>
<TD class=data width="83%">ISO 2022 Traditional Chinese</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">50930</TD>
<TD class=data width="83%">Japanese (Katakana) Extended</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">50931</TD>
<TD class=data width="83%">US/Canada and Japanese</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">50933</TD>
<TD class=data width="83%">Korean Extended and Korean</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">50935</TD>
<TD class=data width="83%">Simplified Chinese Extended and Simplified Chinese</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">50936</TD>
<TD class=data width="83%">Simplified Chinese</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">50937</TD>
<TD class=data width="83%">US/Canada and Traditional Chinese</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">50939</TD>
<TD class=data width="83%">Japanese (Latin) Extended and Japanese</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">51932</TD>
<TD class=data width="83%">EUC - Japanese</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">51936</TD>
<TD class=data width="83%">EUC - Simplified Chinese</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">51949</TD>
<TD class=data width="83%">EUC - Korean</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">51950</TD>
<TD class=data width="83%">EUC - Traditional Chinese</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">52936</TD>
<TD class=data width="83%">HZ-GB2312 Simplified Chinese </TD></TR>
<TR vAlign=top>
<TD class=data width="17%">54936</TD>
<TD class=data width="83%"><B>Windows XP:</B> GB18030 Simplified Chinese (4 Byte) </TD></TR>
<TR vAlign=top>
<TD class=data width="17%">57002</TD>
<TD class=data width="83%">ISCII Devanagari</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">57003</TD>
<TD class=data width="83%">ISCII Bengali</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">57004</TD>
<TD class=data width="83%">ISCII Tamil</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">57005</TD>
<TD class=data width="83%">ISCII Telugu</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">57006</TD>
<TD class=data width="83%">ISCII Assamese</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">57007</TD>
<TD class=data width="83%">ISCII Oriya</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">57008</TD>
<TD class=data width="83%">ISCII Kannada</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">57009</TD>
<TD class=data width="83%">ISCII Malayalam</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">57010</TD>
<TD class=data width="83%">ISCII Gujarati</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">57011</TD>
<TD class=data width="83%">ISCII Punjabi</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">65000</TD>
<TD class=data width="83%">Unicode UTF-7</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">65001</TD>
<TD class=data width="83%">Unicode UTF-8</TD></TR></TBODY></TABLE><img src ="http://www.blogjava.net/jinfeng_wang/aggbug/28540.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jinfeng_wang/" target="_blank">jinfeng_wang</a> 2006-01-18 17:59 <a href="http://www.blogjava.net/jinfeng_wang/archive/2006/01/18/28540.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Table Mapping in ADO.NET (zz from msdn)</title><link>http://www.blogjava.net/jinfeng_wang/archive/2006/01/10/27480.html</link><dc:creator>jinfeng_wang</dc:creator><author>jinfeng_wang</author><pubDate>Tue, 10 Jan 2006 14:27:00 GMT</pubDate><guid>http://www.blogjava.net/jinfeng_wang/archive/2006/01/10/27480.html</guid><wfw:comment>http://www.blogjava.net/jinfeng_wang/comments/27480.html</wfw:comment><comments>http://www.blogjava.net/jinfeng_wang/archive/2006/01/10/27480.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jinfeng_wang/comments/commentRss/27480.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jinfeng_wang/services/trackbacks/27480.html</trackback:ping><description><![CDATA[<DIV id=nsbanner>
<DIV id=TitleRow>
<H1 class=dtH1><A name=data03142002></A>Table Mapping in ADO.NET</H1></DIV></DIV><!--NONSCROLLING BANNER END-->
<P>Dino Esposito<BR>Wintellect</P>
<P>March 15, 2002</P>
<P><I>Table mapping</I> is the process that controls how data adapters copy tables and columns of data from a physical data source to ADO.NET in-memory objects. A data adapter object utilizes the <B>Fill</B> method to populate a <B>DataSet</B> or a <B>DataTable</B> object with data retrieved by a <B>SELECT</B> command. Internally, the <B>Fill</B> method makes use of a data reader to get to the data and the metadata that describe the structure and content of the source tables. The data read is then copied into ad hoc memory containers (that is, the DataTable). The <I>table mapping mechanism</I> is the set of rules and parameters that lets you control how the SQL result sets are mapped onto in-memory objects. </P>
<P>The following code shows the typical way to collect data out of a data source using a data adapter:</P><PRE class=code>SqlDataAdapter da;
DataSet ds;
da = new SqlDataAdapter(m_selectCommand, m_connectionString);
ds = new DataSet();
da.Fill(ds); 
</PRE>
<P>Admittedly, this code isn't exactly rocket science and I can venture a guess that you are already familiar with it, and can successfully run it more than once. But what really happens behind the scenes of this code? Believe it or not, there's a little known object running behind the curtain whose nature and behavior heavily affect the final results. </P>
<P>When you run the code shown above, a new <B>DataTable</B> object is added to the (initially empty) DataSet for each result set that the execution of the <B>SELECT</B> statement may have generated. If you pass a non-empty DataSet to the <B>Fill</B> method, the contents of the result sets and the existing <B>DataTable</B> objects are merged as long as a match is found with the name of the DataTable. Similarly, when it comes to copying rows of data from the result set to a given DataTable, the contents of matching columns are merged. By contrast, if no match is found on the column name, then a new <B>DataColumn</B> object is created (with default settings) and added to the in-memory DataTable.</P>
<P>The question is, how does the adapter map the contents of the result sets into the DataSet constituent items? What tells the data adapter which tables and columns names to match? The <B>TableMappings</B> property of data adapters is the object behind the curtain that decides how tables in the result set map to the objects in the DataSet.</P>
<H2 class=dtH1>The Mapping Mechanism</H2>
<P>The mapping mechanism begins to work once the <B>SELECT</B> command is terminated and the data adapter has returned one or more result sets. The adapter gets a reference to an internal data reader object and starts processing the fetched data. By default, the data reader is positioned on the first result set. The following pseudocode describes what's going on:</P><PRE class=code>int Fill(DataSet ds)
{
   // Execute the SELECT command and gets a reader
   IDataReader dr = SelectCommand.ExecuteReader();
   
   // Map the first result set to the DataSet and return the table
   bool bMoreToRead, bMoreResults;
   DataTable dt = MapCurrentResultSet(ds);
   
   // Copy rows from the result set to the specified DataTable
   while(true)
   {
   // Move to the next data row
   bMoreToRead = dr.Read();
   if (!bMoreToRead)
   {
      // No more rows in this result set. More result sets?
      bMoreResults = dr.NextResult()   
      if (!bMoreResults)
         break;
      else
         // Map this new result set and continue the loop
         dt = MapCurrentResultSet(ds);
   }
   else
   AddRowToDataTable(dt)
   }
}
</PRE>
<P>The <B>Fill</B> method maps the first result set to a <B>DataTable</B> object in the given DataSet. Next, it loops through the result set and adds rows of data to the DataTable. When the end of the result set is reached, the method looks for a new result set and repeats the operation.</P>
<P>Mapping a result set to a DataSet is a process that comprises two phases: 
<UL type=disc>
<LI><B>Table mapping</B> 
<LI><B>Column mapping</B> </LI></UL>
<P>During the table mapping step, the data adapter has to find a name for the DataTable that will contain the rows in the result set being processed. </P>
<P>Each result set is given a default name that you might want to change at will. The default name of the result set depends on the signature of the <B>Fill</B> method that has been used for the call. For example, let's consider the two overloads below:</P><PRE class=code>Fill(ds);
Fill(ds, "MyTable");
</PRE>
<P>In the former case, the name of the first result set defaults to <B>Table</B>. Further result sets are named <B>Table1</B>, <B>Table2</B>, and so on. In the latter case, the first result set is called <B>MyTable</B> and the others are named after it—<B>MyTable1</B>, <B>MyTable2</B>, and so forth.</P>
<P>The adapter looks up its <B>TableMappings</B> collection for an entry that matches the default name of the result set. If a match is found, the adapter attempts to locate a DataTable object with the name specified in the mapping in the DataSet. If no such <B>DataTable</B> object exists, it is created and then filled. If such a DataTable exists in the DataSet, its contents are merged with the contents of the result set. </P>
<P class=fig><IMG alt="" src="http://msdn.microsoft.com/library/en-us/dndive/html/data03142002-fig01.gif" border=0></P>
<P class=label><B>Figure 1. Mapping a result set onto a DataSet object</B></P>
<P>In Figure 1, I assume that the query produces at least three result sets. The <B>TableMappings</B> collection contains three default names and the corresponding mapping names. If the <B>SELECT</B> command creates a result set with a default name of <B>Table</B>, then its contents go into a new or existing <B>DataTable</B> object called <B>Employees</B>. How do you control this from a code standpoint? Look at the code snippet below:</P><PRE class=code>SqlDataAdapter da = new SqlDataAdapter(...);
DataSet ds = new DataSet();
DataTableMapping dtm1, dtm2, dtm3;
dtm1 = da.TableMappings.Add("Table", "Employees"); 
dtm2 = da.TableMappings.Add("Table1", "Products");
dtm3 = da.TableMappings.Add("Table2", "Orders");
da.Fill(ds);
</PRE>
<P>Of course, the default names you map onto your own names must coincide with the default names originated by the call to the <B>Fill </B>method. In other words, if you change the last line to <CODE class=ce>da.Fill(ds, "MyTable");</CODE>, the code won't work any longer because the default names are now <I>MyTable</I>, <I>MyTable1</I>, and <I>MyTable2</I> for which the above <B>TableMappings</B> collection has no entries.</P>
<P>You can have any number of table mappings that are not necessarily related to the expected number of result sets. For example, you can map only Table1, being the second result set returned by the command. In this case, the destination DataSet will hold three tables named <I>Table</I>, <I>Products</I>, and <I>Table2</I>.</P>
<P>The <B>DataTableMapping</B> object describes a mapped relationship between a result set and a <B>DataTable</B> object in a DataSet. The <B>SourceTable</B> property returns the default result set name, whereas <B>DataSetTable</B> contains the mapping name.</P>
<P>If you use Visual Studio® .NET, you can configure the table mappings in a visual manner by running the Data Adapter Configuration Wizard.</P>
<H2 class=dtH1>Column Mapping</H2>
<P>If table mapping ended here, then it wouldn't be such a big deal. In fact, if your goal is to give a mnemonic name to your DataSet tables, you can use the following code:</P><PRE class=code>SqlDataAdapter da = new SqlDataAdapter(...);
DataSet ds = new DataSet();
da.Fill(ds);
ds.Tables["Table"].TableName = "Employees";
ds.Tables["Table1"].TableName = "Products";
</PRE>
<P>The final effect is exactly the same. The mapping mechanism, though, has another, rather interesting facet—column mapping. The figure below extends the previous diagram and includes details of the column mapping.</P>
<P class=fig><IMG alt="" src="http://msdn.microsoft.com/library/en-us/dndive/html/data03142002-fig02.gif" border=0></P>
<P class=label><B>Figure 2. Table and column mappings</B></P>
<P>The <B>DataTableMapping</B> object has a property called <B>ColumnMappings</B> that turns out to be a collection of <B>DataColumnMapping</B> objects. A column mapping represents a mapping between the name of a column in the result set and the name of the corresponding column in the <B>DataTable</B> object. Basically, the ultimate goal of <B>DataColumnMapping</B> object is that it enables you to use column names in a DataTable that are different from those in the data source. </P><PRE class=code>SqlDataAdapter da = new SqlDataAdapter(...);
DataSet ds = new DataSet();
DataTableMapping dtm1;
dtm1 = da.TableMappings.Add("Table", "Employees"); 
dtm1.ColumnMappings.Add("employeeid", "ID");
dtm1.ColumnMappings.Add("firstname", "Name");
dtm1.ColumnMappings.Add("lastname", "Surname");
da.Fill(ds);
</PRE>
<P>In the code above, I assume that the fetched result set has columns called <CODE class=ce>employeeid</CODE>, <CODE class=ce>firstname</CODE>, and <CODE class=ce>lastname</CODE>. These columns have to be copied into an in-memory DataTable child of a DataSet. By default, the target DataColumn will have the same name as the source column. The column mapping mechanism, though, allows you to change the name of the in-memory column. For example, when the column <CODE class=ce>employeeid</CODE> is copied to memory, it is renamed to <B>ID</B> and placed in a DataTable called Employees. </P>
<P>The name of the column is the only argument you can change at this level. Keep in mind that this entire mapping takes place automatically within the body of the <B>Fill</B> method. When <B>Fill</B> terminates and each column in the source result set has been transformed into a <B>DataColumn</B> object, you can intervene and apply further changes—relationships, constraints, primary key, read-only, auto-increment seed and step, support for null values, and more.</P>
<P>In summary, the <B>Fill</B> method accomplishes two main operations. First off, it maps the source result sets onto in-memory tables. Secondly, it fills the tables with the data fetched out of the physical data source. While accomplishing any of these tasks, the <B>Fill</B> method could raise some special exceptions. Conceptually, an exception is an anomalous situation that needs to be specifically addressed from a code standpoint. When the adapter can't find a table or a column mapping, and when a required DataTable or DataColumn can't be found in the target DataSet, the adapter throws a type of lightweight exception. </P>
<P>Unlike real exceptions that must necessarily be resolved in code, this special breed of adapter exceptions have to be resolved declaratively by choosing an action from a small set of feasible options. Adapters raise two types of lightweight exceptions: 
<UL type=disc>
<LI>Missing mapping 
<LI>Missing schema </LI></UL>
<H2 class=dtH1>Missing Mapping Action</H2>
<P>A missing mapping action is required in two circumstances when the adapter is collecting data to fill the DataSet. You need a missing mapping action if a default name is not found in the <B>TableMappings</B>, or if a column name is not available in the table's <B>ColumnMappings</B> collection. You must customize the behavior of the adapter's <B>MissingMappingAction</B> property in order to handle such an exception. Feasible values for the property come from the <B>MissingMappingAction</B> enum type listed in the table below.</P>
<TABLE class=data>
<TBODY>
<TR vAlign=top>
<TH class=data align=left width="17%">Value</TH>
<TH class=data align=left width="83%">Description</TH></TR>
<TR vAlign=top>
<TD class=data width="17%">Error</TD>
<TD class=data width="83%">A SystemException is generated whenever a missing column or a table is detected.</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">Ignore</TD>
<TD class=data width="83%">The unmapped column or table is ignored.</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">Passthrough</TD>
<TD class=data width="83%">Default option; add the missing table or column with the default name. </TD></TR></TBODY></TABLE>
<P class=label><B>Table 1. The MissingMappingAction enumeration</B></P>
<P>Unless you explicitly set the <B>MissingMappingAction</B> property prior to filling the adapter, it assumes a default value of <B>Passthrough</B>. As a result, the table or the column is added to the DataSet using the default name. For example, if no table mapping has been specified for the result set called <I>Table</I>, then the target DataTable takes the same name. In fact, the following statements end up adding a new DataTable to the DataSet called <I>Table</I> and <I>MyTable</I> respectively.</P><PRE class=code>da.Fill(ds);
da.Fill(ds, "MyTable");
</PRE>
<P>If you set the <B>MissingMappingAction</B> property to <B>Ignore</B>, then any unmapped table or column is simply ignored. No error is detected, but there will be no content for the incriminating result set (or one of its columns) in the target DataSet.</P>
<P>If the <B>MissingMappingAction</B> property is set to <B>Error</B>, then the adapter is limited to throw a <I>SystemException</I> exception whenever a missing mapping is detected.</P>
<P>Once the adapter is done with the mapping phase, it starts populating the target DataSet with the contents of the selected result sets. Any required <B>DataTable</B> or <B>DataColumn</B> object that is not available in the target DataSet triggers another lightweight exception and requires another declarative action:missing schema action.</P>
<H2 class=dtH1>Missing Schema Action</H2>
<P>A missing schema action is required if the DataSet does not contain a table with the name that has been determined during the table mapping step. Similarly, the same action is required if the DataSet table does not contain a column with the expected mapping name. <B>MissingSchemaAction</B> is the property that you set to indicate the action you want to be taken in case of an insufficient table schema. Feasible values for the property come from the <B>MissingSchemaAction</B> enum type, listed in the table below.</P>
<TABLE class=data>
<TBODY>
<TR vAlign=top>
<TH class=data align=left width="17%">Value</TH>
<TH class=data align=left width="83%">Description</TH></TR>
<TR vAlign=top>
<TD class=data width="17%">Error</TD>
<TD class=data width="83%">A SystemException is generated whenever a missing column or a table is detected.</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">Ignore</TD>
<TD class=data width="83%">The unmapped column or table is ignored.</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">Add</TD>
<TD class=data width="83%">Default option; complete the schema by adding any missing column or table.</TD></TR>
<TR vAlign=top>
<TD class=data width="17%">AddWithKey</TD>
<TD class=data width="83%">Adds primary key and constraints.</TD></TR></TBODY></TABLE>
<P class=label><B>Table 2. The MissingSchemaAction enumeration</B></P>
<P>By default, the <B>MissingSchemaAction</B> property is set to <B>Add</B>. As a result, the DataSet is completed by adding any constituent item that is missing—<B>DataTable</B> or <B>DataColumn</B>. Bear in mind, though, that the schema information added in this way is very limited. It only includes name and type. If you want extra information—like primary key, auto-increment, read-only and null settings—use the <B>AddWithKey</B> option instead. Notice that even if you use the <B>AddWithKey</B> option, not all available information about the column is loaded into the <B>DataColumn</B>. For example, <B>AddWithKey</B> marks a column as auto-increment, but does not set the related seed and step properties. Also, the default value for the source column, if any, is not automatically copied. The primary key is imported, but not any extra indexes you may have set.</P>
<P>The other two options, <B>Ignore</B> and <B>Error</B>, work exactly as they did with the <B>MissingMappingAction</B> property.</P>
<H2 class=dtH1>Impact on the Code</H2>
<P>Although I repeatedly talked about the actions in terms of (lightweight) exceptions, the actions you declare to execute in case of missing objects are not as expensive as true exceptions. On the other hand, this doesn't mean that your code is completely unaffected by such actions. More specifically, filling a DataSet that already contains all the needed schema information is a form of code optimization. This is especially true as long as your code is structured in such a way that you repeatedly fill an empty DataSet with a fixed schema. In this case, using a global <B>DataSet</B> object preloaded with schema information helps to prevent all those requests for recovery actions. </P>
<P>How can you fill a DataSet with the schema information that belongs to a group of result sets? Guess what, the data adapter objects have a tailor-made method—<B>FillSchema</B>.</P><PRE class=code>DataTable[] FillSchema(DataSet ds, SchemaType mappingMode);
</PRE>
<P><B>FillSchema</B> takes a DataSet and adds as many tables to it as needed by the <B>SELECT</B> command associated with the adapter. The method returns the various <B>DataTable</B> objects (only schema, no data) created in an array. The mapping mode parameter can be one of the values defined in the <B>SchemaType</B> enum.</P>
<TABLE class=data>
<TBODY>
<TR vAlign=top>
<TH class=data align=left width="12%">Value</TH>
<TH class=data align=left width="88%">Description</TH></TR>
<TR vAlign=top>
<TD class=data width="12%">Mapped</TD>
<TD class=data width="88%">Apply any existing table mappings to the incoming schema. Configure the DataSet with the transformed schema. Preferable option.</TD></TR>
<TR vAlign=top>
<TD class=data width="12%">Source</TD>
<TD class=data width="88%">Ignore any table mappings on the DataAdapter. Configure the DataSet using the incoming schema without applying any transformations.</TD></TR></TBODY></TABLE>
<P class=label><B>Table 3. The SchemaType enumeration</B></P>
<P>The options available are quite self-explanatory. <B>Mapped</B> describes what happens when mappings are defined. <B>Source</B>, instead, deliberately ignores any mappings you may have set. The tables in the DataSet retain their default name and all the columns maintain the original name they were given in the source tables.</P>
<H2 class=dtH1>Managing User Profiles</H2>
<P>To round out this discussion about table mappings, let's review a realistic scenario in which you might want to consider their use. Suppose that you have to manage different user profiles. Each profile requires you to access the same tables, but return a different set of columns. You can tackle this issue in a number of ways, but the ADO.NET table mapping mechanism may be the best. </P>
<P>The idea is that you use always a single query—the one targeted to the most privileged profile—and then map to the resulting DataSet with only the columns specific of the current user profile. Here's some Visual Basic® code that illustrates the point:</P><PRE class=code>Dim da As SqlDataAdapter
da = New SqlDataAdapter(m_selectCommand, m_connectionString)

Dim dtm As DataTableMapping
dtm = da.TableMappings.Add(da.DefaultSourceTableName, "Employees")

If bUserProfileAdmin Then       
   dtm.ColumnMappings.Add("EmployeeID", "ID")
   dtm.ColumnMappings.Add("LastName", "Last Name")
   dtm.ColumnMappings.Add("FirstName", "Name")
   dtm.ColumnMappings.Add("Title", "Position")
   dtm.ColumnMappings.Add("HireDate", "Hired")
Else
   dtm.ColumnMappings.Add("LastName", "Last Name")
   dtm.ColumnMappings.Add("FirstName", "Name")
End If

Dim ds As DataSet = New DataSet()
da.MissingMappingAction = MissingMappingAction.Ignore
da.MissingSchemaAction = MissingSchemaAction.Add
da.Fill(ds)
 </PRE>
<P>In this simple case, the query returns only one result set that I decided to identify through its default name of <I>Table</I>. Notice that for the sake of generality you should use the <B>DefaultSourceTableName</B> property of the data adapter object, rather than literal name (<I>Table</I>). The table mapping defines different column mappings according to the role of the user. If the user is an administrator, the DataSet includes more columns. Of course, the actual implementation of concepts like roles and privileges is completely up to you. The key statement for all this to work as expected is the value of the <B>MissingMappingAction</B> property that has been set to <B>Ignore</B>. The result is that unmapped columns are just ignored. Finally, remember that case sensitivity is important for column names, and that the name of the column mapping must match the case of the source column name. </P>
<H2 class=dtH1>Summary</H2>
<P>In this article, I reviewed the table mapping mechanism available in ADO.NET. Table mapping is the set of rules and behaviors that govern the passage of rows from the data source to an in-memory DataSet. The mapping consists of two steps—table and column mapping—and is only the first phase of a broader operation that involves filling a DataSet operated by a data adapter object. The second phase begins when the target DataSet is actually populated. Any logical exception in the mapping and filling phases can be controlled by declaring which actions to take when a table or a column is not explicitly bound to a DataSet table or when a needed table or a column is not present in the DataSet. </P>
<HR noShade SIZE=1>

<H2 class=dtH1>Dialog Box: Getting @ the Difference</H2>
<P><B>What's the difference between @Register and @Import, and what's the right place for a non-system assembly DLL used by ASP.NET applications?</B></P>
<P>First and foremost, ASP.NET applications are .NET applications. As such, they need to link to any assemblies whose objects they plan to use. The @Register directive serves just this purpose. Any assembly you register with the page is then passed as a reference to the compiler of choice. The role of the @Import directive is less important as its function is to simplify the coding. @Import lets you import a namespace, not an assembly. An assembly can contain more namespaces. For example, the assembly system.data.dll contains System.Data, System.Data.OleDb, System.Data.SqlClient, and more.</P>
<P>Importing a namespace lets you write simpler code in the sense that you don't need to specify the full path to a given object. Importing System.Data allows you to use a data set through the class DataSet, instead of System.Data.DataSet. To use a DataSet, you can do without the @Import directive, but not without the reference to system.data.dll.</P>
<P>In particular, with ASP.NET applications you don't need to explicitly register any assemblies available in the Global Assembly Cache (GAC). You use @Register only to reference custom assemblies that have been registered with the system GAC.</P>
<P>Where do these assemblies reside? They must be placed in the BIN directory under the application's virtual directory. If this directory does not exist, you should create it. If your ASP.NET application does not use a virtual directory, then it implicitly runs from the Web server's root directory. Therefore, the BIN directory is below the Web server's root. For example, c:\inetpub\wwwroot\bin. </P>
<HR noShade SIZE=1>

<P><B>Dino Esposito</B> is <A href="http://www.wintellect.com/">Wintellect</A>'s ADO.NET expert and a trainer and consultant based in Rome, Italy. Dino is a contributing editor to MSDN Magazine and writes the Cutting Edge column. He also regularly contributes to <A href="http://www.dnjonline.com/">Developer Network Journal</A> and MSDN News. In addition, he is the author of <A href="http://www.microsoft.com/MSPress/books/5727.asp">Building Web Solutions with ASP.NET and ADO.NET</A> from Microsoft Press, and the cofounder of <A href="http://www.vb2themax.com/">http://www.vb2themax.com/</A>. You can reach Dino at <A href="mailto:dinoe@wintellect.com">dinoe@wintellect.com</A>.</P><!--closes the topic content div--><!--FOOTER_END--><!-- End Content --><img src ="http://www.blogjava.net/jinfeng_wang/aggbug/27480.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jinfeng_wang/" target="_blank">jinfeng_wang</a> 2006-01-10 22:27 <a href="http://www.blogjava.net/jinfeng_wang/archive/2006/01/10/27480.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Data Relations and Relatives (zz from msdn)</title><link>http://www.blogjava.net/jinfeng_wang/archive/2006/01/10/27478.html</link><dc:creator>jinfeng_wang</dc:creator><author>jinfeng_wang</author><pubDate>Tue, 10 Jan 2006 14:25:00 GMT</pubDate><guid>http://www.blogjava.net/jinfeng_wang/archive/2006/01/10/27478.html</guid><wfw:comment>http://www.blogjava.net/jinfeng_wang/comments/27478.html</wfw:comment><comments>http://www.blogjava.net/jinfeng_wang/archive/2006/01/10/27478.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jinfeng_wang/comments/commentRss/27478.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jinfeng_wang/services/trackbacks/27478.html</trackback:ping><description><![CDATA[<DIV id=nsbanner>
<DIV id=TitleRow>
<H1 class=dtH1><A name=data07122001></A>Data Relations and Relatives</H1></DIV></DIV><!--NONSCROLLING BANNER END-->
<P>Dino Esposito<BR>Wintellect</P>
<P>July 12, 2001</P>
<P>Download <A href="http://download.microsoft.com/download/1/6/b/16bc0de0-471e-4f8b-8aaa-c4e240371c47/ViewManager.exe">ViewManager.exe</A>.</P>
<P>A large number of applications need to render data that is somehow related to other data. A well-known example that illustrates this is given by the very popular Customers table that needs to link to an equally famous Orders table. What do they have in common? An even more famous CustID field, of course. How do you typically solve the problem of rendering all the orders for a given customer? According to the constraints and the requirements of your application, you might be applying a number of feasible solutions. </P>
<P>In .NET, a new valuable tool can be added to your programmer's toolkit. This tool is the <B>DataRelation</B> object. It basically represents a parent/child relationship set between two tables. The <B>DataRelation</B> per se is not such a big deal. It gains a lot of importance and usefulness, though, when you look at it in light of the support that the <B>DataSet</B> and other ADO.NET objects and controls have for relations.</P>
<H2 class=dtH1>Typical Approaches</H2>
<P>When you need to fetch data from related tables, a popular solution is to you use a plain old INNER JOIN SQL command. It merges the column of the two input tables that you need to work with into a single resultset . The following code creates a final resultset where you find two columns from <CODE class=ce>Customers</CODE> and three columns from <CODE class=ce>Orders</CODE>. </P><PRE class=code>SELECT c.Name, c.City, o.Date, o.TotalPrice, oShipAddress 
FROM Customers AS c
INNER JOIN Orders AS o 
ON c.CustID = o.CustID
</PRE>
<P>The INNER JOIN statement involves the database server and ends up returning rows with a certain quantity of duplicated data. When you run the above query, you are aimed to obtain and process all the orders for a certain customer. So, you don't need to repeat the information—address, city, and the like—you want to return about the customer. Nevertheless, this is exactly what you get back in the form of tabular structure from the resultset. </P>
<P>In ADO, the data shaping service lets you create hierarchical and, hence, even irregularly shaped recordsets. By using data shaping, the Customers/Orders relationship would have been expressed in terms of one row with all the customer information, plus an extra field pointing to a child recordset. The child recordset would feature one row for each order associated with the specified customer ID. The structure of the order rows is determined by the fields you want to query from the Orders table. </P>
<P>ADO data shaping requires you to write queries with a special language called the SHAPE language. </P><PRE class=code>SHAPE 
  {SELECT Name, City, Custid FROM Employees} 
APPEND (
  {SELECT CustId, Date, TotalPrice, ShipAddress FROM Orders} AS oOrders 
  RELATE CustId TO CustId)
</PRE>
<P>Next, it executes all the necessary queries on the database within a single connection. The results are then shaped in terms of hierarchical recordsets on the way to the client thanks to a special OLE DB service. </P>
<P>INNER JOINs and data shaping have to do more with the way in which you fetch and store the related data. What about retrieving and showing this data in a client application?</P>
<P>What an INNER JOIN statement returns is a tabular structure and the extraction of needed information is completely up to you. With data shaping, the information at least comes in with a layout that lends itself quite well to be displayed the way it should be. </P>
<P>The customer information is distinct from the list of orders. You access it as a normal recordset field with a particular name. </P><PRE class=code>Set rsCustomerOrders = oRS.Fields("oOrders").Value
</PRE>
<P>To access the orders for a given customer, select the corresponding row on the Customers table and then access the field whose name matches the previously set relation.</P>
<H2 class=dtH1>ADO.NET Sort of Data Shaping</H2>
<P>To represent parent/child data relationships in ADO.NET, you are expected to use the <B>DataRelation</B> object. If you're familiar with ADO data shaping, you'll soon recognize, under the hood of a <B>DataRelation</B> object, the SHAPE language code snippet that I just showed above.</P>
<P>In ADO.NET, a <B>DataRelation</B> object is used to establish an in-memory relationship between two <B>DataTable</B> objects. The relationship sets on matching values found in one column the two tables have in common. A column in ADO.NET is represented by the <B>DataColumn</B> object. </P>
<P>Let's see how to code in ADO.NET the Customer/Orders relationship seen earlier.</P><PRE class=code>DataColumn dcCustomerCustID, dcOrdersCustID;
// Fill in the two DataColumn objects
dcCustomerCustID = DataSet1.Tables["Customers"].Columns["CustID"];
dcOrdersCustID = DataSet1.Tables["Orders"].Columns["CustID"];
// Create the relationship between the two columns
DataRelation relCustomerOrders;
relCustomerOrders = new DataRelation("CustomerOrders", 
dcCustomerCustID, dcOrdersCustID); 
</PRE>
<P>A freshly created <B>DataRelation</B> object is rather useless if you don't add it to a <B>DataSet</B>. </P><PRE class=code>DataSet1.Relations.Add(relCustomerOrders);
</PRE>
<P>The <B>DataSet</B> object contains a <B>Relations</B> data member, which is a <B>DataRelationCollection</B> object where all the relations involving DataSet's tables are kept. </P>
<P>Notice that any relation is created between matching columns in two tables within the same DataSet. For this to happen, the .NET type of the columns must be identical. The .NET type of a column is given by the value returned by its <B>DataType</B> property.</P>
<P>When you have a parent/child relationship set between two tables, deleting or updating a value in the parent table can affect the rows of the child table. The impact on the child rows manifests in one of the following ways: 
<UL type=disc>
<LI>The child rows must be deleted or updated with cascading changes. 
<LI>Values in child columns must be set to NULL values. 
<LI>Values in child columns must be set to default values. </LI></UL>
<P>If you don't manage this directly through a <B>ForeignKeyConstraint</B> policy, the operation originates an exception. </P>
<P>So, if you're going to create an in-memory relation for cached, disconnected data that you plan to modify, make sure you first define a <B>ForeignKeyConstraint</B> object on the parent table. This ensures that any change that could affect the related tables is properly managed. You create a constraint like this:</P><PRE class=code>ForeignKeyConstraint fkc; 
DataColumn dcCustomersCustID, dcOrdersCustID;
// Get columns and create the constraint
dcCustomersCustID = DataSet1.Tables["Customers"].Columns["CustID"];
dcCustomersCustID = DataSet1.Tables["Orders"].Columns["CustID"];
fkc = new ForeignKeyConstraint("CustomersFK", 
dcCustomersCustID, dcOrdersCustID);
// Shape up the constraint for delete and update
fkc.DeleteRule = Rule.SetNull;
fkc.UpdateRule = Rule.Cascade;
</PRE>
<P>A <B>ForeignKeyConstraint</B> is created on the parent table using the common column that the parent and child table share. To specify how a child table behaves whenever a row on the parent table is deleted or updated, you use the DeleteRule and UpdateRule fields. In this case, I set all the values on the child row to NULL when the corresponding parent row is deleted. Furthermore, any update simply trickles down from the parent row to the child row.</P>
<P>A <B>DataTable</B> object maintains its collection of <B>ForeignKeyConstraint</B> objects in a ConstraintCollection class that is accessible through the DataTable's <B>Constraints</B> property. As a final note, bear in mind that constraints are not enforced on tables if you set the <B>EnforceConstraints</B> property to false.</P><PRE class=code>// Add the constraint and enforce it  
DataSet1.Tables["Customers"].Constraints.Add(fkc);
DataSet1.EnforceConstraints = true;
</PRE>
<P>Upon creation, ADO.NET verifies that the <B>DataRelation</B> object can be effectively created. This basically means that it checks whether or not all the involved columns are really part of the given tables. According to the syntax, in fact, you could pass the DataRelation's constructor a <B>DataColumn</B> object that you create on the fly with the right type and name but not the "right" column.</P>
<P>The <B>DataRelation</B> object and the involved <B>DataTable</B> objects are disjointed and independent objects until the relation is added to the DataSet's Relations collection. When this happens, ADO.NET prohibits any changes on the tables that could invalidate the relation. For example, changes on columns are disallowed, as well as moving the tables from one <B>DataSet</B> to another.</P>
<P>The <B>DataRelation</B> object also features a method called <B>CheckStateForProperty</B> that allows you to verify the validity of the relation before you add it to a <B>DataSet</B>. The controls operated by this method include checking whether parent and child tables belong to different <B>DataSet</B> objects, whether the columns type matches, and makes sure that parent and child columns aren't the same column. </P>
<P>You can call this method even if the <B>DataRelation</B> doesn't yet belong to a <B>DataSet</B>—the same <B>DataSet</B> to which the involved tables belong. <B>CheckStateForProperty</B> doesn't return a Boolean value to mean success or failure. In case of error, you will be notified through a <B>DataException</B> exception.</P>
<H2 class=dtH1>Accessing Child Records</H2>
<P>Given a parent/child data relation, how can you access the child rows associated with a parent row? In other words, assuming that you have the same Customers and Orders tables in the DataSet , how can you get the orders for a given Customers row?</P>
<P>In the code accessing the related data, you first obtain a <B>DataRow</B> object representing the parent row. You can do this in a number of ways, all strictly dependent on the structure of your application.</P>
<P>For example, if you know the primary key value that uniquely identifies that row, you can use the <B>Find</B> method on the <B>RowsCollection</B> object that represents the rows of the table.</P><PRE class=code>DataRow r = DataSet1.Tables["Customers"].Rows.Find(nCustID);
</PRE>
<P>Once you hold the right <B>DataRow</B> object, obtaining the child rows according to a given relation is as easy as calling the method <B>GetChildRows</B> to fill up an array of <B>DataRow</B> objects.</P><PRE class=code>DataRow[] rgCustomerOrders;
rgCustomerOrders = r.GetChildRows(relCustomerOrders);
</PRE>
<P><B>GetChildRows</B> takes one argument being a reference to a valid <B>DataRelation</B> object set on that <B>DataSet</B>. It returns the child rows as an array of <B>DataRow</B> objects. The following code shows how to dump all the orders of a given customer to the console .</P><PRE class=code>for (int i=0; i &lt; rgCustomerOrders.Length; i++)  {
   DataRow tmp = rgCustomerOrders[i];
   Console.WriteLine(tmp["CustID"].ToString());
   Console.WriteLine(tmp["Date"].ToString());
   Console.WriteLine(tmp["ShipAddress"].ToString());
   Console.WriteLine("");
}  
</PRE>
<P>To be honest, <B>GetChildRows</B> can be called through a couple of other prototypes. You can certainly specify the relation as a <B>DataRelation</B> object as shown above. However, you could also indicate the relation by name. </P><PRE class=code>rgCustomerOrders = r.GetChildRows("CustomerOrders");
</PRE>
<P>In addition, you can select the version of the various rows that must be returned. You do this through the following signatures:</P><PRE class=code>public DataRow[] GetChildRows(
   DataRelation relation,
   DataRowVersion version
);
public DataRow[] GetChildRows(
   String relationName,
   DataRowVersion version
);
</PRE>
<P>You indicate the version of the rows through the values in the DataRowVersion enumeration. Possible values are Default, Original, Current, and Proposed. </P>
<H2 class=dtH1>Automatic Master/Detail Views</H2>
<P>Since the <B>DataRelation</B> object associates rows in one <B>DataTable</B> object with rows in another <B>DataTable</B> object, it lends itself very well to build master/detail views. The <B>GetChildRows</B> method is the key tool to build such views. If you find this behavior quite cool, you'll love the Windows Forms DataGrid control, which does even more.</P>
<P>You set the DataGrid control to show data coming from the source specified in its DataSource property. If DataSource happens to point to a cointainer control, like <B>DataSet</B> or <B>DataViewManager</B>, it will feature one row for each child table prefixed by a + symbol. Click there and you'll see the content of that table. </P>
<P>You can select a specific table by setting the <B>DataMember</B> property with the name of the child table.</P><PRE class=code>theMasterGrid.DataSource = ds
theMasterGrid.DataMember = "Customers"
</PRE>
<P>If you have two datagrid controls on your form and want to realize a master/detail view, you can associate each grid with a different table, and then hook up for the event that fires when a new item is selected in the master table. At this point, you could access the array with related child rows, create a <B>DataTable</B> on the fly, and update the DataSource property of the detail DataGrid. Notice that you must use the <B>SetDataBinding</B> method at run time to reset the DataSource property for a Windows Forms datagrid.</P>
<P>This approach works just fine, but DataGrid controls can better perform this action. You can have the DataGrid automatically refresh the detail view if you use a special syntax when setting the <B>DataMember</B> property. </P><PRE class=code>theChildGrid.DataSource = ds
theChildGrid.DataMember = "Customers.CustomerOrders"
</PRE>
<P>If you concatenate the name of the parent table with the name of an existing relation and put a dot character in the middle of the two, you instruct the DataGrid control to automatically and silently call <B>GetChildRows</B> for the CustomerOrders relation on the currently selected row of the Customers table. </P>
<P>The magic performed by the DataGrid doesn't end here. As long as the two grids have the same data source, the child one will automatically hook for the event that indicates that a new row has been selected in the master grid. At the end of the day, a relation, two Windows Forms datagrid and the following four lines of code, are enough to produce a free auto-refreshing master/detail view.</P><PRE class=code>theMasterGrid.DataSource = ds
theMasterGrid.DataMember = "Customers"
theChildGrid.DataSource = ds
theChildGrid.DataMember = "Customers.CustomerOrders"
</PRE>
<P>A good question to raise at this point is, "How can the child grid know about the parent grid?" Basically, any DataGrid that is assigned the content of a parent/child relationship investigates the running instance of another grid object in the same form with the same content in the <B>DataSource</B> property and with a DataMember that matches the first part of its member expression (Customers in the above example).</P>
<H2 class=dtH1>Rendering DataRelations in XML</H2>
<P>Relations constitute a key information for <B>DataSet</B> objects. <B>DataSets</B>, though, can switch any time from their typical relational representation to a hierarchical one based on XML. When relations are set, internally the <B>DataSet</B> object works in a way that resembles what happens with the ADO data shaping representation. An extra field is spookily added to each row to link to its group of child rows in the child table. What happens of this information when you switch from the traditional representation to XML?</P>
<P>You can do this in two ways. Either you create a new instance of the <B>XmlDataDocument</B> class based on the DataSet </P><PRE class=code>XmlDataDocument xmlDoc = new XmlDataDocument(DataSet1);
</PRE>
<P>Or you can save the whole <B>DataSet</B> to XML using the <B>WriteXml</B> method. </P>
<P>In both cases, the results you get differ quite a bit depending on the value that the <B>Nested</B> property has on the <B>DataRelation</B> object. Nested is a Boolean value that is set to false by default. It controls the way in which the child rows of a relation are rendered in XML. Two DataSet tables are rendered like this:</P><PRE class=code>&lt;CustomerOrders&gt;
  &lt;Customers&gt;
    &lt;CustID&gt;1&lt;/CustID&gt;
    &lt;Name&gt;Acme Inc&lt;/Name&gt;
  &lt;/Customers&gt;
  &lt;Customers&gt;
    &lt;CustID&gt;2&lt;/CustID&gt;
    &lt;Name&gt;Foo Corp&lt;/Name&gt;
  &lt;/Customers&gt;
  &lt;Orders&gt;
    &lt;CustID&gt;1&lt;/CustID&gt;
    &lt;Date&gt;2000-09-25T00:00:00&lt;/Date&gt;
  &lt;/Orders&gt;
&lt;CustomerOrders&gt;
</PRE>
<P>Each record is rendered with a subtree with the table name and as many text nodes as the number of columns. This representation doesn't change if you have relations set as long as <B>Nested</B> remains set to false.</P>
<P>If you set <B>Nested</B> to true, then all the order nodes for any given customer will be rendered as a child subtree.</P><PRE class=code>&lt;CustomerOrders&gt;
<CODE><B class=cfe>  &lt;Customers&gt;</B></CODE>
<CODE><B class=cfe>    &lt;CustID&gt;1&lt;/CustID&gt;</B></CODE>
<CODE><B class=cfe>    &lt;Orders&gt;</B></CODE>
<CODE><B class=cfe>       &lt;CustID&gt;1&lt;/CustID&gt;</B></CODE>
<CODE><B class=cfe>       &lt;Date&gt;2000-09-25T00:00:00&lt;/Date&gt;</B></CODE>
<CODE><B class=cfe>    &lt;/Orders&gt; </B></CODE>
<CODE><B class=cfe>    &lt;Name&gt;Acme Inc&lt;/Name&gt;</B></CODE>
<CODE><B class=cfe>  &lt;/Customers&gt;</B></CODE>
  &lt;Customers&gt;
    &lt;CustID&gt;2&lt;/CustID&gt;
    &lt;Name&gt;Foo Corp&lt;/Name&gt;
  &lt;/Customers&gt;
&lt;CustomerOrders&gt;
</PRE>
<P>All the orders that correspond to a given customer go under the node of that customer, building up a more reasonable and useful structure.</P>
<H2 class=dtH1>Summary</H2>
<P><B>DataRelation</B> is the ADO.NET object that represents a logical link between two tables through a common column. The <B>DataRelation</B> defines the parent/child relationship, but the tables and the columns remain separate entities. Once a relation has been set, you can easily access the child rows of the detail table either by using methods on the <B>DataRow</B> object or switching to the XML hierarchical representation. The <B>DataRelation</B> object looks like an in-memory INNER JOIN, but without the same redundancy of information.</P>
<TABLE class=dtTABLE cellSpacing=2 cellPadding=2 bgColor=#eeeee border=1 frame=box><FONT size=-2>
<TBODY>
<TR>
<TD>
<H3>Dialog Box: Modifications Through the View</H3>
<P><B>What's the role of all those AllowXXX properties on the DataView object? Can I modify rows or not through a DataTable's view?</B></P>
<P>A <B>DataView</B> is an object that provides a particular representation of the content of a given table. The <B>DataView</B> and the <B>DataTable</B> are independent objects, and the <B>DataView</B> simply holds a link to the parent table. The <B>DataView</B> doesn't cache the table, nor does it makes an internal copy of the data. The <B>DataView</B> is simply an object that contains some information about the way and the order in which the content of the table must be shown.</P>
<P>The core function that you execute on a <B>DataView</B> is the enumeration of the items. This happens explicitly when you loop through its content or implicitly when you assign the <B>DataView</B> to the <B>DataSource</B> property of a data-bound control. When a data-bound control calls its <B>DataBind</B> method, the content of the data source is enumerated and the control's <B>Items</B> collection is properly populated. </P>
<P>When a view is involved, the caller enumerates through the view, and the view in turn enumerates through the parent table and applies sorting expressions and filters.</P>
<P>The <B>AllowEdit</B>, <B>AllowDelete</B>, and <B>AllowNew</B> Boolean properties indicate whether the <B>DataView</B> and the user interface associated with it allow updates, deletions, and insertions. This doesn't affect the way in which the parent table is updated. Those properties apply only to the edit operations carried out through the DataView control or a data-bound control that uses it.</P></FONT></TD></TR></TBODY></TABLE>
<P>&nbsp;</P>
<HR noShade SIZE=1>

<P class=clsMagFooter><B>Dino Esposito</B> works for <A href="http://www.wintellect.com/" target=_top>Wintellect</A>,<IMG height=11 alt="Non-MS link" src="http://msdn.microsoft.com/library/en-us/dndive/html/leave-ms.gif" width=33 border=0> where he takes on ADO.NET and ASP.NET training and consulting. He is the co-founder of <A href="http://www.vb2themax.com/" target=_top>VB-2-The-Max</A>,<IMG height=11 alt="Non-MS link" src="http://msdn.microsoft.com/library/en-us/dndive/html/leave-ms.gif" width=33 border=0> and also contributes the Cutting Edge column to MSDN Magazine. You can reach Dino at <A href="mailto:dinoe@wintellect.com">dinoe@wintellect.com</A>. 
<P><BR><!--closes the topic content div--><!--FOOTER_END--><!-- End Content --></P></FONT><img src ="http://www.blogjava.net/jinfeng_wang/aggbug/27478.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jinfeng_wang/" target="_blank">jinfeng_wang</a> 2006-01-10 22:25 <a href="http://www.blogjava.net/jinfeng_wang/archive/2006/01/10/27478.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Inside ADO.NET Batch Update （zz from msdn）</title><link>http://www.blogjava.net/jinfeng_wang/archive/2006/01/10/27476.html</link><dc:creator>jinfeng_wang</dc:creator><author>jinfeng_wang</author><pubDate>Tue, 10 Jan 2006 14:23:00 GMT</pubDate><guid>http://www.blogjava.net/jinfeng_wang/archive/2006/01/10/27476.html</guid><wfw:comment>http://www.blogjava.net/jinfeng_wang/comments/27476.html</wfw:comment><comments>http://www.blogjava.net/jinfeng_wang/archive/2006/01/10/27476.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jinfeng_wang/comments/commentRss/27476.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jinfeng_wang/services/trackbacks/27476.html</trackback:ping><description><![CDATA[<DIV id=nsbanner>
<DIV id=TitleRow>
<H1 class=dtH1><A name=data11082001></A>Inside ADO.NET Batch Update</H1></DIV></DIV><!--NONSCROLLING BANNER END-->
<P>Dino Esposito<BR>Wintellect</P>
<P>November 8, 2001</P>
<P>The interaction between ADO.NET applications and the underlying data sources is based on a dual architecture with two-way channels. You access a data source to read and write rows using either individual and provider-specific commands or<I> batch update procedures</I>. In both cases, the data access results in a complete two-way binding and involves different objects and methods. You use command classes like <B>SqlCommand</B> and <B>OleDbCommand</B> to execute single commands. You would use data adapter objects to download disconnected data and submit sets of updated rows. Individual commands return data through data reader objects, whereas the <B>DataSet</B> is the container object that the data adapter utilizes to return and submit blocks of records.</P>
<P>Updates accomplished through individual commands, stored procedures, and in general any command text the managed provider understands, are normally referred to as <I>update</I>. An update command always carries new data out embedded in the body of the statement. The update command always requires an open connection, and may also require an ongoing or a new transaction. The <I>batch update</I> is the offshoot of a slightly different approach. At the highest level of abstraction, you don't issue a command, no matter how complex it could be. Instead, you submit a snapshot of the current rows as modified on the client and wait for the data source approval. The key concept behind batch update is the concept of data disconnection. You download a table of rows, typically a DataSet, modify it as needed on the client, and then submit the new image of those rows to the database server. You submit changes rather than executing a command that will create changes to the data source. This is the essential difference between update, which I covered in my July column, and batch update. </P>
<P>The figure below illustrates the dual update architecture of ADO.NET. </P>
<P class=fig><IMG alt="" src="http://msdn.microsoft.com/library/en-us/dndive/html/data11082001-fig01.gif" border=0></P>
<P class=label><B>Figure 1. The dual two-way interaction between ADO.NET applications and the data source</B></P>
<P>Before going any further with the details of ADO.NET batch update, I'd like to clarify one aspect of the batch update model that often leads to some misunderstanding. Although update and batch update are philosophically different, in terms of the actual implementation within ADO.NET, they follow the same update model. Both update and batch update are accomplished through direct and provider-specific statements. Of course, since batch update normally involves more rows, the statements are grouped into a batch call. Batch update loops through the rows of the target DataSet and issue the proper update command (INSERT, DELETE, or UPDATE) whenever an updated row is found. In correspondence of an updated row, a predefined direct SQL command is run. In essence, this is batch update. </P>
<P>This comes as no surprise. In fact, if batch update were using a completely different model of update, then a special support would have been required from the data source. (This is what happens when you submit XML updategrams to SQL Server 2000.) Batch update is just a client-provided software mechanism to simplify the submission of multiple row updates. In any case, each new row submission is always made through the normal channels of data source direct commands. </P>
<P>So far I've only hinted at SQL commands, but these hints are a sure sign of an important difference between the ADO and the ADO.NET batch update implementation. In ADO, batch update was only possible for SQL-based data sources. In ADO.NET, instead, batch update is possible for any kind of managed provider, including those that should not be exposing their data through the SQL query language. That said, it's about time I start reviewing the key aspects of ADO.NET batch update programming.</P>
<H2 class=dtH1>Preparing the DataSet for Submission</H2>
<P>ADO.NET batch update takes place through the <B>Update</B> method of the data adapter object. Data can be submitted only on a per-table basis. If you call <B>Update</B> without specifying a table name, a default name of <I>Table</I> is assumed. If no table exists with that name, an exception is raised. <B>Update</B> first examines the <B>RowState</B> property of each table row and then prepares a tailor-made INSERT, UPDATE, or DELETE statement for each inserted, updated, or deleted row in the specified table. </P>
<P>The <B>Update</B> method has several overloads. It can take a pair given by the DataSet and the DataTable, a DataTable, or even an array of DataRow objects. The method returns an integer value being the number of rows successfully updated.</P>
<P>To minimize the network traffic, you normally invoke <B>Update</B> on a subset of the DataSet you are working on. Needless to say, this subset contains only the rows that have been modified in the meantime. You get such a subset by calling the DataSet's <B>GetChanges</B> method.</P><PRE class=code>if (ds.HasChanges())
{
DataSet dsChanges = ds.GetChanges();
adapter.Update(dsChanges, "MyTable");
}
</PRE>
<P>You check the DataSet for changes using the <B>HasChanges</B> method instead. <B>HasChanges</B> returns a Boolean value.</P>
<P>The DataSet returned by <B>GetChanges</B> contains the rows that have been inserted, deleted, or modified in the meantime. But in the meantime of what? This is a tricky aspect of ADO.NET batch update and has to do with the current state of a table row.</P>
<H2 class=dtH1>States of a Row</H2>
<P>Each row in a <B>DataTable</B> is rendered through a <B>DataRow</B> object. A <B>DataRow</B> object mainly exists to be an element of the <B>Rows</B> collection of a parent <B>DataTable</B> object. Conceptually, a database row is inherently linked to the structure of a given table. Just for this case, the DataRow class in ADO.NET does not provide a public constructor. The only way to create a new <B>DataRow</B> object is by means of a method called <B>NewRow</B>, on a particular living instance of a <B>DataTable</B> object. Upon creation, a row does not yet belong to the Rows collection of the parent table, but its relationship with this collection determines the state of the row. The following table shows the feasible values for the <B>RowState</B> property. Those values are grouped in the <B>DataRowState</B> enumeration.</P>
<TABLE class=data>
<TBODY>
<TR vAlign=top>
<TH class=data align=left width="16%">Row State</TH>
<TH class=data align=left width="84%">Description</TH></TR>
<TR vAlign=top>
<TD class=data width="16%">Added</TD>
<TD class=data width="84%">The row has been added to the table.</TD></TR>
<TR vAlign=top>
<TD class=data width="16%">Deleted</TD>
<TD class=data width="84%">The row has been marked for deletion from the parent table.</TD></TR>
<TR vAlign=top>
<TD class=data width="16%">Detached</TD>
<TD class=data width="84%">Either the row has been created but not added to the table, or the row has been removed from the collection of table rows.</TD></TR>
<TR vAlign=top>
<TD class=data width="16%">Modified</TD>
<TD class=data width="84%">Some columns within the row have been changed.</TD></TR>
<TR vAlign=top>
<TD class=data width="16%">Unchanged</TD>
<TD class=data width="84%">No changes made to the row since creation or since the last call to the <B>AcceptChanges</B> method.</TD></TR></TBODY></TABLE>
<P>The <B>RowState</B> property of each row influences the return value of the <B>HasChanges</B> method and the contents of the child DataSet returned by <B>GetChanges</B>. </P>
<P>From the range of the feasible values, it turns out that the value of <B>RowState</B> mostly depends on the operation that has been performed on the row. ADO.NET tables implement a transaction-like commit model based on two methods—<B>AcceptChanges</B> and <B>RejectChanges</B>. When the table is downloaded from the data source, or freshly created in memory, all the rows are unchanged. All the changes you enter are not immediately persistent and can be rolled back at any time by calling <B>RejectChanges</B>. You can call the <B>RejectChanges</B> method at three levels: 
<UL type=disc>
<LI>On the DataSet to reject all changes whatsoever. 
<LI>On the DataTable to cancel all the changes in the particular table. 
<LI>On a particular row to restore its previous state. </LI></UL>
<P>The method <B>AcceptChanges</B> has the power to commit all the ongoing changes. It makes the DataSet accept the current values as the new original values. As a result, all the pending changes are cleared up. Just as <B>RejectChanges</B>, <B>AcceptChanges</B> can also be called on the whole DataSet, on a particular table, or an individual row. </P>
<P>When you start a batch update operation, only the rows marked as <B>Added</B>, <B>Deleted</B>, and <B>Modified</B> are taken into account for submission. If you happen to call <B>AcceptChanges</B> prior to batch-update, no change will be persisted to the data source. </P>
<P>On the other hand, once the batch update operation has successfully completed, you <I>must</I> call <B>AcceptChanges</B> to clear pending changes and mark the current DataSet values as the original values. Notice that omitting a final call to <B>AcceptChanges</B> would maintain pending changes in the DataSet with the result to have them re-issued next time you batch update. </P><PRE class=code>// Get changes in the DataSet
dsChanges = ds.GetChanges();
// Performs the batch update for the given table
da.Update(dsChanges, strTable);
// Clears any pending change in memory
ds.AcceptChanges();
</PRE>
<P>The code above illustrates the three main steps behind ADO.NET batch update. </P>
<P>If you delete a row from a DataSet table, pay attention to the method you use—<B>Delete</B> or <B>Remove</B>. The <B>Delete</B> method performs a logical deletion by marking the row as <B>Deleted</B>. The <B>Remove</B> method, instead, physically removes the row from the Rows collection. As a result, a row deleted through <B>Remove</B> is <I>not</I> marked for deletion and subsequently not processed during batch update. If the ultimate goal of your deletion is removing the row from the data source, then use <B>Delete</B>.</P>
<H2 class=dtH1>Update Internals</H2>
<P>Three operations can modify the state of a table: 
<UL type=disc>
<LI>Insertion of a new row 
<LI>Deletion of an existing row 
<LI>Update of an existing row </LI></UL>
<P>For each of these key operations, the data adapter defines a tailor-made command object that is exposed as a property. Such properties include <B>InsertCommand</B>, <B>DeleteCommand</B>, and <B>UpdateCommand</B>. The programmer is responsible to assign these properties meaningful command objects—for example, <B>SqlCommand</B> objects. </P>
<P>Just the availability of <B>InsertCommand</B>, <B>DeleteCommand</B>, and <B>UpdateCommand</B> properties represents a quantum leap from ADO. Such properties give you unprecedented control over the way in which in-memory updates are submitted to the database server. If you happen to dislike the update code that ADO.NET generates, you can now modify it without renouncing the overall feature of batch update. With ADO you had no control on the SQL commands silently generated by the library. In ADO.NET, instead, publicly exposed command objects allow you to apply updates using made-to-measure stored procedures or SQL statements that better match your user expectations. In particular, you can have the batch update system work with cross-referenced tables and even target non-SQL data providers like Active Directory™ or Indexing Services. </P>
<P>The update commands are expected to run for each changed row in the table and have to be general enough to accommodate different values. Command parameters would be good at this kind of task as long as you can you bind them to the values of a database column. ADO.NET parameter objects expose two properties, like <B>SourceColumn</B> and <B>SourceVersion,</B> which provide for this type of binding. <B>SourceColumn</B>, in particular, represents an indirect way to indicate the parameter's value. Instead of using the <B>Value</B> property and setting it with a scalar value, you could set the <B>SourceColumn</B> property with a column name and have the batch update mechanism to extract the effective value from time to time.</P>
<P><B>SourceVersion</B> indicates which value should be read on the column. By default, ADO.NET returns the current value of the row. As an alternative, you could select the original value and all the values found in the <B>DataRowVersion</B> enumeration. </P>
<P>If you want to batch update a couple of columns on the Northwind's Employees table, you can use the following, handcrafted commands. The INSERT command is defined as follows:</P><PRE class=code>StringBuilder sb = new StringBuilder("");
sb.Append("INSERT Employees (firstname, lastname) VALUES(");
sb.Append("@sFirstName, @sLastName)");
da.InsertCommand = new SqlCommand();
da.InsertCommand.CommandText = sb.ToString();
da.InsertCommand.Connection = conn;
</PRE>
<P>All the parameters will be added to the data adapter's <B>Parameters</B> collection and bound to a DataTable column. </P><PRE class=code>SqlParameter p1 = new SqlParameter("@sFirstName", SqlDbType.NVarChar, 10);
p1.SourceVersion = DataRowVersion.Current;
p1.SourceColumn = "firstname";
da.InsertCommand.Parameters.Add(p1);
SqlParameter p2 = new SqlParameter("@sLastName", SqlDbType.NVarChar, 30);
p2.SourceVersion = DataRowVersion.Current;
p2.SourceColumn = "lastname";
da.InsertCommand.Parameters.Add(p2);
</PRE>
<P>Notice that auto-increment columns should not be listed in the syntax of the INSERT command as their value is being generated by the data source. </P>
<P>The UPDATE command needs to identify one particular row to apply its changes. You do this using a WHERE clause in which a parameterized value is compared against a key field. In this case, the parameter used in the WHERE clause must be bound to the original value of the row, instead of the current value.</P><PRE class=code>StringBuilder sb = new StringBuilder("");
sb.Append("UPDATE Employees SET ");
sb.Append("lastname=@sLastName, firstname=@sFirstName ");
sb.Append("WHERE employeeid=@nEmpID");
da.UpdateCommand = new SqlCommand();
da.UpdateCommand.CommandText = sb.ToString();
da.UpdateCommand.Connection = conn;
// p1 and p2 set as before
:
p3 = new SqlParameter("@nEmpID", SqlDbType.Int);
p3.SourceVersion = DataRowVersion.Original;
p3.SourceColumn = "employeeid";
da.UpdateCommand.Parameters.Add(p3);
</PRE>
<P>Finally, the DELETE command requires a WHERE clause to identify the row to remove. In this case, you need to use the original version of the row to bind the parameter value.</P><PRE class=code>StringBuilder sb = new StringBuilder("");
sb.Append("DELETE FROM Employees ");
sb.Append("WHERE employeeid=@nEmpID");
da.DeleteCommand = new SqlCommand();
da.DeleteCommand.CommandText = sb.ToString();
da.DeleteCommand.Connection = conn;
p1 = new SqlParameter("@nEmpID", SqlDbType.Int);
p1.SourceVersion = DataRowVersion.Original;
p1.SourceColumn = "employeeid";
da.DeleteCommand.Parameters.Add(p1);
</PRE>
<P>The actual structure of the SQL commands is up to you. They don't need to be plain SQL statements, and can be more effective stored procedures if you want to go that direction. If there's a concrete risk that someone else could have updated the row that you read and modified, then you might want to take some more effective counter-measures. If this were the case, you could use a more restrictive WHERE clause on the DELETE and UPDATE commands. The WHERE clause could unequivocally identify the row, but also make sure that all the columns still hold the original value.</P><PRE class=code>UPDATE Employees 
   SET field1=@new_field1, field2=@new_field2, Ã‚Â…, fieldn=@new_fieldn
   WHERE field1=@old_field1 AND 
      field2=@old_field2 AND
      :
      fieldn=@old_fieldn
</PRE>
<P>Notice that you don't need to fill all command properties, but only those that you plan to use. If the code happens to use a command that has not been specified, an exception is thrown. Setting up the commands for a batch update process may require a lot of code, but you don't need to do it each and every time you batch update. In a fair number of cases, in fact, ADO.NET is capable of automatically generating effective update commands for you. </P>
<H2 class=dtH1>Command Builders</H2>
<P>To utilize default commands, you have to fulfill two requirements. First off, you must assign a valid command object to the <B>SelectCommand</B> property. You don't need to populate other command objects, but <B>SelectCommand</B> must point to a valid query statement. A valid query for the batch update is a query that returns a primary key column. In addition, the query must not include INNER JOIN, calculated columns, and reference multiple tables.</P>
<P>The columns and the table listed in the <B>SelectCommand</B> object will actually be used to prepare the body of the update and insert statements. If you don't set <B>SelectCommand</B>, then ADO.NET command auto-generation cannot work. The following code shows how to code the <B>SelectCommand</B> property. </P><PRE class=code>SqlCommand cmd = new SqlCommand();
cmd.CommandText = "SELECT employeeid, firstname, lastname FROM Employees";
cmd.Connection = conn;
da.SelectCommand = cmd;
</PRE>
<P>Don't worry about the possible impact that <B>SelectCommand</B> may have on performance. The related statement executes only once prior to the batch update process, but it only retrieves column metadata. No matter how you write the SQL statement, no rows will ever be returned to the caller program. This happens because at execution time, the <B>SelectCommand</B> is appended to a SQL batch statement that begins with </P><PRE class=code>SET FMTONLY OFF
SET NO_BROWSETABLE ON
SET FMTONLY ON
</PRE>
<P>As a result, the query does not return rows, but rather column metadata information.</P>
<P>The second requirement your code must fulfill regards command builders. A command builder is a managed provider-specific class that works atop the data adapter object and automatically sets its <B>InsertCommand</B>, <B>DeleteCommand</B>, and <B>UpdateCommand</B> properties. A command builder first runs <B>SelectCommand</B> to collect enough information about the involved tables and columns, and then creates the update commands. The actual commands creation takes place in the command builder class constructor.</P><PRE class=code>SqlCommandBuilder cb = new SqlCommandBuilder(da);
</PRE>
<P>The SqlCommandBuilder class ensures that the specified data adapter can be successfully used to batch update the given data source. The SqlCommandBuilder utilizes some of the properties defined in the <B>SelectCommand</B> object. They are <B>Connection</B>, <B>CommandTimeout</B>, and <B>Transaction</B>. Whenever any of these properties is modified, you need to call the command builder's <B>RefreshSchema</B> method to change the structure of the generated commands of further batch updates. </P>
<P>You can mix together command builders and handcrafted commands. If the <B>InsertCommand</B> property points to a valid command object prior to calling the command builder, then the builder would generate only the code for <B>DeleteCommand</B> and <B>UpdateCommand</B>. A non-null <B>SelectCommand</B> property, instead, is key for command builders to work. </P>
<P>Typically, you use command builders because you don't want to cope with the intricacies of SQL commands. However, if you want to have a look at the source code generated by the builders, you can call methods like <B>GetInsertCommand</B>, <B>GetUpdateCommand</B>, and <B>GetDeleteCommand</B>.</P>
<P>Command builders are a provider-specific feature. So, you should not expect to find them supported by all types of managed providers. They work with SQL Server 7.0 and higher and OLE DB providers.</P>
<P>A nice feature of command builders is that they can detect auto-increment fields and properly tune up the code. In particular, they would take auto-increment fields out of the INSERT statement as long as they have the means to recognize certain fields as auto-increment. This can be done in two ways. For example, you could manually set the <B>AutoIncrement</B> property of the corresponding <B>DataColumn</B> object, or, better yet, have this happen automatically based on the attributes that the column has in a data source like SQL Server. To automatically inherit such an attribute, make sure you change the <B>MissingSchemaAction</B> property of the data adapter from the default value of <B>Add</B> to <B>AddWithKey</B>. </P>
<H2 class=dtH1>Conflicts Detection</H2>
<P>The batch update mechanism is based on an optimistic vision of concurrency. Each record is not locked after being read and remains exposed to other users for reading and writing. In this scenario, a number of potentially inconsistent situations can occur. For example, a row could have been modified, or even deleted, after it was handed to your application from a SELECT statement, but before a batch update process actually changes it back to the server.</P>
<P>If you update data on the server that has been modified in the meantime by some other user, you may raise a data conflict. To avoid new data being overwritten, the ADO.NET command builders generate statements with a WHERE clause that works only if the current state of the data source row is consistent with what the application previously read . If such a command fails updating the row, the ADO.NET runtime throws an exception of type <B>DBConcurrencyException</B>. </P>
<P>The following code snippet demonstrates a more accurate way to execute a batch update operation with ADO.NET.</P><PRE class=code>try 
{
   da.Update(dsChanges, "Employees");
}
catch (DBConcurrencyException dbdcex)   
{
   // resolve the conflict
}
</PRE>
<P>The <B>Update</B> method of the data adapter you are using throws the exception for the first row where the update fails. At this time, the control passes back the to client application and the batch update process is stopped. However, all previously submitted changes are committed. This represents another shift from the ADO batch update model.</P>
<P>The <B>DataRow</B> object involved in the conflicted update is made available through the <B>Row</B> property of the DBConcurrencyException class. This <B>DataRow</B> object contains both the proposed and the original value of the row. It does not contain the value currently stored in the database for a given column. This value—the <B>UnderlyingValue</B> property of ADO—can only be retrieved with another query command. </P>
<P>The way in which the conflict is resolved, and the batch update possibly resumed, is strictly application-specific. If there is a situation in which your application needs to resume the update, then be aware of a subtle, yet tricky problem. Once the conflict on the row has been solved in one way or another, you still must figure out a way to accept the changes on the in-memory rows for which batch update completed successfully. If you neglect this technicality, then a new conflict will be raised for the first row that was previously and successfully updated! This will happen over and over again, heading your application straight into a joyful deadlock.</P>
<H2 class=dtH1>Summary</H2>
<P>Compared to ADO, batch update in ADO.NET is more powerful and accessible. In ADO, the batch update mechanism was a sort of black box with rare chances for you to plug into it and change what you needed to do in a slightly different way. Batch update in ADO.NET is more of a low-level solution, and its implementation provides several points where you can get in and take control of the events. The trickiest part of ADO.NET batch update is conflict resolution. I heartily suggest you spend as much time as possible testing and retesting. This investment will payoff with all the time you save with command builders. </P>
<P>
<TABLE class=dtTABLE cellSpacing=2 cellPadding=2 bgColor=#eeeee border=1 frame=box><FONT size=-2>
<TBODY>
<TR>
<TD>
<H3>Dialog Box: Null Values in Data Tables</H3>
<P><B>I fetch a DataSet out of a database and I am happy. Then I try to save this DataSet into an XML file and I am still happy. But when I read this XML file back into a DataSet, my happiness ends. This is because all the columns with a NULL value are not persisted to XML. Is there a way by which NULL values get added as empty tags to the resultant XML?</P></B>
<P>The behavior is by design and introduced with the best intentions to save a few bytes during the XML serialization process. If this happens over the network (say, within an XML Web service) the advantage might be significant. </P>
<P>That said, your problem has a very simple workaround. The trick is fetching the column through the <B>ISNULL</B> T-SQL function. Instead of using:</P><PRE class=clsCode>SELECT MyColumn FROM MyTable
</PRE>
<P>You should resort to:</P><PRE class=clsCode>SELECT ISNULL(MyColumn, '') FROM MyTable
</PRE>
<P>In this case, any NULL value for the column is automatically turned into an empty string and not ignored during the DataSet-to-XML serialization process. The neutral value does not necessarily have to be the empty string. Numeric columns can use 0 or any other logically null value you want to use.</P></FONT></TD></TR></TBODY></TABLE>
<HR noShade SIZE=1>
<B>Dino Esposito</B> is <A href="http://www.wintellect.com/">Wintellect</A>'s ADO.NET expert and a trainer and consultant based in Rome, Italy. Dino is a contributing editor to MSDN Magazine and writes the Cutting Edge column. He also regularly contributes to <A href="http://www.dnjonline.com/">Developer Network Journal</A> and MSDN News. Dino is the author of the upcoming <I>Building Web Solutions with ASP.NET and ADO.NET</I> from Microsoft Press, and the cofounder of <A href="http://www.vb2themax.com/">http://www.vb2themax.com/</A>. You can reach Dino at <A href="mailto:dinoe@wintellect.com">dinoe@wintellect.com</A>.</FONT><img src ="http://www.blogjava.net/jinfeng_wang/aggbug/27476.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jinfeng_wang/" target="_blank">jinfeng_wang</a> 2006-01-10 22:23 <a href="http://www.blogjava.net/jinfeng_wang/archive/2006/01/10/27476.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Inside .NET Managed Providers (zz from msdn)</title><link>http://www.blogjava.net/jinfeng_wang/archive/2006/01/10/27474.html</link><dc:creator>jinfeng_wang</dc:creator><author>jinfeng_wang</author><pubDate>Tue, 10 Jan 2006 14:15:00 GMT</pubDate><guid>http://www.blogjava.net/jinfeng_wang/archive/2006/01/10/27474.html</guid><wfw:comment>http://www.blogjava.net/jinfeng_wang/comments/27474.html</wfw:comment><comments>http://www.blogjava.net/jinfeng_wang/archive/2006/01/10/27474.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jinfeng_wang/comments/commentRss/27474.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jinfeng_wang/services/trackbacks/27474.html</trackback:ping><description><![CDATA[<DIV id=nsbanner>
<DIV id=TitleRow>
<H1 class=dtH1><A name=data010112001></A>Inside .NET Managed Providers</H1></DIV></DIV><!--NONSCROLLING BANNER END-->
<P>Dino Esposito<BR>Wintellect</P>
<P>October 9, 2001</P>
<P>When compared to full-fledged OLE DB providers, Microsoft .NET managed providers have a lot to offer. First off, they deliver a simplified data access architecture that often results in improved performance without the loss of functional capabilities. Furthermore, .NET managed providers directly expose provider-specific behavior to consumers through methods and properties. They also involve a much smaller set of interfaces than OLE DB providers. Last but not least, .NET managed providers work within the boundaries of the Common Language Runtime (CLR) and require no COM interaction. For SQL Server 7.0 and SQL Server 2000, the managed provider hooks up directly to the wire level, gaining a substantial performance advantage. </P>
<P>The functionalities that a .NET data provider supplies may fall into a couple of categories: 
<UL type=disc>
<LI>Support of the <I>DataSet</I> class through an implementation of the methods of the <B>IDataAdapter</B> interface 
<LI>Support of connected data access, which includes classes representing connections, commands, and parameters </LI></UL>
<P>The simplest flavor of a data provider interacts with callers only through the DataSet, both in reading and writing. In the other case, you can control connections, transactions, and execute direct commands, regardless of the SQL language. The figure below shows the class hierarchy of the two standard managed providers in .NET—OLE DB providers and for SQL Server.</P>
<P class=fig><IMG alt="" src="http://msdn.microsoft.com/library/en-us/dndive/html/data10112001-fig1.gif" border=0></P>
<P class=label><B>Figure 1. Managed providers connect, execute commands, and get data in a data source-specific way.</B></P>
<P>The objects that wrap connections, commands, and readers are provider-specific and may result in a slightly different set of properties and methods. Any internal implementation is rigorously database-aware. The only class that is out of this schema is the DataSet. The class is common to all providers and works as a generic container for disconnected data. The DataSet class belongs to a kind of super-namespace called <B>System.Data</B>. Classes specific of a data provider belong to the specific namespace. For example, <B>System.Data.SqlClient</B> and <B>System.Data.OleDb </B>belong to a specific namespace. The schema in the figure above is rather basic, though not simplistic. It is simplified because it does not include all the classes and the interfaces involved. The figure below is a bit more accurate.</P>
<P class=fig><IMG alt="" src="http://msdn.microsoft.com/library/en-us/dndive/html/data10112001-fig2.gif" border=0></P>
<P class=label><B>Figure 2. Classes involved with a managed provider</B></P>
<P>The table below shows the list of the interfaces that make a .NET provider. </P>
<TABLE class=data>
<TBODY>
<TR vAlign=top>
<TH class=data align=left width="20%">Interface </TH>
<TH class=data align=left width="80%">Description</TH></TR>
<TR vAlign=top>
<TD class=data width="20%">IDbConnection</TD>
<TD class=data width="80%">Represents a unique session with a data source</TD></TR>
<TR vAlign=top>
<TD class=data width="20%">IDbTransaction</TD>
<TD class=data width="80%">Represents a local, non distributed, transaction</TD></TR>
<TR vAlign=top>
<TD class=data width="20%">IDbCommand</TD>
<TD class=data width="80%">Represents a command that executes when connected to a data source</TD></TR>
<TR vAlign=top>
<TD class=data width="20%">IDataParameter</TD>
<TD class=data width="80%">Allows implementation of a parameter to a command</TD></TR>
<TR vAlign=top>
<TD class=data width="20%">IDataReader</TD>
<TD class=data width="80%">Reads a forward-only, read-only stream of data created after the execution of a command</TD></TR>
<TR vAlign=top>
<TD class=data width="20%">IDataAdapter </TD>
<TD class=data width="80%">Populates a DataSet and resolves changes in the DataSet back to the data source</TD></TR>
<TR vAlign=top>
<TD class=data width="20%">IDbDataAdapter</TD>
<TD class=data width="80%">Supplies methods to execute typical operations on relational databases (insert, update, select, delete)</TD></TR></TBODY></TABLE>
<P>Of all of interfaces, only <B>IDataAdapter</B> is mandatory and must be present in every managed provider. If you don't plan to implement one of the interfaces, or one method of a given interface, expose the interface anyway, but throw a <B>NotSupportedException</B> exception. Wherever possible, avoid providing no-op implementations of methods and interfaces as this may result in data corruption, particularly with the commit/rollback of transactions. For example, providers are not required to support nested transactions even though the <B>IDbTransaction</B> interface is designed to allow also for this situation. </P>
<P>Before going any further with the explanation of the role that each class plays in the overall workings of a .NET provider, let me say a few words about the naming convention that a managed provider is recommended to utilize. This is useful if you happen to write your own providers. The first guideline regards the namespace. Make sure you assign your own managed provider a unique namespace. Next, prefix classes with a nickname that identifies the provider throughout any internal and client code. For example, use class names like OdbcConnection, OdbcCommand, OdbcDataReader, and so on. In this case, the nickname is Odbc. In addition, try to use distinct files to compile distinct functionalities. </P>
<H2 class=dtH1>Implementing a Connection</H2>
<P>The provider connection class inherits from <B>IDbConnection</B> and must expose the <B>ConnectionString</B>, <B>State</B>, <B>Database</B>, and <B>ConnectionTimeout </B>properties. The mandatory methods are <B>Open</B>, <B>Close</B>, <B>BeginTransaction</B>, <B>ChangeDatabase</B>, and <B>CreateCommand</B>. You are not strictly required to implement transactions. The following code snippet gives you an idea of the code used to implement a connection.</P><PRE class=code>namespace DotNetMyDataProvider {
  public class MyConnection : IDbConnection
  {
    private ConnectionState m_state;
    private String m_sConnString;

    public MyConnection () {
m_state = ConnectionState.Closed;
m_sConnString = "";
    }
    public MyConnection (String connString) {
m_state = ConnectionState.Closed;
m_sConnString = connString;
    }
    public IDbTransaction BeginTransaction() {
      throw new NotSupportedException();
    }
    public IDbTransaction BeginTransaction(IsolationLevel level) {
      throw new NotSupportedException();
    }
 }
}
</PRE>
<P>You should provide at least two constructors, one being the default that takes no argument. The other recommended constructor would accept only the connection string. When returning the connection string through the <B>ConnectionString</B> property, make sure you always return exactly what the user set. The only exception might be constituted by any security-sensitive information that you might want to remove.</P>
<P>The items you recognize and support in the connection string are up to you, but standard names should be used whenever it makes sense. The <B>Open</B> method is responsible for opening the physical channel of communication with the data source. This should happen not before the <B>Open</B> method is called. Consider using some sort of connection pooling if opening a connection turns out to be an expensive operation. Finally, if the provider is expected to provide automatic enlistment in distributed transactions, the enlistment should occur during <B>Open</B>.</P>
<P>An important point that makes ADO.NET connections different from, say, ADO connections, is that you are requested to guarantee that a connection is created and opened before any command can be executed. Clients have to explicitly open and close connections, and no method will open and close connections implicitly for the client. This approach leads to a sort of centralization of security checks. In this way, checks are performed only when the connection is obtained, but the benefits apply to all other classes in the provider that happen to work with connection objects.</P>
<P>You close the connection with the method <B>Close</B>. In general, <B>Close</B> should simply detach the connection and return the object to the pool, if there is a pool. You could also implement a <B>Dispose</B> method to customize the destruction of the object. The state of a connection is identified through the <B>ConnectionState</B> enum data type. While the client works over the connection, you should ensure that the internal state of the connection matches the contents of the <B>State</B> property. So, for instance, when you are fetching data, set the connection's <B>State</B> property to ConnectionState.Fetching.</P>
<H2 class=dtH1>The ODBC Connection </H2>
<P>Let's see how a concrete .NET managed provider turns these principles into practice. For this example, I'll take into account the newest managed provider that appeared, though only in early beta releases. I'm talking about the .NET provider for ODBC data sources. You probably noticed already that the .NET provider for OLE DB does not support the DSN token in the connection string. Such a name is required to automatically select the MSDASQL provider and go through ODBC sources. The following code is how ODBC.NET declares its connection class:</P><PRE class=code>public sealed class OdbcConnection : Component, 
ICloneable, IdbConnection
</PRE>
<P>The <B>OdbcConnection</B> object utilizes ODBC-typical resources, such as environment and connection handles. These objects are stored internally using class private members. The class provides for both <B>Close</B> and <B>Dispose</B>. In general, you can close a connection with either method, but do it before the connection object goes out of scope. Otherwise, the freeing of internal memory (that is, ODBC handles) is left to the garbage collector, whose timing you cannot control. For connection pooling, the OdbcConnection class relies on the services of the ODBC Driver Manager.</P>
<P>To play with the ODBC.NET provider (currently in beta 1), you should include <B>System.Data.Odbc</B>. At this time, the provider is guaranteed to work with drivers for JET, SQL Server, and Oracle.</P>
<H2 class=dtH1>Implementing a Command</H2>
<P>The command object formulates a request for some actions and passes it on to the data source. If results are returned, the command object is responsible for packaging and returning results as a tailored <B>DataReader</B> object, a scalar value, and/or through output parameters. According to the special features of your data provider, you can arrange results to appear in other formats. For example, the managed provider for SQL Server lets you obtain results in XML format if the command text includes the FOR XML clause.</P>
<P>The class must support at least the <B>CommandText</B> property and at least the text command type. Parsing and executing the command is up to the provider. This is the key aspect that makes it possible for a provider to accept any text or information as a command. Supporting command behaviors is not mandatory and, if needed, you can support more, completely custom behaviors. </P>
<P>Within a command, the connection can be associated with a transaction. If you reset the connection—and users should be able to change the connection at any time—then first null out the corresponding transaction object. If you support transactions, then when setting the <B>Transaction</B> property of the command object, consider additional steps to ensure that the transaction you're using is already associated with the connection the command is using. </P>
<P>A command object works in conjunction with two classes representing parameters. They are xxxParameterCollection, which is accessed through the <B>Parameters</B> property, and xxxParameter, which represents a single command parameter stored in the collection. Of course, the <I>xxx</I> stands for the provider-specific nickname. For ODBC.NET, they are OdbcParameterCollection and OdbcParameter.</P>
<P>You create provider-specific command parameters using the <B>new</B> operator on the parameter class or through the <B>CreateParameter</B> method of the command object. Newly created parameters are populated and added to the command's collection through the methods of the Parameters collection. The module that provides for command execution is then responsible for collecting data sets through parameters. Using named parameters (as the SQL Server provider does) or the ? placeholder (similar to the OLE DB provider) is up to you. </P>
<P>You must have a valid and open connection to execute commands. Execute the commands using any of the standard types of commands, which are ExecuteNonQuery, ExecuteReader, and ExecuteScalar. Also, consider providing an implementation for the <B>Cancel</B> and <B>Prepare</B> methods. </P>
<H2 class=dtH1>The ODBC Command </H2>
<P>The OdbcCommand class does not support passing named parameters with SQL commands and stored procedures. You must resort to the ? placeholder instead. At least in this early version, it does not support <B>Cancel</B> and <B>Prepare</B> either. As you can expect, the ODBC .NET provider requires that the number of command parameters in the <B>Parameters</B> collection matches the number of placeholders found within the command text. Otherwise, an exception is thrown. The line below shows how to add a new parameter to an ODBC command and assign it at the same time.</P><PRE class=code>cmd.Parameters.Add("@CustID", OdbcType.Integer).Value = 99
</PRE>
<P>Notice that the provider defines its own set of types. The enumeration <B>OdbcType</B> includes all and only the types that the low-level API of ODBC can safely recognize. There is a close match between the original ODBC types, such as SQL_BINARY, SQL_BIGINT, or SQL_CHAR, and the .NET types. In particular, the ODBC type SQL_CHAR maps to the .NET String type.</P>
<H2 class=dtH1>Implementing a DataReader</H2>
<P>A data reader is a kind of connected, cache-less buffer that the provider creates to let clients read data in a forward-only manner. The actual implementation of the reader is up to the provider's writer. However, a few guidelines should be taken into careful account. </P>
<P>First off, when returned to the user, the <B>DataReader</B> object should always be open and positioned prior to the first record. In addition, users should not be able to directly create a <B>DataReader</B> object. Only the command object must create and return a reader. For this reason, you should mark the constructors as internal. You should use the keyword <I>internal</I> in C# </P><PRE class=code>internal MyDataReader(object resultset)
{...}
</PRE>
<P>and the keyword <I>friend</I> in Visual Basic® .NET</P><PRE class=code>Friend Sub New(ByRef resultset As object)
      MyBase.New
      ...
End Sub
</PRE>
<P>The DataReader must have at least two constructors—one taking the result set of the query, and one taking the connection object used to carry the command out. The connection is necessary only if the command must execute with the <B>CommandBehavior.CloseConnection</B> style. In this case, the connection must be automatically closed when the <B>DataReader</B> object is closed. Internally, the resultset can take any form that serves your needs. For example, you can implement it as an array or a dictionary.</P>
<P>A DataReader should properly manage the property <B>RecordsAffected</B>. It is only applicable to batch statements that include inserts, updates, or deletes. It normally does not apply to query commands. When the reader is closed, you might want to disallow certain operations and change the reader's internal state, cleaning up internal resources like the array used to store data. </P>
<P>The DataReader's <B>Read</B> method always moves forward to a new valid row, if any. More importantly, it should only place the internal data pointer forward, but makes no reading. The actual reading takes place with other reader-specific methods, such as <B>GetString</B> and <B>GetValues</B>. Finally, <B>NextResult</B> moves to the next result set. Basically, it copies a new internal structure into a common repository from which methods like <B>GetValues</B> read.</P>
<H2 class=dtH1>The ODBC DataReader</H2>
<P>As all the reader classes, OdbcDataReader is sealed and not inheritable. Methods of the class that have access to column values automatically coerce the type of data they return to the type of data that was initially retrieved from that column. The type used the first time to read one cell from a given column is used for all the other cells of the same column. In other words, you cannot read data from the same column as string and long in successive times. </P>
<P>When the <B>CommandType</B> property of a command object is set to <B>StoredProcedure</B>, the <B>CommandText</B> property must be set using the standard ODBC escape sequence for procedures. Unlike other providers, the simple name of the procedure is not enough for the ODBC.NET provider. The following pattern represents the typical way of calling stored procedures through ODBC drivers.</P><PRE class=code>{ call storedproc_name(?, ..., ?) }
</PRE>
<P>The string must be wrapped by {...} and have the keyword call to precede the actual name and the list of parameters.</P>
<H2 class=dtH1>Implementing a DataAdapter</H2>
<P>A full-fledged .NET data provider supplies a data adapter class that inherits both <B>IDbDataAdapter</B> and <B>DbDataAdapter</B>. The class DbDataAdapter implements a data adapter designed for use with a relational database. In other cases, though, what you need is a class that implements the <B>IDataAdapter</B> interface and copies some disconnected data to an in-memory programmable buffer like the DataSet. Implementing the <B>Fill</B> method of the <B>IDataAdapter</B> interface, in fact, is in most cases sufficient to return disconnected data through a DataSet object. </P>
<P>Typical constructors for the <B>DataAdapter</B> object are:</P><PRE class=code>XxxDataAdapter(SqlCommand selectCommand) 
XxxDataAdapter(String selectCommandText, String selectConnectionString) 
XxxDataAdapter(String selectCommandText, SqlConnection selectConnection)
</PRE>
<P>Classes that inherit from DbDataAdapter must implement all the members, and define additional members in case of provider-specific functionality. This ends up requiring the implementation of the following methods:</P><PRE class=code>Fill(DataSet ds)
FillSchema(DataSet ds, SchemaType st)
Update(DataSet ds)
GetFillParameters()
</PRE>
<P>The required properties are: 
<UL type=disc>
<LI><B>TableMappings</B> (which defaults to the empty collection) 
<LI><B>MissingSchemaAction </B>(which defaults to <B>Add</B>) 
<LI><B>MissingMappingAction </B>(which defaults to <B>Passthrough</B>) </LI></UL>
<P>You can provide as many implementations of the <B>Fill</B> method as needed. </P>
<P>Table mappings govern the way in which source tables (that is, database tables) are mapped to DataTable objects in the parent DataSet. Mappings take into account table names as well as columns names and properties. Schema mapping, instead, regards the way in which columns and tables are treated when it comes to adding new data to existing DataSets. The default value for the missing mapping property tells the adapter to create in-memory tables that looks like source tables. The default value for the missing schema property handles possible issues that arise when the DataTable objects are actually populated. If any of the mapped elements (tables and columns) are missing in the target DataSet, then the value of MissingSchemaAction suggests what to do. In a certain way, both <B>MissingXXX</B> properties are a kind of exception handler. The value <B>Add</B> forces the adapter to add any table or column that proves to be missing. No key information is added unless another (AddWithKey) value is assigned to the property.</P>
<P>When an application calls the <B>Update</B> method, the class examines the <B>RowState</B> property for each row in the DataSet and executes the required INSERT, UPDATE, or DELETE statement. If the class does not provide <B>UpdateCommand</B>, <B>InsertCommand</B>, or <B>DeleteCommand</B> properties, but implements <B>IDbDataAdapter</B>, then you can try to generate commands on the fly or raise an exception. You could also provide a made-to-measure command builder class to help with the command generation.</P>
<P>The ODBC provider supplies the <I>OdbcCommandBuilder </I>class as a means of automatically generating single-table commands. The OLE DB and SQL Server providers have provided similar classes. If you need to update cross-referenced tables, then you might want to use stored procedures or ad-hoc SQL batches. In this case, just override the <B>InsertCommand</B>, <B>UpdateCommand</B>, and <B>DeleteCommand</B> properties to make them run the command object you indicate.</P>
<H2 class=dtH1>Summary</H2>
<P>The functionality that a .NET data provider offers can be divided into two main categories: 
<UL type=disc>
<LI>Support for the disconnected DataSet object 
<LI>Support for connected data access, including connections, transactions, commands, and parameters </LI></UL>
<P>Data providers in .NET support DataSet objects through an implementation of the <B>IDataAdapter</B> interface. They may also support parameterized queries by implementing the <B>IDataParameter</B> interface. If you can't afford disconnected data, then use .NET data readers through the <B>IDataReader</B> interface. </P>
<TABLE class=dtTABLE cellSpacing=2 cellPadding=2 bgColor=#eeeee border=1 frame=box><FONT size=-2>
<TBODY>
<TR>
<TD>
<H3>Dialog Box: Naming Multiple Resultsets</H3>
<P><B>Visual Studio® .NET has a very nice feature that lets you assign a consistent name to all the tables a data adapter is going to generate. After you've configured a data adapter object in any .NET application, the dialog shows the standard names of the tables being created: Table, Table1, Table2, and so forth. For each of them, you can then specify in a single shot a more evocative name. Is there a way to get this programmatically?</B></P>
<P>Visual Studio .NET is an excellent product, but there's only a little bit of magic in what it does. To answer your question, yes, there is a way to obtain that programmatically and, incidentally, it's the same code that Visual Studio utilizes behind the scenes. </P>
<P>The <B>DataAdapter</B> object has a collection called <B>TableMappings </B>whose elements are objects of type DataTableMapping. What's a table mapping anyway? It is a dynamic association set between a source table and the corresponding DataTable object that the adapter is going to create. If no mapping has been set, then the adapter creates a DataTable object with the same structure as the source table, except for the name. The name is the string specified through the call to the <B>Fill</B> method, or the word <B>Table</B>. Extra tables that originate from multiple resultsets are named after the first. So, in the default case, they are called Table1, Table2, and the like. Instead, if the data adapter is filled out like the code below, then the extra tables are named Employees1, Employees2, and so forth.</P><PRE class=clsCode>myDataAdapter.Fill(myDataSet, "Employees");
</PRE>
<P>What Visual Studio does when you configure your data adapter is create one <B>DataTableMapping</B> object for each association you visually create. The following lines of code are the programmatic way to assign meaningful names to the first two tables of a DataSet filled as above. </P><PRE class=clsCode>myDataAdapter.TableMappings.Add("Employees", "FirstTable");
myDataAdapter.TableMappings.Add("Employees1", "SecondTable");
</PRE>
<P>A third table, if any, could be accessed through Table2. </P>While this is the most elegant way to name DataTable objects that originate from multiple resultsets, nothing prevents you from using the following, equally effective, code: 
<P></P><PRE class=clsCode>myDataAdapter.Fill(myDataSet, "Employees");
myDataSet.Tables["Employees1"].TableName = "SecondTable";
</PRE>
<P>You could also access the table through the index:</P><PRE class=clsCode>myDataSet.Tables[1].TableName = "SecondTable";
</PRE></FONT></TD></TR></TBODY></TABLE>
<P>&nbsp;</P>
<HR noShade SIZE=1>

<P class=clsMagFooter><B>Dino Esposito</B> works for <A href="http://www.wintellect.com/" target=_top>Wintellect</A>, where he takes on ADO.NET and ASP.NET training and consulting. He is the co-founder of <A href="http://www.vb2themax.com/" target=_top>VB-2-The-Max</A>, and also contributes the Cutting Edge column to MSDN Magazine. You can reach Dino at <A href="mailto:dinoe@wintellect.com">dinoe@wintellect.com</A>.</P></FONT><img src ="http://www.blogjava.net/jinfeng_wang/aggbug/27474.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jinfeng_wang/" target="_blank">jinfeng_wang</a> 2006-01-10 22:15 <a href="http://www.blogjava.net/jinfeng_wang/archive/2006/01/10/27474.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Building a custom data provider for .Text (a custom ASP.NET blogging application) (zz)</title><link>http://www.blogjava.net/jinfeng_wang/archive/2006/01/10/27473.html</link><dc:creator>jinfeng_wang</dc:creator><author>jinfeng_wang</author><pubDate>Tue, 10 Jan 2006 14:14:00 GMT</pubDate><guid>http://www.blogjava.net/jinfeng_wang/archive/2006/01/10/27473.html</guid><wfw:comment>http://www.blogjava.net/jinfeng_wang/comments/27473.html</wfw:comment><comments>http://www.blogjava.net/jinfeng_wang/archive/2006/01/10/27473.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jinfeng_wang/comments/commentRss/27473.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jinfeng_wang/services/trackbacks/27473.html</trackback:ping><description><![CDATA[<DIV class=postTitle>Whether you love or loathe blogging, it has grown extremely popular. Many sites and applications that have been created for blogging in an attempt to fulfill this new demand. One such application is <A href="http://scottwater.com/blog/">.Text</A>, written by Scott Watermasysk. </DIV>
<DIV class=postText>
<P>By default .Text uses SQL Server for its database back end. However, .Text was <A href="http://dottextwiki.scottwater.com/default.aspx/Dottext.DataAccess">designed with a provider model to support different database back ends</A>. In 2003, <A href="http://blogs.borland.com/johnk/">John Kaster</A> presented <A href="http://www.borland.com/delphi_net/index.html">Delphi 8</A> to our <A href="http://www.slcdug.org/">local users group</A>. Afterwards he issued a challenge, and I accepted. The challenge was to support InterBase with .Text. The result is <A href="http://blogs.borland.com/">blogs.borland.com</A> and <A href="http://blogs.teamb.com/">blogs.teamb.com.</A> </P>
<P>.Text provides two interfaces to make replaceable database backends possible.</P>
<UL>
<LI>Dottext.Framework.Data.IDbProvider 
<LI>Dottext.Framework.Data.IDTOProvider </LI></UL>
<H2>Dottext.Framework.Data.IDbProvider</H2>
<P>This interface defines approximately 65 methods needed to select, insert, update, and delete records for various tables in the database. Data is retrieved from <CODE>select</CODE> statements using standard ADO.NET Interfaces and classes. The <A href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfsystemdataidatareaderclasstopic.asp">IDataReader</A> interface is returned for many of the select statements. The rest are returned in a <A href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfSystemDataDataSetClassTopic.asp">DataSet</A>.</P>
<H2>Dottext.Framework.Data.IDTOProvider</H2>
<P>For the most part .Text does not directly access data that is returned from a class that implements Dottext.Framework.Data.IDbProvider. Instead it calls a class that implements Dottext.Framework.Data.IDTOProvider, .Text provides a default implementation of this class which it uses for SQL Server called Dottext.Framework.Data.DataDTOProvider. It is responsible for taking the raw data and mapping it into objects that .Text has defined for each type of data.</P>
<H2>Implementing the Interfaces</H2>
<P>After converting the database schema to InterBase, (Minus the 60+ stored procedures that .Text has for its MS SQL Server implementation) I started by implementing a custom IDbProvider in Delphi for .NET. This provider Implemented all of the database calls with SQL statements instead of using stored procedures.<BR></P>
<P>One problem I ran into was the way .Text used several procedures that returned multiple cursors. The only way to duplicate this behavior for InterBase was to fill a DataSet using Multiple SQL Statements. </P><PRE class=sourcecode><CODE><BR><B>var</B><BR>   Cmd : BDPCommand;<BR>   DA   : BDPDataAdapter;<BR>   ResultData : DataSet;<BR><B>begin</B><BR>   ResultData := DataSet.Create;<BR>...<BR>   Cmd.CommandText := <FONT color=#9933cc>'SELECT * FROM TABLEA'</FONT>;<BR>...<BR>   DA := BdpDataAdapter.Create(Cmd);<BR>   DA.Fill(ResultData);<BR>   DA.Free;<BR>...<BR>   Cmd.CommandText := <FONT color=#9933cc>'SELECT * FROM TABLEB'</FONT>;<BR>...<BR>   DA := BdpDataAdapter.Create(Cmd);<BR>   DA.Fill(ResultData.Tables.Add);<BR>   DA.Free;<BR>...<BR><B>end</B>;<BR></CODE></PRE>
<P>I also created a new class that implemented the Dottext.Framework.Data.IDTOProvider using C#, to address data transformation issues. I used C# for this class as it was almost the same as the original one provided by .Text so I able to copy, rename, modify instead of writing it from scratch.<BR></P>
<H2>Database Connections</H2>BDP does not currently implement connection pooling, so I needed a custom solution. For performance reasons I could not have each page request connecting to the database. Instead I opted to create a unique Database Connection per thread. With Delphi this was quite easy to do, we placed our connection variable in a threadvar section, then we always accessed our database connection through a function that would check to see if the database was created if it was not created it would create it. <PRE class=sourcecode><CODE><B>function</B> BdpDataProvider.GetDbConnection: BdpConnection;<BR><B>begin</B><BR>  <B>if</B> <B>Not</B> Assigned(FConnection) <B>then</B><BR>     FConnection := BdpConnection.Create(ConnectionString);<BR><B>end</B>;<BR></CODE></PRE>
<P class=technote>The number of possible page request threads your ASP.NET application can have is partially controlled by how you have <A href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpgenref/html/gngrfProcessmodelSection.asp">configured your system</A>, and also is further controlled by how you have <A href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpgenref/html/gngrfhttpruntimesection.asp">configured you application</A>. The following article explains many of the details on how <A href="http://msdn.microsoft.com/msdnmag/issues/03/06/Threading/default.aspx">threading works in ASP.NET</A>.<BR></P>
<H2>Closing the BdpCommand</H2>When working BDP you must remember to always call close on the BdpCommand. SqlClient does not seem to have this requirement most of the original .Text DTO provider code never called close on the Command, but I have heard from other users of .Text that their servers might run more reliably if the commands were closed in the .Text code. During during the testing, I found myself on a bug hunt, making sure every command was closed.<BR>
<P class=technote>Specifically remember to call close in the correct order.<BR><BR>BdpReader.Close;<BR>BdpCommand.Close;<BR>BdpConnection.Close;<BR></P>
<H2>Building the Code </H2>
<P>.Text was written with C# in Visual Studio 2003. My IDBProvider was written in Delphi for .NET. Initially, to compile the application, I had to first compile the .Text Solution in Visual Studio. Then I used Delphi 8 for .Net to compile the Provider. .Text does not know about the Delphi Assembly at compile time: it dynamically loads the provider using a value stored in the web.config file. Initially, I was unsure of what to expect when debugging my Delphi Assembly when a Visual Studio application was the main project. I found out that as long as I include debug information, it is possible to step through your Delphi code in Visual Studio. </P>
<P>Now that <A href="http://www.borland.com/delphi">Delphi 2005</A> that combines both Delphi and C#, we were able to use the Visual Studio project Import wizard to import the existing projects. After setting up all the projects into a single Project Group and I am able to compile the entire blogging application inside Delphi, with no need to use Visual Studio at all on the project. </P>
<P>In addition to writing a custom provider, I modified the security system in .Text so that Borland Employees can use their BDN account to administer their .Text blogs, instead of having another user id/password to maintain. Currently this is done by modifying <CODE>security.cs</CODE> from the <A href="http://www.gotdotnet.com/community/workspaces/viewuploads.aspx?id=e99fccb3-1a8c-42b5-90ee-348f6b77c407">.Text code base</A>, but I hope to change this to be a provider-based model similar to the database provider model in use now.</P>
<H2>Feedback intelligence</H2>
<P>If you are logged into BDN when you submit feedback, your name will auto-populate with your BDN account information. In the future, the feedback may be changed to require that you log into your BDN account to comment, if comment spam becomes a problem as it has for some other popular blog servers.</P>
<H2>Complete interoperability</H2>
<P>During this exercise, I was able to prove to myself that Delphi is a first class citizen in the world of the .Net framework. It was able to work in a mixed language environment with out any problems. I also found that BDP is a good database solution that implements the ADO.NET interfaces correctly. 
<P>Robert Love - <A href="http://peakxml.com/">http://peakxml.com (My personal blog)</A></P>
<P class=technote>Side Note: If you are Delphi user interested in creating a technical blog for yourself, visit <A href="http://blogs.slcdug.org/">blogs.slcdug.org</A>. we still have enough bandwidth for quite a few more active bloggers.</P>
<P align=left>
<P>
<HR align=left width=400>

<P></SPAN>
<P><SPAN class=body3>NOTE: The views and information expressed in this document represent those of its author(s) who are solely responsible for its content. Borland does not make or give any representation or warranty with respect to such content.</SPAN></P></DIV><img src ="http://www.blogjava.net/jinfeng_wang/aggbug/27473.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jinfeng_wang/" target="_blank">jinfeng_wang</a> 2006-01-10 22:14 <a href="http://www.blogjava.net/jinfeng_wang/archive/2006/01/10/27473.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>