﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>IT博客-玄铁剑-文章分类-ASP.NET相关</title><link>http://www.cnitblog.com/MartinYao/category/4539.html</link><description>成功的途径：抄，创造，研究，发明...</description><language>zh-cn</language><lastBuildDate>Mon, 26 Sep 2011 08:01:01 GMT</lastBuildDate><pubDate>Mon, 26 Sep 2011 08:01:01 GMT</pubDate><ttl>60</ttl><item><title>20套Web UI元素</title><link>http://www.cnitblog.com/MartinYao/articles/67795.html</link><dc:creator>玄铁剑</dc:creator><author>玄铁剑</author><pubDate>Fri, 30 Jul 2010 11:00:00 GMT</pubDate><guid>http://www.cnitblog.com/MartinYao/articles/67795.html</guid><wfw:comment>http://www.cnitblog.com/MartinYao/comments/67795.html</wfw:comment><comments>http://www.cnitblog.com/MartinYao/articles/67795.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/MartinYao/comments/commentRss/67795.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/MartinYao/services/trackbacks/67795.html</trackback:ping><description><![CDATA[<a href="http://speckyboy.com/2010/07/21/20-free-web-ui-element-kits-and-stencils/">http://speckyboy.com/2010/07/21/20-free-web-ui-element-kits-and-stencils/</a><br>
<div id=posttitle>
<h1><a href="http://speckyboy.com/2010/07/21/20-free-web-ui-element-kits-and-stencils/" rel=bookmark><font color=#2a6fa8>20 Free Web UI Element Kits and Stencils</font></a></h1>
</div>
<div class=postinfo>July 21, 2010 - <a title="Comment on 20 Free Web UI Element Kits and Stencils" href="http://speckyboy.com/2010/07/21/20-free-web-ui-element-kits-and-stencils/#comments"><font color=#507aa5>31 Comments</font></a><br><a title="View all posts in Illustrator" href="http://speckyboy.com/category/illustrator/" rel="category tag"><font color=#507aa5>Illustrator</font></a>,<a title="View all posts in Photoshop" href="http://speckyboy.com/category/photoshop/" rel="category tag"><font color=#507aa5>Photoshop</font></a>,<a title="View all posts in Vector" href="http://speckyboy.com/category/vector/" rel="category tag"><font color=#507aa5>Vector</font></a>,<a title="View all posts in Web Apps" href="http://speckyboy.com/category/web-20-apps/" rel="category tag"><font color=#507aa5>Web Apps</font></a>,<a title="View all posts in Web Design" href="http://speckyboy.com/category/web-design/" rel="category tag"><font color=#507aa5>Web Design</font></a></div>
<div class=adspace><a title="Create a free website" href="http://www.wix.com/start/matrix?utm_campaign=ma_speckyboy.com&amp;experiment_id=ma_speckyboy.com2"><font color=#507aa5><img class=logo alt="Create a free website" src="http://speckyboy.specky.netdna-cdn.com/wp-content/uploads/2010/04/wix_long_banner.jpg"></font></div>
<div id=socialbuttons>
<div class=sociable>
<div class=sociable_tagline><font color=#507aa5></font></div>
<ul>
    <li><a title=RSS href="http://speckyboy.com/feed" rel=nofollow target=_blank><font color=#507aa5><img class=sociable-hovers title=RSS alt=RSS src="http://speckyboy.com/wp-content/plugins/sociable/images/rss_32.png"></font></a></li>
    <li><a title=del.icio.us href="http://del.icio.us/post?url=http%3A%2F%2Fspeckyboy.com%2F2010%2F07%2F21%2F20-free-web-ui-element-kits-and-stencils%2F&amp;title=20%20Free%20Web%20UI%20Element%20Kits%20and%20Stencils" rel=nofollow target=_blank><font color=#507aa5><img class=sociable-hovers title=del.icio.us alt=del.icio.us src="http://speckyboy.com/wp-content/plugins/sociable/images/delicious_32.png"></font></a></li>
    <li><a title=StumbleUpon href="http://www.stumbleupon.com/submit?url=http%3A%2F%2Fspeckyboy.com%2F2010%2F07%2F21%2F20-free-web-ui-element-kits-and-stencils%2F&amp;title=20%20Free%20Web%20UI%20Element%20Kits%20and%20Stencils" rel=nofollow target=_blank><font color=#507aa5><img class=sociable-hovers title=StumbleUpon alt=StumbleUpon src="http://speckyboy.com/wp-content/plugins/sociable/images/stumbleupon_32.png"></font></a></li>
    <li><a title=Digg href="http://digg.com/submit?phase=2&amp;url=http%3A%2F%2Fspeckyboy.com%2F2010%2F07%2F21%2F20-free-web-ui-element-kits-and-stencils%2F&amp;title=20%20Free%20Web%20UI%20Element%20Kits%20and%20Stencils" rel=nofollow target=_blank><font color=#507aa5><img class=sociable-hovers title=Digg alt=Digg src="http://speckyboy.com/wp-content/plugins/sociable/images/digg_32.png"></font></a></li>
    <li><a title=TwitThis href="http://twitthis.com/twit?url=http%3A%2F%2Fspeckyboy.com%2F2010%2F07%2F21%2F20-free-web-ui-element-kits-and-stencils%2F" rel=nofollow target=_blank><font color=#507aa5><img class=sociable-hovers title=TwitThis alt=TwitThis src="http://speckyboy.com/wp-content/plugins/sociable/images/twitter_32.png"></font></a></li>
    <li><a title=Mixx href="http://www.mixx.com/submit?page_url=http%3A%2F%2Fspeckyboy.com%2F2010%2F07%2F21%2F20-free-web-ui-element-kits-and-stencils%2F&amp;title=20%20Free%20Web%20UI%20Element%20Kits%20and%20Stencils" rel=nofollow target=_blank><font color=#507aa5><img class=sociable-hovers title=Mixx alt=Mixx src="http://speckyboy.com/wp-content/plugins/sociable/images/mixx_32.png"></font></a></li>
    <li><a title=Technorati href="http://technorati.com/faves?add=http%3A%2F%2Fspeckyboy.com%2F2010%2F07%2F21%2F20-free-web-ui-element-kits-and-stencils%2F" rel=nofollow target=_blank><font color=#507aa5><img class=sociable-hovers title=Technorati alt=Technorati src="http://speckyboy.com/wp-content/plugins/sociable/images/technorati_32.png"></font></a></li>
    <li><a title=Facebook href="http://www.facebook.com/sharer.php?u=http%3A%2F%2Fspeckyboy.com%2F2010%2F07%2F21%2F20-free-web-ui-element-kits-and-stencils%2F&amp;t=20%20Free%20Web%20UI%20Element%20Kits%20and%20Stencils" rel=nofollow target=_blank><font color=#507aa5><img class=sociable-hovers title=Facebook alt=Facebook src="http://speckyboy.com/wp-content/plugins/sociable/images/facebook_32.png"></font></a></li>
    <li><a title=NewsVine href="http://www.newsvine.com/_tools/seed&amp;save?u=http%3A%2F%2Fspeckyboy.com%2F2010%2F07%2F21%2F20-free-web-ui-element-kits-and-stencils%2F&amp;h=20%20Free%20Web%20UI%20Element%20Kits%20and%20Stencils" rel=nofollow target=_blank><font color=#507aa5><img class=sociable-hovers title=NewsVine alt=NewsVine src="http://speckyboy.com/wp-content/plugins/sociable/images/newsvine_32.png"></font></a></li>
    <li><a title=Reddit href="http://reddit.com/submit?url=http%3A%2F%2Fspeckyboy.com%2F2010%2F07%2F21%2F20-free-web-ui-element-kits-and-stencils%2F&amp;title=20%20Free%20Web%20UI%20Element%20Kits%20and%20Stencils" rel=nofollow target=_blank><font color=#507aa5><img class=sociable-hovers title=Reddit alt=Reddit src="http://speckyboy.com/wp-content/plugins/sociable/images/reddit_32.png"></font></a></li>
    <li><a title=Google href="http://www.google.com/bookmarks/mark?op=edit&amp;bkmk=http%3A%2F%2Fspeckyboy.com%2F2010%2F07%2F21%2F20-free-web-ui-element-kits-and-stencils%2F&amp;title=20%20Free%20Web%20UI%20Element%20Kits%20and%20Stencils" rel=nofollow target=_blank><font color=#507aa5><img class=sociable-hovers title=Google alt=Google src="http://speckyboy.com/wp-content/plugins/sociable/images/google_32.png"></font></a></li>
    <li><a title=LinkedIn href="http://www.linkedin.com/shareArticle?mini=true&amp;url=http%3A%2F%2Fspeckyboy.com%2F2010%2F07%2F21%2F20-free-web-ui-element-kits-and-stencils%2F&amp;title=20%20Free%20Web%20UI%20Element%20Kits%20and%20Stencils&amp;source=Speckyboy+Design+Magazine+Web+Design%2C+Web+Development+and+Graphic+Design+Resources.+As+well+as+Wordpress%2C+Web+2%2C+New+Technology%2C+and+Inspiration.&amp;summary=No%20doubt%20every%20web%20designer%20has%20spent%20many%20endless%20hours%20mocking%20up%20web%20pages%2C%20and%20as%20such%20every%20designer%20should%20have%20a%20good%20set%20of%20re-usable%20and%20uniform%20Web%20UI%20elements%20that%20they%20can%20rely%20upon%20to%20save%20them%20precious%20time%20and%20spare%20headaches%20when%20they" rel=nofollow target=_blank><font color=#507aa5><img class=sociable-hovers title=LinkedIn alt=LinkedIn src="http://speckyboy.com/wp-content/plugins/sociable/images/linkedin_32.png"></font></a></li>
    <li><a title=co.mments href="http://co.mments.com/track?url=http%3A%2F%2Fspeckyboy.com%2F2010%2F07%2F21%2F20-free-web-ui-element-kits-and-stencils%2F&amp;title=20%20Free%20Web%20UI%20Element%20Kits%20and%20Stencils" rel=nofollow target=_blank><font color=#507aa5><img class=sociable-hovers title=co.mments alt=co.mments src="http://speckyboy.com/wp-content/plugins/sociable/images/co.mments.gif"></font></a></li>
    <li><a title=YahooMyWeb href="http://myweb2.search.yahoo.com/myresults/bookmarklet?u=http%3A%2F%2Fspeckyboy.com%2F2010%2F07%2F21%2F20-free-web-ui-element-kits-and-stencils%2F&amp;=20%20Free%20Web%20UI%20Element%20Kits%20and%20Stencils" rel=nofollow target=_blank><font color=#507aa5><img class=sociable-hovers title=YahooMyWeb alt=YahooMyWeb src="http://speckyboy.com/wp-content/plugins/sociable/images/yahoobuzz_32.png"></font></a></li>
    <li><a title="E-mail this story to a friend!" href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#63;&#115;&#117;&#98;&#106;&#101;&#99;&#116;&#61;&#50;&#48;&#37;&#50;&#48;&#70;&#114;&#101;&#101;&#37;&#50;&#48;&#87;&#101;&#98;&#37;&#50;&#48;&#85;&#73;&#37;&#50;&#48;&#69;&#108;&#101;&#109;&#101;&#110;&#116;&#37;&#50;&#48;&#75;&#105;&#116;&#115;&#37;&#50;&#48;&#97;&#110;&#100;&#37;&#50;&#48;&#83;&#116;&#101;&#110;&#99;&#105;&#108;&#115;&#38;&#97;&#109;&#112;&#59;&#98;&#111;&#100;&#121;&#61;&#104;&#116;&#116;&#112;&#37;&#51;&#65;&#37;&#50;&#70;&#37;&#50;&#70;&#115;&#112;&#101;&#99;&#107;&#121;&#98;&#111;&#121;&#46;&#99;&#111;&#109;&#37;&#50;&#70;&#50;&#48;&#49;&#48;&#37;&#50;&#70;&#48;&#55;&#37;&#50;&#70;&#50;&#49;&#37;&#50;&#70;&#50;&#48;&#45;&#102;&#114;&#101;&#101;&#45;&#119;&#101;&#98;&#45;&#117;&#105;&#45;&#101;&#108;&#101;&#109;&#101;&#110;&#116;&#45;&#107;&#105;&#116;&#115;&#45;&#97;&#110;&#100;&#45;&#115;&#116;&#101;&#110;&#99;&#105;&#108;&#115;&#37;&#50;&#70;" rel=nofollow target=_blank><font color=#507aa5><img class=sociable-hovers title="E-mail this story to a friend!" alt="E-mail this story to a friend!" src="http://speckyboy.com/wp-content/plugins/sociable/images/email_32.png"></font></a></li>
</ul>
</div>
</div>
<div id=news-info>Why not join <strong>30,000</strong> of our readers, by following us via our <span class=orange><a title="Follow us via RSS" href="http://speckyboy.com/feed"><strong><font color=#f7902d>RSS Feed</font></strong></a></span>, on <a href="http://twitter.com/speckyboy"><strong><font color=#507aa5>Twitter</font></strong></a> or on <a href="http://www.facebook.com/pages/Speckyboy-Design-Magazine/140568253177"><strong><font color=#507aa5>Facebook</font></strong></a></div>
<div id=main_post_clear>
<div style="PADDING-BOTTOM: 10px; MARGIN-TOP: 2px; PADDING-RIGHT: 3px; FLOAT: left; PADDING-TOP: 0px"><a class="retweet vert self" href=""><font color=#507aa5></font></a></div>
<p>No doubt every web designer has spent many endless hours mocking up web pages, and as such every designer should have a good set of re-usable and uniform Web UI elements that they can rely upon to save them precious time and spare headaches when they are in the initial stages of a web project.</p>
<p>In this article we have 20 completely free Web UI kits and stencils that are perfect for the initial mockup or wireframe stage of a web design project. Most of the kits are editable and the kits are in either .psd, .ai or .png format, but you will also find some Omnigraffle stencils and some kits in .svg format. So, whatever your preferred format you are bound to find the perfect kit for you.</p>
<h2><a onclick="javascript:pageTracker._trackPageview('/outbound/article/feedgrids.com');" href="http://feedgrids.com/originals/post/freebie_modern_web_ui_set/"><font color=#507aa5>Modern Web UI Set (.psd)</font></a></h2>
<p><a onclick="javascript:pageTracker._trackPageview('/outbound/article/feedgrids.com');" href="http://feedgrids.com/originals/post/freebie_modern_web_ui_set/"><font color=#507aa5><img alt="Modern Web UI Set (.psd)" src="http://speckyboy.specky.netdna-cdn.com/wp-content/uploads/2010/07/web_ui_01.jpg"></font></a>This free Web UI Set has everything included in the PSD file in well organized in folders, and fully editable using vector layers in Photoshop, from the button shapes, to the gradients, and text. Having every element editable means you can recreate anything you need that is not included in the file, just by using existing items as your base.<br><a onclick="javascript:pageTracker._trackPageview('/outbound/article/feedgrids.com');" href="http://feedgrids.com/originals/post/freebie_modern_web_ui_set/"><font color=#507aa5>Modern Web UI Set (.psd) &#8594;</font></a></p>
<h2><a onclick="javascript:pageTracker._trackPageview('/outbound/article/medialoot.com');" href="http://medialoot.com/blog/freebie-massive-web-ui-button-set/"><font color=#507aa5>Massive Web UI &amp; Button Set (.psd)</font></a></h2>
<p><a onclick="javascript:pageTracker._trackPageview('/outbound/article/medialoot.com');" href="http://medialoot.com/blog/freebie-massive-web-ui-button-set/"><font color=#507aa5><img alt="Massive Web UI &amp; Button Set (.psd)" src="http://speckyboy.specky.netdna-cdn.com/wp-content/uploads/2010/07/web_ui_02.jpg"></font></a>This free UI set contains all of the following elements in three distinct styles: glossy, satin/light gradient, and one-color. The satin/light gradient set is available in 7 different colors (which you&#8217;ll find all of in the psd file).<br><a onclick="javascript:pageTracker._trackPageview('/outbound/article/medialoot.com');" href="http://medialoot.com/blog/freebie-massive-web-ui-button-set/"><font color=#507aa5>Massive Web UI &amp; Button Set (.psd) &#8594;</font></a></p>
<h2><a onclick="javascript:pageTracker._trackPageview('/outbound/article/www.webstuffshare.com');" href="http://www.webstuffshare.com/2010/04/free-web-ui-element-pack/"><font color=#507aa5>Web UI Element Pack (.psd)</font></a></h2>
<p><a onclick="javascript:pageTracker._trackPageview('/outbound/article/www.webstuffshare.com');" href="http://www.webstuffshare.com/2010/04/free-web-ui-element-pack/"><font color=#507aa5><img alt="Web UI Element Pack (.psd)" src="http://speckyboy.specky.netdna-cdn.com/wp-content/uploads/2010/07/web_ui_03.jpg"></font></a>This User Interface Element Pack in PSD format, contains 19 elements including Loading Bar, Button in normal and clicked state, Button toggle, close, next, previous and paging icon and slider.<br><a onclick="javascript:pageTracker._trackPageview('/outbound/article/www.webstuffshare.com');" href="http://www.webstuffshare.com/2010/04/free-web-ui-element-pack/"><font color=#507aa5>Web UI Element Pack (.psd) &#8594;</font></a></p>
<h2><a onclick="javascript:pageTracker._trackPageview('/outbound/article/www.fuelyourinterface.com');" href="http://www.fuelyourinterface.com/free-web-ui-wireframe-kit/"><font color=#507aa5>Web UI Wireframe Kit (.psd)</font></a></h2>
<p><a onclick="javascript:pageTracker._trackPageview('/outbound/article/www.fuelyourinterface.com');" href="http://www.fuelyourinterface.com/free-web-ui-wireframe-kit/"><font color=#507aa5><img alt="Web UI Wireframe Kit (.psd)" src="http://speckyboy.specky.netdna-cdn.com/wp-content/uploads/2010/07/web_ui_04.jpg"></font></a>This web UI template kit is made completley with shape objects and in some cases converted into SmartObjects. So they&#8217;re totally scalable.<br><a onclick="javascript:pageTracker._trackPageview('/outbound/article/www.fuelyourinterface.com');" href="http://www.fuelyourinterface.com/free-web-ui-wireframe-kit/"><font color=#507aa5>Web UI Wireframe Kit (.psd) &#8594;</font></a></p>
<h2><a onclick="javascript:pageTracker._trackPageview('/outbound/article/www.teehanlax.com');" href="http://www.teehanlax.com/blog/2008/12/16/browser-form-elements-psd/"><font color=#507aa5>Browser Form Elements (.psd)</font></a></h2>
<p><a onclick="javascript:pageTracker._trackPageview('/outbound/article/www.teehanlax.com');" href="http://www.teehanlax.com/blog/2008/12/16/browser-form-elements-psd/"><font color=#507aa5><img alt="Browser Form Elements (.psd)" src="http://speckyboy.specky.netdna-cdn.com/wp-content/uploads/2010/07/web_ui_05.jpg"></font></a><a onclick="javascript:pageTracker._trackPageview('/outbound/article/www.teehanlax.com');" href="http://www.teehanlax.com/blog/2008/12/16/browser-form-elements-psd/"><font color=#507aa5>Browser Form Elements (.psd) &#8594;</font></a></p>
<h2><a onclick="javascript:pageTracker._trackPageview('/outbound/article/re-run.com');" href="http://re-run.com/article/omnigraffle-stencil"><font color=#507aa5>Web Page Elements (for Omnigraffle)</font></a></h2>
<p><a onclick="javascript:pageTracker._trackPageview('/outbound/article/re-run.com');" href="http://re-run.com/article/omnigraffle-stencil"><font color=#507aa5><img alt="Web Page Elements (for Omnigraffle)" src="http://speckyboy.specky.netdna-cdn.com/wp-content/uploads/2010/07/web_ui_06.jpg"></font></a>With the open-source &#8220;Bitstream Vera&#8221; font set, free icons from FamFamFam and this Omnigraffle web ui set, you should have most of your common web page elements covered, including headings, form elements, content management function, image placeholders, etc.<br><a onclick="javascript:pageTracker._trackPageview('/outbound/article/re-run.com');" href="http://re-run.com/article/omnigraffle-stencil"><font color=#507aa5>Web Page Elements (for Omnigraffle) &#8594;</font></a></p>
<h2><a onclick="javascript:pageTracker._trackPageview('/outbound/article/mortenjust.com');" href="http://mortenjust.com/2010/04/19/a-wireframe-kit-for-google-drawings/"><font color=#507aa5>Wireframe Kit (Google Drawings)</font></a></h2>
<p><a onclick="javascript:pageTracker._trackPageview('/outbound/article/mortenjust.com');" href="http://mortenjust.com/2010/04/19/a-wireframe-kit-for-google-drawings/"><font color=#507aa5><img alt="Wireframe Kit (Google Drawings)" src="http://speckyboy.specky.netdna-cdn.com/wp-content/uploads/2010/07/web_ui_07.jpg"></font></a>Google Drawings is still in its early and simple form and didn't have any notable UI kits, until now. These kits have been specifically designed for Google Drawings, and you have a choice of four templates, that include: The main blank template, product detail page, landing page and item list view.<br><a onclick="javascript:pageTracker._trackPageview('/outbound/article/mortenjust.com');" href="http://mortenjust.com/2010/04/19/a-wireframe-kit-for-google-drawings/"><font color=#507aa5>Wireframe Kit (Google Drawings) &#8594;</font></a></p>
<h2><a onclick="javascript:pageTracker._trackPageview('/outbound/article/www.jankoatwarpspeed.com');" href="http://www.jankoatwarpspeed.com/post/2009/12/24/sketching-wireframing-kit.aspx"><font color=#507aa5>Sketching &amp; Wireframing Kit (.ai, .eps, .pdf &amp; .svg)</font></a></h2>
<p><a onclick="javascript:pageTracker._trackPageview('/outbound/article/www.jankoatwarpspeed.com');" href="http://www.jankoatwarpspeed.com/post/2009/12/24/sketching-wireframing-kit.aspx"><font color=#507aa5><img alt="Sketching &amp; Wireframing Kit (.ai, .eps, .pdf &amp; .svg)" src="http://speckyboy.specky.netdna-cdn.com/wp-content/uploads/2010/07/web_ui_08.jpg"></font></a>Sketching &amp; Wireframing kit is a free set of elements for sketching and wireframing. It consist of form elements, icons, indicators, feedback messages, tooltips, navigation elements, image placeholders, embedded videos, sliders and common ad banners.<br>The Kit comes in two vector formats, one for Adobe Illustrator and the other in SVG vector format that can be easily modified. It can also be downloaded in PDF and EPS formats.<br><a onclick="javascript:pageTracker._trackPageview('/outbound/article/www.jankoatwarpspeed.com');" href="http://www.jankoatwarpspeed.com/post/2009/12/24/sketching-wireframing-kit.aspx"><font color=#507aa5>Sketching &amp; Wireframing Kit &#8594;</font></a></p>
<h2><a onclick="javascript:pageTracker._trackPageview('/outbound/article/www.johnnynines.com');" href="http://www.johnnynines.com/2009/03/wireframe-symbols/"><font color=#507aa5>Wireframe Symbols (.ai)</font></a></h2>
<p><a onclick="javascript:pageTracker._trackPageview('/outbound/article/www.johnnynines.com');" href="http://www.johnnynines.com/2009/03/wireframe-symbols/"><font color=#507aa5><img alt="Wireframe Symbols (.ai)" src="http://speckyboy.specky.netdna-cdn.com/wp-content/uploads/2010/07/web_ui_09.jpg"></font></a>This download contains the symbol library and a full Adobe Illustrator file with all of the elements spread out on the art board. To install this library just drag and drop the file named &#8220;Wireframe Symbols.ai&#8221; into your Adobe Illustrator Symbols directory. Once you are in Illustrator go to your Symbols Palette and load the library.<br><a onclick="javascript:pageTracker._trackPageview('/outbound/article/www.johnnynines.com');" href="http://www.johnnynines.com/2009/03/wireframe-symbols/"><font color=#507aa5>Wireframe Symbols (.ai) &#8594;</font></a></p>
<h2><a onclick="javascript:pageTracker._trackPageview('/outbound/article/developer.yahoo.com');" href="http://developer.yahoo.com/ypatterns/about/stencils/"><font color=#507aa5>Yahoo Design Stencils (.xml, .pdf, .svg, .png and Omnigraffle)</font></a></h2>
<p><a onclick="javascript:pageTracker._trackPageview('/outbound/article/developer.yahoo.com');" href="http://developer.yahoo.com/ypatterns/about/stencils/"><font color=#507aa5><img alt="Yahoo Design Stencils (.xml, .pdf, .svg, .png and Omnigraffle)" src="http://speckyboy.specky.netdna-cdn.com/wp-content/uploads/2010/07/web_ui_10.jpg"></font></a>The popular Yahoo! Design Stencil Kit version 1.0 is available for OmniGraffle, Visio (XML), Adobe Illustrator (PDF and SVG), and Adobe Photoshop (PNG), and covers the following topics: Ad Units, Calendars, Carousels, Charts and Tables, UI Controls, Form Elements, Grids, Menus and Buttons, Mobile, Navigation and Pagination, OS Elements, Placeholder Text, Screen Resolutions, Tabs and Windows and Containers.<br><a onclick="javascript:pageTracker._trackPageview('/outbound/article/developer.yahoo.com');" href="http://developer.yahoo.com/ypatterns/about/stencils/"><font color=#507aa5>Yahoo Design Stencils (.xml, .pdf, .svg, .png and Omnigraffle) &#8594;</font></a></p>
<h2><a onclick="javascript:pageTracker._trackPageview('/outbound/article/cloverleafllc.com');" href="http://cloverleafllc.com/content/omnigraffle-stencil-ext-js-v30"><font color=#507aa5>Ext JS v3.0 Stencil (for Omnigraffle)</font></a></h2>
<p><a onclick="javascript:pageTracker._trackPageview('/outbound/article/cloverleafllc.com');" href="http://cloverleafllc.com/content/omnigraffle-stencil-ext-js-v30"><font color=#507aa5><img alt="Ext JS v3.0 Stencil (for Omnigraffle)" src="http://speckyboy.specky.netdna-cdn.com/wp-content/uploads/2010/07/web_ui_11.jpg"></font></a>This is the updated version of the Omnigraffle ExtJS stencil Ext JS. This update contains many improvements and additions, namely that the developers have recreated most Ext JS elements as Graffletopia shapes or groups. This is especially helpful for resizing titles, tables cells, and so on.<br><a onclick="javascript:pageTracker._trackPageview('/outbound/article/cloverleafllc.com');" href="http://cloverleafllc.com/content/omnigraffle-stencil-ext-js-v30"><font color=#507aa5>Ext JS v3.0 Stencil (for Omnigraffle) &#8594;</font></a></p>
<h2><a onclick="javascript:pageTracker._trackPageview('/outbound/article/www.gosquared.com');" href="http://www.gosquared.com/liquidicity/archives/122"><font color=#507aa5>165 Vector Icons in 5 Colours (.ai, .jpg and .svg)</font></a></h2>
<p><a onclick="javascript:pageTracker._trackPageview('/outbound/article/www.gosquared.com');" href="http://www.gosquared.com/liquidicity/archives/122"><font color=#507aa5><img alt="165 Vector Icons in 5 Colours (.ai, .jpg and .svg)" src="http://speckyboy.specky.netdna-cdn.com/wp-content/uploads/2010/07/web_ui_12.jpg"></font></a>This huge kit, from Liquidicity, contains 165 icons and are available in these five colors: Red, green, blue, black and white.<br><a onclick="javascript:pageTracker._trackPageview('/outbound/article/www.gosquared.com');" href="http://www.gosquared.com/liquidicity/archives/122"><font color=#507aa5>165 Vector Icons in 5 Colours (.ai, .jpg and .svg) &#8594;</font></a></p>
<h2><a onclick="javascript:pageTracker._trackPageview('/outbound/article/www.graffletopia.com');" href="http://www.graffletopia.com/stencils/431"><font color=#507aa5>Flex 3 Stencil (for Omnigraffle)</font></a></h2>
<p><a onclick="javascript:pageTracker._trackPageview('/outbound/article/www.graffletopia.com');" href="http://www.graffletopia.com/stencils/431"><font color=#507aa5><img alt="Flex 3 Stencil (for Omnigraffle)" src="http://speckyboy.specky.netdna-cdn.com/wp-content/uploads/2010/07/web_ui_13.jpg"></font></a>This Omnigraffle stencil contains all of the Flex components from the Flex 3 Style Guide: panels, data grid, buttons, fields, links, toggle, menu, scrolls, accordion, tabs, list, data picker, tool tip, errors.<br><a onclick="javascript:pageTracker._trackPageview('/outbound/article/www.graffletopia.com');" href="http://www.graffletopia.com/stencils/431"><font color=#507aa5>Flex 3 Stencil (for Omnigraffle) &#8594;</font></a></p>
<h2><a onclick="javascript:pageTracker._trackPageview('/outbound/article/www.patsheridan.com');" href="http://www.patsheridan.com/content/omnigraffle-twitter-widget-stencil"><font color=#507aa5>Twitter Widget Stencil (for Omnigraffle)</font></a></h2>
<p><a onclick="javascript:pageTracker._trackPageview('/outbound/article/www.patsheridan.com');" href="http://www.patsheridan.com/content/omnigraffle-twitter-widget-stencil"><font color=#507aa5><img alt="Twitter Widget Stencil  (for Omnigraffle)" src="http://speckyboy.specky.netdna-cdn.com/wp-content/uploads/2010/07/web_ui_14.jpg"></font></a>This stencil includes a selection of useful Twitter badges and widgets.<br><a onclick="javascript:pageTracker._trackPageview('/outbound/article/www.patsheridan.com');" href="http://www.patsheridan.com/content/omnigraffle-twitter-widget-stencil"><font color=#507aa5>Twitter Widget Stencil (for Omnigraffle) &#8594;</font></a></p>
<h2><a onclick="javascript:pageTracker._trackPageview('/outbound/article/templay.de');" href="http://templay.de/Downloads/39/Webdesigner-Toolkit.html"><font color=#507aa5>Web Designer Toolkit (.psd)</font></a></h2>
<p><a onclick="javascript:pageTracker._trackPageview('/outbound/article/templay.de');" href="http://templay.de/Downloads/39/Webdesigner-Toolkit.html"><font color=#507aa5><img alt="Web Designer Toolkit (.psd)" src="http://speckyboy.specky.netdna-cdn.com/wp-content/uploads/2010/07/web_ui_15.jpg"></font></a><a onclick="javascript:pageTracker._trackPageview('/outbound/article/templay.de');" href="http://templay.de/Downloads/39/Webdesigner-Toolkit.html"><font color=#507aa5>Web Designer Toolkit (.psd) &#8594;</font></a></p>
<h2><a onclick="javascript:pageTracker._trackPageview('/outbound/article/surgeworks.com');" href="http://surgeworks.com/blog/design/facebook-gui-free-psd-resource"><font color=#507aa5>Facebook GUI (.psd)</font></a></h2>
<p><a onclick="javascript:pageTracker._trackPageview('/outbound/article/surgeworks.com');" href="http://surgeworks.com/blog/design/facebook-gui-free-psd-resource"><font color=#507aa5><img alt="Facebook GUI (.psd)" src="http://speckyboy.specky.netdna-cdn.com/wp-content/uploads/2010/07/web_ui_16.jpg"></font></a><a onclick="javascript:pageTracker._trackPageview('/outbound/article/surgeworks.com');" href="http://surgeworks.com/blog/design/facebook-gui-free-psd-resource"><font color=#507aa5>Facebook GUI (.psd) &#8594;</font></a></p>
<h2><a onclick="javascript:pageTracker._trackPageview('/outbound/article/www.graffletopia.com');" href="http://www.graffletopia.com/stencils/410"><font color=#507aa5>Facebook Applications (Omnigraffle)</font></a></h2>
<p><a onclick="javascript:pageTracker._trackPageview('/outbound/article/www.graffletopia.com');" href="http://www.graffletopia.com/stencils/410"><font color=#507aa5><img alt="Facebook Applications (Omnigraffle)" src="http://speckyboy.specky.netdna-cdn.com/wp-content/uploads/2010/07/web_ui_17a.jpg"></font></a><br>This is a pretty sizable collection of Facebook related elements to use in creating wireframes for Facebook applications.<a onclick="javascript:pageTracker._trackPageview('/outbound/article/www.graffletopia.com');" href="http://www.graffletopia.com/stencils/410"><font color=#507aa5>Facebook Applications (Omnigraffle) &#8594;</font></a></p>
<h2><a onclick="javascript:pageTracker._trackPageview('/outbound/article/bestblogbox.com');" href="http://bestblogbox.com/freebies/all-in-one-web-elements-kit/"><font color=#507aa5>Web Elements Kit (.psd)</font></a></h2>
<p><a onclick="javascript:pageTracker._trackPageview('/outbound/article/bestblogbox.com');" href="http://bestblogbox.com/freebies/all-in-one-web-elements-kit/"><font color=#507aa5><img alt="Web Elements Kit (.psd)" src="http://speckyboy.specky.netdna-cdn.com/wp-content/uploads/2010/07/web_ui_17.jpg"></font></a><br>This huge set of various web elements with 17 easy to modify modules, with each module comes with four different color options.<a onclick="javascript:pageTracker._trackPageview('/outbound/article/bestblogbox.com');" href="http://bestblogbox.com/freebies/all-in-one-web-elements-kit/"><font color=#507aa5>Web Elements Kit (.psd) &#8594;</font></a></p>
<h2><a onclick="javascript:pageTracker._trackPageview('/outbound/article/psdthemes.com');" href="http://psdthemes.com/theme-406-marvelous-flex-darkskin-psd-ui.html"><font color=#507aa5>Flex Darkskin UI (.psd)</font></a></h2>
<p><a onclick="javascript:pageTracker._trackPageview('/outbound/article/psdthemes.com');" href="http://psdthemes.com/theme-406-marvelous-flex-darkskin-psd-ui.html"><font color=#507aa5><img alt="Flex Darkskin UI (.psd)" src="http://speckyboy.specky.netdna-cdn.com/wp-content/uploads/2010/07/web_ui_18.jpg"></font></a>This beautiful and polished free Flex UI skin is available for free from psdthemes.com, but you do need to signup first.<br><a onclick="javascript:pageTracker._trackPageview('/outbound/article/psdthemes.com');" href="http://psdthemes.com/theme-406-marvelous-flex-darkskin-psd-ui.html"><font color=#507aa5>Flex Darkskin UI (.psd) &#8594;</font></a></p>
<h2><a onclick="javascript:pageTracker._trackPageview('/outbound/article/lazycrazy.deviantart.com');" href="http://lazycrazy.deviantart.com/art/WEB-UI-Treasure-Chest-v-1-0-139165343"><font color=#507aa5>WEB UI Treasure Chest</font></a></h2>
<p><a onclick="javascript:pageTracker._trackPageview('/outbound/article/lazycrazy.deviantart.com');" href="http://lazycrazy.deviantart.com/art/WEB-UI-Treasure-Chest-v-1-0-139165343"><font color=#507aa5><img alt="WEB UI Treasure Chest" src="http://speckyboy.specky.netdna-cdn.com/wp-content/uploads/2010/07/web_ui_19.jpg"></font></a><a onclick="javascript:pageTracker._trackPageview('/outbound/article/lazycrazy.deviantart.com');" href="http://lazycrazy.deviantart.com/art/WEB-UI-Treasure-Chest-v-1-0-139165343"><font color=#507aa5>WEB UI Treasure Chest &#8594;</font></a></p>
<h2>You might also like&#8230;</h2>
<p><a href="http://speckyboy.com/2010/01/11/10-completely-free-wireframe-and-mockup-applications/"><font color=#507aa5>10 Completely Free Wireframe and Mockup Applications &#8594;</font></a><br><a href="http://speckyboy.com/2009/11/23/a-collection-of-useful-web-design-wireframing-resources/"><font color=#507aa5>A Collection of Useful Web Design Wireframing Resources &#8594;</font></a><br><a href="http://speckyboy.com/2010/07/14/a-collection-of-printable-web-browser-sketching-and-wireframe-templates/"><font color=#507aa5>A Collection of Printable Web Browser Sketching and Wireframe Templates &#8594;</font></a><br><a href="http://speckyboy.com/2010/05/17/15-javascript-web-ui-libraries-frameworks-and-libraries//"><font color=#507aa5>15 Javascript Web UI Libraries, Frameworks and Toolkits &#8594;</font></a><br><a href="http://speckyboy.com/2010/02/01/25-ui-inspiration-and-design-pattern-resources/"><font color=#507aa5>25 UI Inspiration and Design Pattern Resources &#8594;</font></a><br><a href="http://speckyboy.com/2009/08/31/50-essential-web-typography-tutorials-tips-guides-and-best-practices/"><font color=#507aa5>50 Essential Web Typography Tutorials, Tips, Guides and Best Practices &#8594;</font></a><br><a href="http://speckyboy.com/2009/07/15/50-useful-tools-and-generators-for-easy-css-development/"><font color=#507aa5>50 Useful Tools and Generators for Easy CSS Development &#8594;</font></a><br><a href="http://speckyboy.com/2009/07/02/20-resources-and-tutorials-for-creative-forms-using-css/"><font color=#507aa5>20+ Resources and Tutorials for Creative Forms using CSS &#8594;</font></a><br><a href="http://speckyboy.com/2009/06/08/24-css-in-some-cases-with-jquery-navigation-and-menu-tutorials/"><font color=#507aa5>24 CSS (in some cases with jQuery) Navigation and Menu Tutorials &#8594;</font></a></p>
</div>
<img src ="http://www.cnitblog.com/MartinYao/aggbug/67795.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/MartinYao/" target="_blank">玄铁剑</a> 2010-07-30 19:00 <a href="http://www.cnitblog.com/MartinYao/articles/67795.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Linq VS Entity framework</title><link>http://www.cnitblog.com/MartinYao/articles/67203.html</link><dc:creator>玄铁剑</dc:creator><author>玄铁剑</author><pubDate>Tue, 06 Jul 2010 13:27:00 GMT</pubDate><guid>http://www.cnitblog.com/MartinYao/articles/67203.html</guid><wfw:comment>http://www.cnitblog.com/MartinYao/comments/67203.html</wfw:comment><comments>http://www.cnitblog.com/MartinYao/articles/67203.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/MartinYao/comments/commentRss/67203.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/MartinYao/services/trackbacks/67203.html</trackback:ping><description><![CDATA[<p><img title="ADO.net，Linq to SQL和Entity Framework性能实测分析 — Windows Live - longniezhang - 疯狂计算机技术" border=0 alt="ADO.net，Linq to SQL和Entity Framework性能实测分析 — Windows Live - longniezhang - 疯狂计算机技术" src="https://wr61eq.bay.livefilestore.com/y1mL-FLm34vQ3Kt0XXTC_6jWaq6D16Ae3nyIf7n2WwJdPSz4fpn6uzEiAVFt52t2hmKdTvuseMxkNrqQ7DPJkJPsEUZMdxUSk7C2mefNKPX8k7Ib1IWd7Rfp6CvlbrgpX3pR_cfT2D7lJM/image_thumb[30].png" width=553 height=349> </p>
<div align=center>
<table>
    第二次读写10条数据
    <tbody>
    </tbody>
</table>
</div>
<p><img title="ADO.net，Linq to SQL和Entity Framework性能实测分析 — Windows Live - longniezhang - 疯狂计算机技术" border=0 alt="ADO.net，Linq to SQL和Entity Framework性能实测分析 — Windows Live - longniezhang - 疯狂计算机技术" src="https://wr61eq.bay.livefilestore.com/y1mOaGxJJmIclM7iP0nrlOkoeKBnl5cxxlEYrYuk-tEGy2PLtpQuKNnF7wxv4CYSYyfHdCuVCw4qeBZkD4z8wk8uW1kW5yy3CCA_cNmieYaCtZkXVILuJCDOi_QA6ehVeemkw8iVV9PWHU/image_thumb[34].png" width=555 height=357> </p>
<div align=center>
<table>
    操作100条数据
    <tbody>
    </tbody>
</table>
</div>
<p><img title="ADO.net，Linq to SQL和Entity Framework性能实测分析 — Windows Live - longniezhang - 疯狂计算机技术" border=0 alt="ADO.net，Linq to SQL和Entity Framework性能实测分析 — Windows Live - longniezhang - 疯狂计算机技术" src="https://wr61eq.bay.livefilestore.com/y1mZeEY04gWG2ouk8GKlXjWaMS7ubj_KdZDg819afWokBsZamj_r65UhRRCnxK_L32SMrEQYclupVMyVZ5DxojPvYGssqsGMv5D0iORmD64HHHPqqWZ0s_-NSZN3KtANkaV5FEVySDh0z0/image_thumb38.png" width=558 height=347> </p>
<div align=center>
<table>
    操作1000条数据
    <tbody>
    </tbody>
</table>
</div>
<p><img title="ADO.net，Linq to SQL和Entity Framework性能实测分析 — Windows Live - longniezhang - 疯狂计算机技术" border=0 alt="ADO.net，Linq to SQL和Entity Framework性能实测分析 — Windows Live - longniezhang - 疯狂计算机技术" src="https://wr61eq.bay.livefilestore.com/y1mNU8Y5RO0cdDkVi9QwQI3zFQgWKR7Lvp1XKLN83zOlVDjpiEpEwvmVNPNcG5wbJtNM-NSvDmFX-fFRQ58X2xaGII-xYAsIs2b3sUG1Kr3WZPYIjYtncBfyhzlA43wxi-PW4vlYMxbOec/image_thumb42.png" width=558 height=356> </p>
<div align=center>
<table>
    操作10000条数据
    <tbody>
    </tbody>
</table>
</div>
<p><img title="ADO.net，Linq to SQL和Entity Framework性能实测分析 — Windows Live - longniezhang - 疯狂计算机技术" border=0 alt="ADO.net，Linq to SQL和Entity Framework性能实测分析 — Windows Live - longniezhang - 疯狂计算机技术" src="https://wr61eq.bay.livefilestore.com/y1m_n5bRSj-pCTH8tRlXN34d16sjMcH5giyrtt6eX7MToLX0ZcTk2vX1Y7egGLykZIPTxoDZDjbItZ1bv5ZCP6aYgHq9pYeER08tf9sDx9Vz3F9w9mh-Eth2SqGcG6mxeB3sTSTHlmzNOQ/image_thumb46.png" width=553 height=349> </p>
<div align=center>
<table>
    操作100000条数据
    <tbody>
    </tbody>
</table>
</div>
<p><img title="ADO.net，Linq to SQL和Entity Framework性能实测分析 — Windows Live - longniezhang - 疯狂计算机技术" border=0 alt="ADO.net，Linq to SQL和Entity Framework性能实测分析 — Windows Live - longniezhang - 疯狂计算机技术" src="https://wr61eq.bay.livefilestore.com/y1md5mMqxFYiM4Qpru3CPnn1K47S69f5cBaPKNZBxcwR0r0hE7A5s-BjB5gtFWtaK6bUSYbdMvy3AEo2ooJniXgqVIGkgnBRpChqlDdH0xvnn5FzI5Qt-K_tifxTCC-xBvGY6eSGhzDTzI/image_thumb51.png" width=555 height=355>&nbsp;<br></p>
<p>关于ado.net entity framework 性能比较网上也有很多，这里我只是初步的介绍下ado.net entity framework使用不同的方法查询数据的不同性能</p>
<p>第一部分：重复查询单个实体</p>
<p>第一种：Linq To Entitiess</p>
<p>代码如下：</p>
<p>static　void　Main(string[]　args)<br>　　　　　　　　{<br>　　　　　　　　　　　　DateTime　time1;<br>　　　　　　　　　　　　DateTime　time2;<br>　　　　　　　　　　　　time1　=　DateTime.Now;<br>　　　　　　　　　　　　NorthwindEntities　context　=　new　NorthwindEntities();<br>　　　　　　　　　　　　for　(int　i　=　0;　i　&lt;　1000;　i++)<br>　　　　　　　　　　　　{<br>　　　　　　　　　　　　　　　　var　data　=　(from　c　in　context.Customers　where　c.CustomerID　==　"ALFKI"　select　c).FirstOrDefault();<br>　　　　　　　　　　　　　　　　string　addr　=　data.Address;<br>　　　　　　　　　　　　}<br>　　　　　　　　　　　　time2　=　DateTime.Now;<br>　　　　　　　　　　　　Console.WriteLine((time2-time1).ToString());<br>　　　　　　　　}</p>
<p>查询使用时间为6.2秒左右</p>
<p>第二种：使用Entity SQL</p>
<p>static　void　Main(string[]　args)<br>　　　　　　　　{<br>　　　　　　　　　　　　DateTime　time1;<br>　　　　　　　　　　　　DateTime　time2;<br>　　　　　　　　　　　　time1　=　DateTime.Now;<br>　　　　　　　　　　　　NorthwindEntities　context　=　new　NorthwindEntities();<br>　　　　　　　　　　　　for　(int　i　=　0;　i　&lt;　1000;　i++)<br>　　　　　　　　　　　　{<br>　　　　　　　　　　　　　　　　var　data　=　context.Customers.Where("it.CustomerID=@Id",　new　System.Data.Objects.ObjectParameter("Id",　"ALFKI")).FirstOrDefault();<br>　　　　　　　　　　　　　　　　string　addr　=　data.Address;<br>　　　　　　　　　　　　}<br>　　　　　　　　　　　　time2　=　DateTime.Now;<br>　　　　　　　　　　　　Console.WriteLine((time2-time1).ToString());<br>　　　　　　　　}<br>查询使用时间为6.2秒左右</p>
<p>第三种：使用EntityKey 来查询</p>
<p>　static　void　Main(string[]　args)<br>　　　　　　　　{<br>　　　　　　　　　　　　DateTime　time1;<br>　　　　　　　　　　　　DateTime　time2;<br>　　　　　　　　　　　　time1　=　DateTime.Now;<br>　　　　　　　　　　　　NorthwindEntities　context　=　new　NorthwindEntities();<br>　　　　　　　　　　　　for　(int　i　=　0;　i　&lt;　1000;　i++)<br>　　　　　　　　　　　　{<br>　　　　　　　　　　　　　　　　var　data　=　context.GetObjectByKey(new　System.Data.EntityKey("NorthwindEntities.Customers",　"CustomerID",　"ALFKI"))　as　Customers;<br>　　　　　　　　　　　　　　　　string　addr　=　data.Address;<br>　　　　　　　　　　　　}<br>　　　　　　　　　　　　time2　=　DateTime.Now;<br>　　　　　　　　　　　　Console.WriteLine((time2-time1).ToString());<br>　　　　　　　　}</p>
<p>查询使用时间为1秒，没错是一秒</p>
<p>总结：</p>
<p>前两种方法查询所使用的时间都差不多为6.2秒，时间比较长，但是使用第三种方法查询数据仅仅使用了1秒，为什么会相差那么多，区别在于前两种方法每次查询都要从数据库中查找，1000次查询每次的查询时间都一样，但是第三种方法只有第一次是从数据库中查数据，后面的999次都是在context通过主键直接取数据，速度当然会快很多啦，EF4.0之前的实体当含有外键引用时是自动生成对象引用的，而EF4.0现在多了一个外键ID，这样我们可以很方便的通过外键来查询数据</p>
<p>第二部分：查询不同是实体<br>第一种：Linq To Entitiess</p>
<p>　static　void　Main(string[]　args)<br>　　　　　　　　{<br>　　　　　　　　　　　　DateTime　time1;<br>　　　　　　　　　　　　DateTime　time2;<br>　　　　　　　　　　　　NorthwindEntities　context　=　new　NorthwindEntities();<br>　　　　　　　　　　　　//先将Customers所有的主键查询出来<br>　　　　　　　　　　　　List&lt;string&gt;　keys　=　context.Customers.Select(c　=&gt;　c.CustomerID).Distinct().ToList();<br>　　　　　　　　　　　　//和上面的context以作区别<br>　　　　　　　　　　　　NorthwindEntities　　context_　=　new　NorthwindEntities();<br>　　　　　　　　　　　　time1　=　DateTime.Now;<br>　　　　　　　　　　　　foreach　(var　key　in　keys)<br>　　　　　　　　　　　　{<br>　　　　　　　　　　　　　　　　var　data　=　context_.Customers.Where(c　=&gt;　c.CustomerID　==　key).FirstOrDefault();<br>　　　　　　　　　　　　　　　　string　addr　=　data.Address;<br>　　　　　　　　　　　　}<br>　　　　　　　　　　　　time2　=　DateTime.Now;<br>　　　　　　　　　　　　Console.WriteLine((time2　-　time1).ToString());<br>　　　　　　　　}</p>
<p>查询使用时间0.68秒左右</p>
<p>第二种：使用Entity SQL</p>
<p>static　void　Main(string[]　args)<br>　　　　　　　　{<br>　　　　　　　　　　　　DateTime　time1;<br>　　　　　　　　　　　　DateTime　time2;<br>　　　　　　　　　　　　NorthwindEntities　context　=　new　NorthwindEntities();<br>　　　　　　　　　　　　//先将Customers所有的主键查询出来<br>　　　　　　　　　　　　List&lt;string&gt;　keys　=　context.Customers.Select(c　=&gt;　c.CustomerID).Distinct().ToList();<br>　　　　　　　　　　　　//和上面的context以作区别<br>　　　　　　　　　　　　NorthwindEntities　　context_　=　new　NorthwindEntities();<br>　　　　　　　　　　　　time1　=　DateTime.Now;<br>　　　　　　　　　　　　foreach　(var　key　in　keys)<br>　　　　　　　　　　　　{<br>　　　　　　　　　　　　　　　　var　data　=　context_.Customers.Where("it.CustomerID=@Id",　new　System.Data.Objects.ObjectParameter("Id",　key)).FirstOrDefault();<br>　　　　　　　　　　　　　　　　string　addr　=　data.Address;<br>　　　　　　　　　　　　}<br>　　　　　　　　　　　　time2　=　DateTime.Now;<br>　　　　　　　　　　　　Console.WriteLine((time2　-　time1).ToString());<br>　　　　　　　　}</p>
<p>查询使用时间0.5秒左右</p>
<p>第三种：使用EntityKey 来查询</p>
<p>static　void　Main(string[]　args)<br>　　　　　　　　{<br>　　　　　　　　　　　　DateTime　time1;<br>　　　　　　　　　　　　DateTime　time2;<br>　　　　　　　　　　　　NorthwindEntities　context　=　new　NorthwindEntities();<br>　　　　　　　　　　　　//先将Customers所有的主键查询出来<br>　　　　　　　　　　　　List&lt;string&gt;　keys　=　context.Customers.Select(c　=&gt;　c.CustomerID).Distinct().ToList();<br>　　　　　　　　　　　　//和上面的context以作区别<br>　　　　　　　　　　　　NorthwindEntities　　context_　=　new　NorthwindEntities();<br>　　　　　　　　　　　　time1　=　DateTime.Now;<br>　　　　　　　　　　　　foreach　(var　key　in　keys)<br>　　　　　　　　　　　　{<br>　　　　　　　　　　　　　　　　var　data　=　context_.GetObjectByKey(new　System.Data.EntityKey("NorthwindEntities.Customers",　"CustomerID",　key))　as　Customers;<br>　　　　　　　　　　　　　　　　string　addr　=　data.Address;<br>　　　　　　　　　　　　}<br>　　　　　　　　　　　　time2　=　DateTime.Now;<br>　　　　　　　　　　　　Console.WriteLine((time2　-　time1).ToString());<br>　　　　　　　　}</p>
<p>查询时间为0.18秒左右</p>
<p>总结：</p>
<p>通过比较第三种方法仍然比前两种来的快，查询效率更高</p>
<p>使用EntityKey来查询数据比其他两种方法来的更快，而且不是快一点点，而是相差有好几倍，这里的比较并没有使用存储过程来比较查询，但是从这里我们可以看出，在平常的应用中如果知道实体的主键尽量用主键来查询</p>
<img src ="http://www.cnitblog.com/MartinYao/aggbug/67203.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/MartinYao/" target="_blank">玄铁剑</a> 2010-07-06 21:27 <a href="http://www.cnitblog.com/MartinYao/articles/67203.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title> TransactionScope </title><link>http://www.cnitblog.com/MartinYao/articles/67057.html</link><dc:creator>玄铁剑</dc:creator><author>玄铁剑</author><pubDate>Sun, 27 Jun 2010 04:08:00 GMT</pubDate><guid>http://www.cnitblog.com/MartinYao/articles/67057.html</guid><wfw:comment>http://www.cnitblog.com/MartinYao/comments/67057.html</wfw:comment><comments>http://www.cnitblog.com/MartinYao/articles/67057.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/MartinYao/comments/commentRss/67057.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/MartinYao/services/trackbacks/67057.html</trackback:ping><description><![CDATA[<p>TransactionScope 收藏 <br>.NET Framework 2.0 版中新增的 TransactionScope 单独使用确实很方便。但是在实际项目中都有自己的访问层，如何才能和自己的数据访问层结合起来使用呢？ <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在项目中我是这样处理数据的：</p>
<p><br>&nbsp; /**//// &lt;summary&gt;<br>&nbsp;&nbsp;&nbsp; /// 外包业务访问类<br>&nbsp;&nbsp;&nbsp; /// &lt;/summary&gt;<br>&nbsp;&nbsp;&nbsp; public class OutSourcingDAO<br>&nbsp;&nbsp;&nbsp; {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /**//// &lt;summary&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// 增加<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// &lt;/summary&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// &lt;param name="bt"&gt;&lt;/param&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// &lt;returns&gt;&lt;/returns&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public int InsertGetIdentity(OutSourcing bt)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return new DAHelper(DataAccess.Create()).InsertGetIdentity(bt);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /**//// &lt;summary&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// 更新<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// &lt;/summary&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// &lt;param name="bt"&gt;&lt;/param&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// &lt;returns&gt;&lt;/returns&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public int Update(OutSourcing bt)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return new DAHelper(DataAccess.Create()).Update(bt);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /**//// &lt;summary&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// 删除<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// &lt;/summary&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// &lt;param name="bt"&gt;&lt;/param&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// &lt;returns&gt;&lt;/returns&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public int Delete(OutSourcing bt)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return new DAHelper(DataAccess.Create()).Delete(bt);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; }<br>上面 OutSourcing 为与表相对应的实体类 ；DAHelper 为作者自己实现的一个包装类，可以对任意实体进行增加，修改，删除 查询等功能。<br>&nbsp; 再贴一段代码： </p>
<p>&nbsp;&nbsp; public int ExecSql(string strSql)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try<br>&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; this.OpenConnection();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cmd.CommandType = CommandType.Text;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cmd.CommandText = strSql;<br>&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; return cmd.ExecuteNonQuery();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; catch (System.Exception e)<br>&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; throw this.CatchException(e);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; finally<br>&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; this.CloseConnection();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>我最终通过调用 ExecSql&nbsp; 方法来与数据库交互，而该方法会自己打开数据库连接，执行语句，然后关闭连接。<br>在操作同一个数据库的时候，如果要高效的使用 TransactionScope，必须保证 SqlConnection 不改变，即用同一个 SqlConnection 来完成所需要的增加<br>删除，或修改。我想写这样的代码进行事务控制：</p>
<p><br>&nbsp;public int UpdateTest(OutSourcing outSourcing, BusinessAccept businessAccept)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; IDataAccess dac = DataAccess.Create();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DAHelper myHelper = new DAHelper(dac);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; using (TransactionScope ts = new TransactionScope())<br>&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; myHelper.Update(outSourcing);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; myHelper.Update(businessAccept);<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; ts.Complete();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>这样就需要，执行第一个操作时候打开数据库连接，执行，不关闭连接，然后执行第二个操作，执行完关闭。显然，我想让 TransactionScope 在<br>实行 Dispose() 方法的时候关闭数据库连接。using 代码块的本质上等同于 try{}finally{} 语句代码块。为什么不封装一下 TransactionScope 让它满足自己的要求呢？</p>
<p><br>/**//// &lt;summary&gt;<br>/// TransactionScope 包装类<br>/// &lt;/summary&gt;<br>public sealed class Scope : IDisposable<br>{<br>&nbsp;&nbsp;&nbsp; private TransactionScope m_TransactionScope = null;<br>&nbsp;&nbsp;&nbsp; /**//// &lt;summary&gt;<br>&nbsp;&nbsp;&nbsp; ///&nbsp; 测试访问类<br>&nbsp;&nbsp;&nbsp; /// &lt;/summary&gt;<br>&nbsp;&nbsp;&nbsp; private DataAccessTest m_DataAccessTest = null;<br>&nbsp;&nbsp;&nbsp; /**//// &lt;summary&gt;<br>&nbsp;&nbsp;&nbsp; /// 实例化一个新的 TransactionScope<br>&nbsp;&nbsp;&nbsp; /// &lt;/summary&gt;<br>&nbsp;&nbsp;&nbsp; /// &lt;param name="dac"&gt;&lt;/param&gt;<br>&nbsp;&nbsp;&nbsp; public Scope(DataAccessTest dac)<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.m_DataAccessTest = dac;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //告诉访问类 你已经使用了事务<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dac.SetScope(this);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.m_TransactionScope = new TransactionScope();</p>
<p>&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; /**//// &lt;summary&gt;<br>&nbsp;&nbsp;&nbsp; /// 发出事务结束命令<br>&nbsp;&nbsp;&nbsp; /// &lt;/summary&gt;<br>&nbsp;&nbsp;&nbsp; public void Complete()<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.m_TransactionScope.Complete();<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; IDisposable 成员#region IDisposable 成员<br>&nbsp;&nbsp;&nbsp; /**//// &lt;summary&gt;<br>&nbsp;&nbsp;&nbsp; /// 当执行该方法的时候完成两件任务<br>&nbsp;&nbsp;&nbsp; /// 1 关闭数据库连接<br>&nbsp;&nbsp;&nbsp; /// 2 调用 TransactionScope 的 Dispose()方法<br>&nbsp;&nbsp;&nbsp; /// &lt;/summary&gt;<br>&nbsp;&nbsp;&nbsp; void IDisposable.Dispose()<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_DataAccessTest.Close();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; finally<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_TransactionScope.Dispose();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp; #endregion<br>}</p>
<p><br>数据库访问类代码如下：</p>
<p><br>/**//// &lt;summary&gt;<br>/// 模拟数据库访问类<br>/// &lt;/summary&gt;<br>public class DataAccessTest<br>{<br>&nbsp;&nbsp;&nbsp; SqlConnection con = null;<br>&nbsp;&nbsp;&nbsp; SqlCommand cmd = new SqlCommand();<br>&nbsp;&nbsp;&nbsp; Scope scope = null;<br>&nbsp;&nbsp;&nbsp; string strCon = "这里是数据库连接字符串。。。。。";</p>
<p>&nbsp;&nbsp;&nbsp; public void SetScope(Scope scope)<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.scope = scope;<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; private void OpenConnection()<br>&nbsp;&nbsp;&nbsp; {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (con == null || scope == null)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; con = new SqlConnection(strCon);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cmd.Connection = con;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; con.Open();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Console.WriteLine(" 打开数据库连接;");<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp; private void CloseConnection()<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.cmd.Parameters.Clear();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (scope == null)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; con.Close();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; con.Dispose();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Console.WriteLine(" 未使用事务 关闭数据库连接;");<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp; public int ExecuteSql(string strSql)<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.OpenConnection();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cmd.CommandType = CommandType.Text;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cmd.CommandText = strSql;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Console.WriteLine("执行 Sql 语句。。。");</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return cmd.ExecuteNonQuery();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; catch (System.Exception e)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; throw e;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; finally<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.CloseConnection();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp;<br>&nbsp;&nbsp;&nbsp; public void Close()<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; con.Close();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; con.Dispose();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Console.WriteLine(" 关闭数据库连接-&gt;该方法由 Scope中的Dispose()方法调用 ");<br>&nbsp;&nbsp;&nbsp; }<br>}<br>赶快写个方法测试一下吧！</p>
<p><br>&nbsp;&nbsp; /**//// &lt;summary&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// 测试<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// &lt;/summary&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// &lt;param name="sender"&gt;&lt;/param&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// &lt;param name="e"&gt;&lt;/param&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private void button1_Click(object sender, EventArgs e)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try<br>&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; Console.WriteLine("下面是使用事务的输出。。。。。。。。");<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.TestACT();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Console.WriteLine("*********************下面是&nbsp; 未&nbsp; 使用事务的输出。。。。。。。。");<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.TestNoACT();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; catch (System.Exception ex)<br>&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; Console.WriteLine("出现了异常？？？？？？？？？？？？");<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MessageBox.Show(ex.ToString());<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /**//// &lt;summary&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// 使用事务<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// &lt;/summary&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public void TestACT()<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DataAccessTest dac = new DataAccessTest();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; using (Scope scope = new Scope(dac))<br>&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; string strSql1 = "INSERT INTO [tilva20].[dbo].[T_Test]([TestName])VALUES('a')";<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; string strSql2 = "INSERT INTO [tilva20].[dbo].[T_Test]([TestName])VALUES('b')";<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dac.ExecuteSql(strSql1);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dac.ExecuteSql(strSql2);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; scope.Complete();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /**//// &lt;summary&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// 不使用事务<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// &lt;/summary&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public void TestNoACT()<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DataAccessTest dac = new DataAccessTest();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; string strSql1 = "INSERT INTO [tilva20].[dbo].[T_Test]([TestName])VALUES('a')";<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; string strSql2 = "INSERT INTO [tilva20].[dbo].[T_Test]([TestName])VALUES('b')";<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dac.ExecuteSql(strSql1);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dac.ExecuteSql(strSql2);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;</p>
<p>查看一下输入的结果 <br>下面是使用事务的输出。。。。。。。。<br>&nbsp;打开数据库连接;<br>执行 Sql 语句。。。<br>执行 Sql 语句。。。<br>&nbsp;关闭数据库连接-&gt;该方法由 Scope中的Dispose()方法调用 <br>*********************下面是&nbsp; 未&nbsp; 使用事务的输出。。。。。。。。<br>&nbsp;打开数据库连接;<br>执行 Sql 语句。。。<br>&nbsp;未使用事务 关闭数据库连接;<br>&nbsp;打开数据库连接;<br>执行 Sql 语句。。。<br>&nbsp;未使用事务 关闭数据库连接;</p>
<p>输出结果和想像中的完全一样 数据库也正确（没任何原因不正确！）<br>最后 改变一下方法：</p>
<p><br>&nbsp;&nbsp; /**//// &lt;summary&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// 使用事务<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// &lt;/summary&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public void TestACT()<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DataAccessTest dac = new DataAccessTest();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; using (Scope scope = new Scope(dac))<br>&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; string strSql1 = "INSERT INTO [tilva20].[dbo].[T_Test]([TestName])VALUES('a')";<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; string strSql2 = "INSERT INTO [tilva20].[dbo].[T_Test]([TestName])VALUES('b22222222222222222222222222222222222222222222222222222222222222222222222222222222222222')";<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dac.ExecuteSql(strSql1);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dac.ExecuteSql(strSql2);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; scope.Complete();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>熟悉数据库的肯定知道 &#8220;将截断字符串或二进制数据&#8221;吧！<br>输出结果为：<br>&nbsp;下面是使用事务的输出。。。。。。。。<br>&nbsp;打开数据库连接;<br>执行 Sql 语句。。。<br>执行 Sql 语句。。。<br>&nbsp;关闭数据库连接-&gt;该方法由 Scope中的Dispose()方法调用 <br>出现了异常？？？？？？？？？？？？<br>数据库一条记录没有增加 数据库连接已经关闭&nbsp; 测试结果完全满意！<br>对自己访问层稍做修改：<br>最终的代码为：</p>
<p><br>&nbsp;&nbsp; public int UpdateTest(OutSourcing outSourcing, BusinessAccept businessAccept)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; IDataAccess dac = DataAccess.Create();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DAHelper myHelper = new DAHelper(dac);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; using (Scope ts=new Scope())<br>&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; myHelper.Update(outSourcing);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; myHelper.Update(businessAccept);<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; ts.Complete();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>本文来自CSDN博客，转载请标明出处：<a href="http://blog.csdn.net/logicbiz/archive/2008/04/23/2317712.aspx">http://blog.csdn.net/logicbiz/archive/2008/04/23/2317712.aspx</a></p>
<img src ="http://www.cnitblog.com/MartinYao/aggbug/67057.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/MartinYao/" target="_blank">玄铁剑</a> 2010-06-27 12:08 <a href="http://www.cnitblog.com/MartinYao/articles/67057.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>asp.net单点登录(SSO)解决方案 </title><link>http://www.cnitblog.com/MartinYao/articles/67054.html</link><dc:creator>玄铁剑</dc:creator><author>玄铁剑</author><pubDate>Sun, 27 Jun 2010 03:38:00 GMT</pubDate><guid>http://www.cnitblog.com/MartinYao/articles/67054.html</guid><wfw:comment>http://www.cnitblog.com/MartinYao/comments/67054.html</wfw:comment><comments>http://www.cnitblog.com/MartinYao/articles/67054.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/MartinYao/comments/commentRss/67054.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/MartinYao/services/trackbacks/67054.html</trackback:ping><description><![CDATA[<div id=appShareShareIcon class=share-btn-img onclick="share.share('6519658','','6519658')"></div>
asp.net单点登录(SSO)解决方案
<div id=app-share-container>
<div id=appShareOpt></div>
<div id=app-share-content>前些天一位朋友要我帮忙做一单点登录，其实这个概念早已耳熟能详，但实际应用很少，难得最近轻闲，于是决定通过本文来详细描述一个SSO解决方案，希望对大家有所帮助。SSO的解决方案很多，但搜索结果令人大失所望，大部分是相互转载，并且描述的也是走马观花。
<p dir=ltr>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 闲话少叙，进入正题，我的想法是使用集中验证方式，多个站点集中Passport验证。&nbsp;如下图所示：</p>
<p dir=ltr><img border=0 src="http://images.cnblogs.com/cnblogs_com/luck0235/sso0.jpg" width=521 height=237>&nbsp;</p>
<p dir=ltr>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 为方便清晰描述，先定义几个名词，本文中出现之处均为如下含义。</p>
<p dir=ltr>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <strong>主站</strong>：Passport集中验证服务器 <a><font color=#0066aa>http://www.passport.com/</font></a> 。</p>
<p dir=ltr>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <strong>分站</strong>：<a><font color=#0066aa>http://www.a.com/</font></a>、<a><font color=#0066aa>http://www.b.com/</font></a>、<a><font color=#0066aa>http://www.c.com/</font></a></p>
<p dir=ltr>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <strong>凭证</strong>：用户登录后产生的数据标识，用于识别授权用户，可为多种方式，DEMO中主站我使用的是Cache，分站使用Session。</p>
<p dir=ltr>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <strong>令牌</strong>：由Passport颁发可在各分站中流通的唯一标识。</p>
<p dir=ltr>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; OK，现在描述一下单点登录的过程：</p>
<p dir=ltr>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 情形一、匿名用户：匿名用户访问分站a上的一个授权页面，首先跳转到主站让用户输入帐号、密码进行登录，验证通过后产生主站凭证，同时产生令牌，跳转回分 站a，此时分站a检测到用户已持有令牌，于是用令牌再次去主站获取用户凭证，获取成功后允许用户访问该授权页面。同时产生分站a的本地凭证，当该用户需要 再次验证时将先检查本地凭证，以减少网络交互。</p>
<p dir=ltr>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 情形二、在分站a登录的用户访问分站b：因为用户在分站a登录过，已持有令牌，所以分站b会用令牌去主站获取用户凭证，获取成功后允许用户访问授权页面。同时产生分站b的本地凭证。</p>
<p dir=ltr><img border=0 src="http://images.cnblogs.com/cnblogs_com/luck0235/sso2.jpg" width=495 height=648>&nbsp;</p>
<p dir=ltr>设计完成后，接下来是方案实现的一些关键点：</p>
<p dir=ltr><strong>令牌</strong>：令牌由主站颁发，主站颁发令牌同时生成用户凭证，并记录令牌与用户凭证之间的对应关 系，以根据用户提供的令牌响应对应的凭证；令牌要在各跨域分站中进行流通，所以DEMO中令牌我使用主站的Cookie，并指定 Cookie.Domain="passport.com"。各分站如何共享主站的Cookie？从分站Redirect到主站页面，然后该页面读取 Cookie并以URL参数方式回传即可，可在DEMO代码中查看详细实现，当然如果哪位有更好的令牌实现方式也拿出来分享。</p>
<div class=cnblogs_code><span style="COLOR: rgb(0,128,0)">//</span><span style="COLOR: rgb(0,128,0)">产生令牌</span><span style="COLOR: rgb(0,128,0)"><br></span><span style="COLOR: rgb(0,0,255)">string</span><span style="COLOR: rgb(0,0,0)">&nbsp;tokenValue&nbsp;</span><span style="COLOR: rgb(0,0,0)">=</span><span style="COLOR: rgb(0,0,0)">&nbsp;Guid.NewGuid().ToString().ToUpper();<br>HttpCookie&nbsp;tokenCookie&nbsp;</span><span style="COLOR: rgb(0,0,0)">=</span><span style="COLOR: rgb(0,0,0)">&nbsp;</span><span style="COLOR: rgb(0,0,255)">new</span><span style="COLOR: rgb(0,0,0)">&nbsp;HttpCookie(</span><span style="COLOR: rgb(128,0,0)">"</span><span style="COLOR: rgb(128,0,0)">Token</span><span style="COLOR: rgb(128,0,0)">"</span><span style="COLOR: rgb(0,0,0)">);<br>tokenCookie.Values.Add(</span><span style="COLOR: rgb(128,0,0)">"</span><span style="COLOR: rgb(128,0,0)">Value</span><span style="COLOR: rgb(128,0,0)">"</span><span style="COLOR: rgb(0,0,0)">,&nbsp;tokenValue);<br>tokenCookie.Domain&nbsp;</span><span style="COLOR: rgb(0,0,0)">=</span><span style="COLOR: rgb(0,0,0)">&nbsp;</span><span style="COLOR: rgb(128,0,0)">"</span><span style="COLOR: rgb(128,0,0)">passport.com</span><span style="COLOR: rgb(128,0,0)">"</span><span style="COLOR: rgb(0,0,0)">;<br>Response.AppendCookie(tokenCookie);&nbsp;</span></div>
<p dir=ltr>&nbsp;</p>
<p dir=ltr><strong>主站凭证</strong>：主站凭证是一个关系表，包含了三个字段：令牌、凭证数据、过期时间。有多种实现方式可供选择，要求可靠的话用数据库，要求性能的话用Cache，DEMO中我使用的是Cache中的DataTable。如下代码所示：</p>
<div class=cnblogs_code><span style="COLOR: rgb(128,128,128)">///</span><span style="COLOR: rgb(0,128,0)">&nbsp;</span><span style="COLOR: rgb(128,128,128)">&lt;summary&gt;</span><span style="COLOR: rgb(0,128,0)"><br></span><span style="COLOR: rgb(128,128,128)">///</span><span style="COLOR: rgb(0,128,0)">&nbsp;初始化数据结构<br></span><span style="COLOR: rgb(128,128,128)">///</span><span style="COLOR: rgb(0,128,0)">&nbsp;</span><span style="COLOR: rgb(128,128,128)">&lt;/summary&gt;</span><span style="COLOR: rgb(0,128,0)"><br></span><span style="COLOR: rgb(128,128,128)">///</span><span style="COLOR: rgb(0,128,0)">&nbsp;</span><span style="COLOR: rgb(128,128,128)">&lt;remarks&gt;</span><span style="COLOR: rgb(0,128,0)"><br></span><span style="COLOR: rgb(128,128,128)">///</span><span style="COLOR: rgb(0,128,0)">&nbsp;----------------------------------------------------<br></span><span style="COLOR: rgb(128,128,128)">///</span><span style="COLOR: rgb(0,128,0)">&nbsp;|&nbsp;token(令牌)&nbsp;|&nbsp;info(用户凭证)&nbsp;|&nbsp;timeout(过期时间)&nbsp;|<br></span><span style="COLOR: rgb(128,128,128)">///</span><span style="COLOR: rgb(0,128,0)">&nbsp;|--------------------------------------------------|<br></span><span style="COLOR: rgb(128,128,128)">///</span><span style="COLOR: rgb(0,128,0)">&nbsp;</span><span style="COLOR: rgb(128,128,128)">&lt;/remarks&gt;</span><span style="COLOR: rgb(128,128,128)"><br></span><span style="COLOR: rgb(0,0,255)">private</span><span style="COLOR: rgb(0,0,0)">&nbsp;</span><span style="COLOR: rgb(0,0,255)">static</span><span style="COLOR: rgb(0,0,0)">&nbsp;</span><span style="COLOR: rgb(0,0,255)">void</span><span style="COLOR: rgb(0,0,0)">&nbsp;cacheInit()<br>{<br></span><span style="COLOR: rgb(0,0,255)">if</span><span style="COLOR: rgb(0,0,0)">&nbsp;(HttpContext.Current.Cache[</span><span style="COLOR: rgb(128,0,0)">"</span><span style="COLOR: rgb(128,0,0)">CERT</span><span style="COLOR: rgb(128,0,0)">"</span><span style="COLOR: rgb(0,0,0)">]&nbsp;</span><span style="COLOR: rgb(0,0,0)">==</span><span style="COLOR: rgb(0,0,0)">&nbsp;</span><span style="COLOR: rgb(0,0,255)">null</span><span style="COLOR: rgb(0,0,0)">)<br>{<br>DataTable&nbsp;dt&nbsp;</span><span style="COLOR: rgb(0,0,0)">=</span><span style="COLOR: rgb(0,0,0)">&nbsp;</span><span style="COLOR: rgb(0,0,255)">new</span><span style="COLOR: rgb(0,0,0)">&nbsp;DataTable();<br><br>dt.Columns.Add(</span><span style="COLOR: rgb(128,0,0)">"</span><span style="COLOR: rgb(128,0,0)">token</span><span style="COLOR: rgb(128,0,0)">"</span><span style="COLOR: rgb(0,0,0)">,&nbsp;Type.GetType(</span><span style="COLOR: rgb(128,0,0)">"</span><span style="COLOR: rgb(128,0,0)">System.String</span><span style="COLOR: rgb(128,0,0)">"</span><span style="COLOR: rgb(0,0,0)">));<br>dt.Columns[</span><span style="COLOR: rgb(128,0,0)">"</span><span style="COLOR: rgb(128,0,0)">token</span><span style="COLOR: rgb(128,0,0)">"</span><span style="COLOR: rgb(0,0,0)">].Unique&nbsp;</span><span style="COLOR: rgb(0,0,0)">=</span><span style="COLOR: rgb(0,0,0)">&nbsp;</span><span style="COLOR: rgb(0,0,255)">true</span><span style="COLOR: rgb(0,0,0)">;<br><br>dt.Columns.Add(</span><span style="COLOR: rgb(128,0,0)">"</span><span style="COLOR: rgb(128,0,0)">info</span><span style="COLOR: rgb(128,0,0)">"</span><span style="COLOR: rgb(0,0,0)">,&nbsp;Type.GetType(</span><span style="COLOR: rgb(128,0,0)">"</span><span style="COLOR: rgb(128,0,0)">System.Object</span><span style="COLOR: rgb(128,0,0)">"</span><span style="COLOR: rgb(0,0,0)">));<br>dt.Columns[</span><span style="COLOR: rgb(128,0,0)">"</span><span style="COLOR: rgb(128,0,0)">info</span><span style="COLOR: rgb(128,0,0)">"</span><span style="COLOR: rgb(0,0,0)">].DefaultValue&nbsp;</span><span style="COLOR: rgb(0,0,0)">=</span><span style="COLOR: rgb(0,0,0)">&nbsp;</span><span style="COLOR: rgb(0,0,255)">null</span><span>;<br><br><strong>&nbsp;</strong>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dt.Columns.Add("</span><span style="COLOR: rgb(128,0,0)">timeout</span><span style="COLOR: rgb(128,0,0)">"</span><span style="COLOR: rgb(0,0,0)">,&nbsp;Type.GetType(</span><span style="COLOR: rgb(128,0,0)">"</span><span style="COLOR: rgb(128,0,0)">System.DateTime</span><span>"));<br>dt.Columns[</span><span style="COLOR: rgb(128,0,0)">"</span><span style="COLOR: rgb(128,0,0)">timeout</span><span style="COLOR: rgb(128,0,0)">"</span><span style="COLOR: rgb(0,0,0)">].DefaultValue&nbsp;</span><span style="COLOR: rgb(0,0,0)">=</span><span style="COLOR: rgb(0,0,0)">&nbsp;DateTime.Now.AddMinutes(</span><span style="COLOR: rgb(0,0,255)">double</span><span style="COLOR: rgb(0,0,0)">.Parse(System.Configuration.ConfigurationManager.AppSettings[</span><span style="COLOR: rgb(128,0,0)">"</span><span style="COLOR: rgb(128,0,0)">timeout</span><span>"]));<br><br>DataColumn[]&nbsp;keys&nbsp;</span><span style="COLOR: rgb(0,0,0)">=</span><span style="COLOR: rgb(0,0,0)">&nbsp;</span><span style="COLOR: rgb(0,0,255)">new</span><span style="COLOR: rgb(0,0,0)">&nbsp;DataColumn[</span><span>1];<br>keys[</span><span style="COLOR: rgb(128,0,128)">0</span><span style="COLOR: rgb(0,0,0)">]&nbsp;</span><span style="COLOR: rgb(0,0,0)">=</span><span style="COLOR: rgb(0,0,0)">&nbsp;dt.Columns[</span><span style="COLOR: rgb(128,0,0)">"</span><span style="COLOR: rgb(128,0,0)">token</span><span>"];<br>dt.PrimaryKey&nbsp;</span><span>=&nbsp;keys;<br><br></span><span style="COLOR: rgb(0,128,0)">//</span><span>Cache的过期时间为&nbsp;令牌过期时间*2<br>HttpContext.Current.Cache.Insert(</span><span style="COLOR: rgb(128,0,0)">"</span><span style="COLOR: rgb(128,0,0)">CERT</span><span style="COLOR: rgb(128,0,0)">"</span><span style="COLOR: rgb(0,0,0)">,&nbsp;dt,&nbsp;</span><span style="COLOR: rgb(0,0,255)">null</span><span style="COLOR: rgb(0,0,0)">,&nbsp;DateTime.MaxValue,&nbsp;TimeSpan.FromMinutes(</span><span style="COLOR: rgb(0,0,255)">double</span><span style="COLOR: rgb(0,0,0)">.Parse(System.Configuration.ConfigurationManager.AppSettings[</span><span style="COLOR: rgb(128,0,0)">"</span><span style="COLOR: rgb(128,0,0)">timeout</span><span style="COLOR: rgb(128,0,0)">"</span><span style="COLOR: rgb(0,0,0)">])&nbsp;</span><span style="COLOR: rgb(0,0,0)">*</span><span style="COLOR: rgb(0,0,0)">&nbsp;</span><span>2));<br>}<br>}</span></div>
<p dir=ltr>&nbsp;</p>
<p dir=ltr><strong>分站凭证</strong>：分站凭证主要用于减少重复验证时网络的交互，比如用户已在分站a上登录过，当他再次访问分站a时，就不必使用令牌去主站验证了，因为分站a已有该用户的凭证。分站凭证相对比较简单，使用Session、Cookie均可。</p>
<p dir=ltr><strong>分站SSO页面基类</strong>：分站使用SSO的页面会做一系列的逻辑判断处理，如文章开头的流程图。如果有多个页面的话不可能为每个页写一个这样的逻辑，OK，那么把这套逻辑封装成一个基类，凡是要使用SSO的页面继承该基类即可。如下代码所示：</p>
<div class=cnblogs_code><span style="COLOR: rgb(0,0,255)">using</span><span style="COLOR: rgb(0,0,0)">&nbsp;System;<br></span><span style="COLOR: rgb(0,0,255)">using</span><span style="COLOR: rgb(0,0,0)">&nbsp;System.Data;<br></span><span style="COLOR: rgb(0,0,255)">using</span><span style="COLOR: rgb(0,0,0)">&nbsp;System.Configuration;<br></span><span style="COLOR: rgb(0,0,255)">using</span><span style="COLOR: rgb(0,0,0)">&nbsp;System.Web;<br></span><span style="COLOR: rgb(0,0,255)">using</span><span style="COLOR: rgb(0,0,0)">&nbsp;System.Web.Security;<br></span><span style="COLOR: rgb(0,0,255)">using</span><span style="COLOR: rgb(0,0,0)">&nbsp;System.Web.UI;<br></span><span style="COLOR: rgb(0,0,255)">using</span><span style="COLOR: rgb(0,0,0)">&nbsp;System.Web.UI.WebControls;<br></span><span style="COLOR: rgb(0,0,255)">using</span><span style="COLOR: rgb(0,0,0)">&nbsp;System.Web.UI.WebControls.WebParts;<br></span><span style="COLOR: rgb(0,0,255)">using</span><span style="COLOR: rgb(0,0,0)">&nbsp;System.Web.UI.HtmlControls;<br></span><span style="COLOR: rgb(0,0,255)">using</span><span style="COLOR: rgb(0,0,0)">&nbsp;System.Text.RegularExpressions;<br><br></span><span style="COLOR: rgb(0,0,255)">namespace</span><span style="COLOR: rgb(0,0,0)">&nbsp;SSO.SiteA.Class<br>{<br></span><span style="COLOR: rgb(128,128,128)">///</span><span style="COLOR: rgb(0,128,0)">&nbsp;</span><span style="COLOR: rgb(128,128,128)">&lt;summary&gt;</span><span style="COLOR: rgb(0,128,0)"><br></span><span style="COLOR: rgb(128,128,128)">///</span><span style="COLOR: rgb(0,128,0)">&nbsp;授权页面基类<br></span><span style="COLOR: rgb(128,128,128)">///</span><span style="COLOR: rgb(0,128,0)">&nbsp;</span><span style="COLOR: rgb(128,128,128)">&lt;/summary&gt;</span><span style="COLOR: rgb(128,128,128)"><br></span><span style="COLOR: rgb(0,0,0)">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: rgb(0,0,255)">public</span><span style="COLOR: rgb(0,0,0)">&nbsp;</span><span style="COLOR: rgb(0,0,255)">class</span><span style="COLOR: rgb(0,0,0)">&nbsp;AuthBase&nbsp;:&nbsp;System.Web.UI.Page<br>{<br></span><span style="COLOR: rgb(0,0,255)">protected</span><span style="COLOR: rgb(0,0,0)">&nbsp;</span><span style="COLOR: rgb(0,0,255)">override</span><span style="COLOR: rgb(0,0,0)">&nbsp;</span><span style="COLOR: rgb(0,0,255)">void</span><span style="COLOR: rgb(0,0,0)">&nbsp;OnLoad(EventArgs&nbsp;e)<br>{<br></span><span style="COLOR: rgb(0,0,255)">if</span><span style="COLOR: rgb(0,0,0)">&nbsp;(Session[</span><span style="COLOR: rgb(128,0,0)">"</span><span style="COLOR: rgb(128,0,0)">Token</span><span style="COLOR: rgb(128,0,0)">"</span><span style="COLOR: rgb(0,0,0)">]&nbsp;</span><span style="COLOR: rgb(0,0,0)">!=</span><span style="COLOR: rgb(0,0,0)">&nbsp;</span><span style="COLOR: rgb(0,0,255)">null</span><span style="COLOR: rgb(0,0,0)">)<br>{<br></span><span style="COLOR: rgb(0,128,0)">//</span><span style="COLOR: rgb(0,128,0)">分站凭证存在</span><span style="COLOR: rgb(0,128,0)"><br></span><span style="COLOR: rgb(0,0,0)">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Response.Write(</span><span style="COLOR: rgb(128,0,0)">"</span><span style="COLOR: rgb(128,0,0)">恭喜，分站凭证存在，您被授权访问该页面！</span><span style="COLOR: rgb(128,0,0)">"</span><span style="COLOR: rgb(0,0,0)">);<br>}<br></span><span style="COLOR: rgb(0,0,255)">else</span><span style="COLOR: rgb(0,0,0)"><br>{<br></span><span style="COLOR: rgb(0,128,0)">//</span><span style="COLOR: rgb(0,128,0)">令牌验证结果</span><span style="COLOR: rgb(0,128,0)"><br></span><span style="COLOR: rgb(0,0,0)">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: rgb(0,0,255)">if</span><span style="COLOR: rgb(0,0,0)">&nbsp;(Request.QueryString[</span><span style="COLOR: rgb(128,0,0)">"</span><span style="COLOR: rgb(128,0,0)">Token</span><span style="COLOR: rgb(128,0,0)">"</span><span style="COLOR: rgb(0,0,0)">]&nbsp;</span><span style="COLOR: rgb(0,0,0)">!=</span><span style="COLOR: rgb(0,0,0)">&nbsp;</span><span style="COLOR: rgb(0,0,255)">null</span><span style="COLOR: rgb(0,0,0)">)<br>{<br></span><span style="COLOR: rgb(0,0,255)">if</span><span style="COLOR: rgb(0,0,0)">&nbsp;(Request.QueryString[</span><span style="COLOR: rgb(128,0,0)">"</span><span style="COLOR: rgb(128,0,0)">Token</span><span style="COLOR: rgb(128,0,0)">"</span><span style="COLOR: rgb(0,0,0)">]&nbsp;</span><span style="COLOR: rgb(0,0,0)">!=</span><span style="COLOR: rgb(0,0,0)">&nbsp;</span><span style="COLOR: rgb(128,0,0)">"</span><span style="COLOR: rgb(128,0,0)">$Token$</span><span style="COLOR: rgb(128,0,0)">"</span><span style="COLOR: rgb(0,0,0)">)<br>{<br></span><span style="COLOR: rgb(0,128,0)">//</span><span style="COLOR: rgb(0,128,0)">持有令牌</span><span style="COLOR: rgb(0,128,0)"><br></span><span style="COLOR: rgb(0,0,0)">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: rgb(0,0,255)">string</span><span style="COLOR: rgb(0,0,0)">&nbsp;tokenValue&nbsp;</span><span style="COLOR: rgb(0,0,0)">=</span><span style="COLOR: rgb(0,0,0)">&nbsp;Request.QueryString[</span><span style="COLOR: rgb(128,0,0)">"</span><span style="COLOR: rgb(128,0,0)">Token</span><span style="COLOR: rgb(128,0,0)">"</span><span style="COLOR: rgb(0,0,0)">];<br></span><span style="COLOR: rgb(0,128,0)">//</span><span style="COLOR: rgb(0,128,0)">调用WebService获取主站凭证</span><span style="COLOR: rgb(0,128,0)"><br></span><span style="COLOR: rgb(0,0,0)">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SSO.SiteA.RefPassport.TokenService&nbsp;tokenService&nbsp;</span><span style="COLOR: rgb(0,0,0)">=</span><span style="COLOR: rgb(0,0,0)">&nbsp;</span><span style="COLOR: rgb(0,0,255)">new</span><span style="COLOR: rgb(0,0,0)">&nbsp;SSO.SiteA.RefPassport.TokenService();<br></span><span style="COLOR: rgb(0,0,255)">object</span><span style="COLOR: rgb(0,0,0)">&nbsp;o&nbsp;</span><span style="COLOR: rgb(0,0,0)">=</span><span style="COLOR: rgb(0,0,0)">&nbsp;tokenService.TokenGetCredence(tokenValue);<br></span><span style="COLOR: rgb(0,0,255)">if</span><span style="COLOR: rgb(0,0,0)">&nbsp;(o&nbsp;</span><span style="COLOR: rgb(0,0,0)">!=</span><span style="COLOR: rgb(0,0,0)">&nbsp;</span><span style="COLOR: rgb(0,0,255)">null</span><span style="COLOR: rgb(0,0,0)">)<br>{<br></span><span style="COLOR: rgb(0,128,0)">//</span><span style="COLOR: rgb(0,128,0)">令牌正确</span><span style="COLOR: rgb(0,128,0)"><br></span><span style="COLOR: rgb(0,0,0)">&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;Session[</span><span style="COLOR: rgb(128,0,0)">"</span><span style="COLOR: rgb(128,0,0)">Token</span><span style="COLOR: rgb(128,0,0)">"</span><span style="COLOR: rgb(0,0,0)">]&nbsp;</span><span style="COLOR: rgb(0,0,0)">=</span><span style="COLOR: rgb(0,0,0)">&nbsp;o;<br>Response.Write(</span><span style="COLOR: rgb(128,0,0)">"</span><span style="COLOR: rgb(128,0,0)">恭喜，令牌存在，您被授权访问该页面！</span><span style="COLOR: rgb(128,0,0)">"</span><span style="COLOR: rgb(0,0,0)">);<br>}<br></span><span style="COLOR: rgb(0,0,255)">else</span><span style="COLOR: rgb(0,0,0)"><br>{<br></span><span style="COLOR: rgb(0,128,0)">//</span><span style="COLOR: rgb(0,128,0)">令牌错误</span><span style="COLOR: rgb(0,128,0)"><br></span><span style="COLOR: rgb(0,0,0)">&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;Response.Redirect(</span><span style="COLOR: rgb(0,0,255)">this</span><span style="COLOR: rgb(0,0,0)">.replaceToken());<br>}<br>}<br></span><span style="COLOR: rgb(0,0,255)">else</span><span style="COLOR: rgb(0,0,0)"><br>{<br></span><span style="COLOR: rgb(0,128,0)">//</span><span style="COLOR: rgb(0,128,0)">未持有令牌</span><span style="COLOR: rgb(0,128,0)"><br></span><span style="COLOR: rgb(0,0,0)">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Response.Redirect(</span><span style="COLOR: rgb(0,0,255)">this</span><span style="COLOR: rgb(0,0,0)">.replaceToken());<br>}<br>}<br></span><span style="COLOR: rgb(0,128,0)">//</span><span style="COLOR: rgb(0,128,0)">未进行令牌验证，去主站验证</span><span style="COLOR: rgb(0,128,0)"><br></span><span style="COLOR: rgb(0,0,0)">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: rgb(0,0,255)">else</span><span style="COLOR: rgb(0,0,0)"><br>{<br>Response.Redirect(</span><span style="COLOR: rgb(0,0,255)">this</span><span style="COLOR: rgb(0,0,0)">.getTokenURL());<br>}<br>}<br><br></span><span style="COLOR: rgb(0,0,255)">base</span><span style="COLOR: rgb(0,0,0)">.OnLoad(e);<br>}<br><br></span><span style="COLOR: rgb(128,128,128)">///</span><span style="COLOR: rgb(0,128,0)">&nbsp;</span><span style="COLOR: rgb(128,128,128)">&lt;summary&gt;</span><span style="COLOR: rgb(0,128,0)"><br></span><span style="COLOR: rgb(128,128,128)">///</span><span style="COLOR: rgb(0,128,0)">&nbsp;获取带令牌请求的URL<br></span><span style="COLOR: rgb(128,128,128)">///</span><span style="COLOR: rgb(0,128,0)">&nbsp;在当前URL中附加上令牌请求参数<br></span><span style="COLOR: rgb(128,128,128)">///</span><span style="COLOR: rgb(0,128,0)">&nbsp;</span><span style="COLOR: rgb(128,128,128)">&lt;/summary&gt;</span><span style="COLOR: rgb(0,128,0)"><br></span><span style="COLOR: rgb(128,128,128)">///</span><span style="COLOR: rgb(0,128,0)">&nbsp;</span><span style="COLOR: rgb(128,128,128)">&lt;returns&gt;&lt;/returns&gt;</span><span style="COLOR: rgb(128,128,128)"><br></span><span style="COLOR: rgb(0,0,0)">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: rgb(0,0,255)">private</span><span style="COLOR: rgb(0,0,0)">&nbsp;</span><span style="COLOR: rgb(0,0,255)">string</span><span style="COLOR: rgb(0,0,0)">&nbsp;getTokenURL()<br>{<br></span><span style="COLOR: rgb(0,0,255)">string</span><span style="COLOR: rgb(0,0,0)">&nbsp;url&nbsp;</span><span style="COLOR: rgb(0,0,0)">=</span><span style="COLOR: rgb(0,0,0)">&nbsp;Request.Url.AbsoluteUri;<br>Regex&nbsp;reg&nbsp;</span><span style="COLOR: rgb(0,0,0)">=</span><span style="COLOR: rgb(0,0,0)">&nbsp;</span><span style="COLOR: rgb(0,0,255)">new</span><span style="COLOR: rgb(0,0,0)">&nbsp;Regex(</span><span style="COLOR: rgb(128,0,0)">@"</span><span style="COLOR: rgb(128,0,0)">^.*\?.+=.+$</span><span style="COLOR: rgb(128,0,0)">"</span><span style="COLOR: rgb(0,0,0)">);<br></span><span style="COLOR: rgb(0,0,255)">if</span><span style="COLOR: rgb(0,0,0)">&nbsp;(reg.IsMatch(url))<br>url&nbsp;</span><span style="COLOR: rgb(0,0,0)">+=</span><span style="COLOR: rgb(0,0,0)">&nbsp;</span><span style="COLOR: rgb(128,0,0)">"</span><span style="COLOR: rgb(128,0,0)">&amp;Token=$Token$</span><span style="COLOR: rgb(128,0,0)">"</span><span style="COLOR: rgb(0,0,0)">;<br></span><span style="COLOR: rgb(0,0,255)">else</span><span style="COLOR: rgb(0,0,0)"><br>url&nbsp;</span><span style="COLOR: rgb(0,0,0)">+=</span><span style="COLOR: rgb(0,0,0)">&nbsp;</span><span style="COLOR: rgb(128,0,0)">"</span><span style="COLOR: rgb(128,0,0)">?Token=$Token$</span><span style="COLOR: rgb(128,0,0)">"</span><span style="COLOR: rgb(0,0,0)">;<br><br></span><span style="COLOR: rgb(0,0,255)">return</span><span style="COLOR: rgb(0,0,0)">&nbsp;</span><span style="COLOR: rgb(128,0,0)">"</span><span style="COLOR: rgb(128,0,0)">http://www.passport.com/gettoken.aspx?BackURL=</span><span style="COLOR: rgb(128,0,0)">"</span><span style="COLOR: rgb(0,0,0)">&nbsp;</span><span style="COLOR: rgb(0,0,0)">+</span><span style="COLOR: rgb(0,0,0)">&nbsp;Server.UrlEncode(url);<br>}<br><br></span><span style="COLOR: rgb(128,128,128)">///</span><span style="COLOR: rgb(0,128,0)">&nbsp;</span><span style="COLOR: rgb(128,128,128)">&lt;summary&gt;</span><span style="COLOR: rgb(0,128,0)"><br></span><span style="COLOR: rgb(128,128,128)">///</span><span style="COLOR: rgb(0,128,0)">&nbsp;去掉URL中的令牌<br></span><span style="COLOR: rgb(128,128,128)">///</span><span style="COLOR: rgb(0,128,0)">&nbsp;在当前URL中去掉令牌参数<br></span><span style="COLOR: rgb(128,128,128)">///</span><span style="COLOR: rgb(0,128,0)">&nbsp;</span><span style="COLOR: rgb(128,128,128)">&lt;/summary&gt;</span><span style="COLOR: rgb(0,128,0)"><br></span><span style="COLOR: rgb(128,128,128)">///</span><span style="COLOR: rgb(0,128,0)">&nbsp;</span><span style="COLOR: rgb(128,128,128)">&lt;returns&gt;&lt;/returns&gt;</span><span style="COLOR: rgb(128,128,128)"><br></span><span style="COLOR: rgb(0,0,0)">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: rgb(0,0,255)">private</span><span style="COLOR: rgb(0,0,0)">&nbsp;</span><span style="COLOR: rgb(0,0,255)">string</span><span style="COLOR: rgb(0,0,0)">&nbsp;replaceToken()<br>{<br></span><span style="COLOR: rgb(0,0,255)">string</span><span style="COLOR: rgb(0,0,0)">&nbsp;url&nbsp;</span><span style="COLOR: rgb(0,0,0)">=</span><span style="COLOR: rgb(0,0,0)">&nbsp;Request.Url.AbsoluteUri;<br>url&nbsp;</span><span style="COLOR: rgb(0,0,0)">=</span><span style="COLOR: rgb(0,0,0)">&nbsp;Regex.Replace(url,&nbsp;</span><span style="COLOR: rgb(128,0,0)">@"</span><span style="COLOR: rgb(128,0,0)">(\?|&amp;)Token=.*</span><span style="COLOR: rgb(128,0,0)">"</span><span style="COLOR: rgb(0,0,0)">,&nbsp;</span><span style="COLOR: rgb(128,0,0)">""</span><span style="COLOR: rgb(0,0,0)">,&nbsp;RegexOptions.IgnoreCase);<br></span><span style="COLOR: rgb(0,0,255)">return</span><span style="COLOR: rgb(0,0,0)">&nbsp;</span><span style="COLOR: rgb(128,0,0)">"</span><span style="COLOR: rgb(128,0,0)">http://www.passport.com/userlogin.aspx?BackURL=</span><span style="COLOR: rgb(128,0,0)">"</span><span style="COLOR: rgb(0,0,0)">&nbsp;</span><span style="COLOR: rgb(0,0,0)">+</span><span style="COLOR: rgb(0,0,0)">&nbsp;Server.UrlEncode(url);<br>}<br><br>}</span><span style="COLOR: rgb(0,128,0)">//</span><span style="COLOR: rgb(0,128,0)">end&nbsp;class</span><span style="COLOR: rgb(0,128,0)"><br></span><span style="COLOR: rgb(0,0,0)">}<br></span></div>
<p dir=ltr>&nbsp;</p>
<p dir=ltr><strong>用户退出：</strong>用户退出时分别清空主站凭证与当前分站凭证。如果要求A站点退出，B、C站点也退出，可自行扩展接口清空每个分站凭证。</p>
<p dir=ltr>&nbsp;</p>
<p dir=ltr><strong>主站过期凭证/令牌清除</strong>：定时清除(DataTable)Cache[&#8220;CERT&#8221;]中timeout字段超过当前时间的记录。</p>
<p dir=ltr>&nbsp;</p>
<p dir=ltr>&nbsp;</p>
<p dir=ltr><a style="COLOR: rgb(255,0,0); FONT-SIZE: 16px; TEXT-DECORATION: underline" title=点击此处下载DEMO target=_blank>点击此处下载DEMO</a>&nbsp;</p>
<p dir=ltr><strong><span style="FONT-SIZE: 12pt">1.</span><span style="FONT-SIZE: 12pt">在</span><span style="FONT-SIZE: 12pt">IIS</span><span style="FONT-SIZE: 12pt">中配置站点</span></strong></p>
<p><span>配置</span>4<span>个站点指向相应的目录，并分别指定</span>4<span>个站点的主机头：</span></p>
<p><a><font color=#0066aa>http://www.passport.com/</font></a></p>
<p><a><font color=#0066aa>http://www.a.com/</font></a></p>
<p><a><font color=#0066aa>http://www.b.com/</font></a></p>
<p><a><font color=#0066aa>http://www.c.com/</font></a>&nbsp;</p>
<h2><span style="FONT-SIZE: 12pt">2.</span><span style="FONT-SIZE: 12pt">修改</span><span style="FONT-SIZE: 12pt">hosts</span><span style="FONT-SIZE: 12pt">文件以将域名解析到本地站点</span></h2>
<p>127.0.0.1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <a><font color=#0066aa>http://www.passport.com/</font></a></p>
<p>127.0.0.1<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <a><font color=#0066aa>http://www.a.com/</font></a> </span></p>
<p>127.0.0.1 <span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <a><font color=#0066aa>http://www.b.com/</font></a> </span></p>
<p dir=ltr>127.0.0.1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a><font color=#0066aa>http://www.c.com/</font></a></p>
<p dir=ltr></p>
<p dir=ltr>转载：http://www.cnblogs.com/maizitongxue/archive/2009/07/31/1536059.html</p>
</div>
</div>
<img src ="http://www.cnitblog.com/MartinYao/aggbug/67054.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/MartinYao/" target="_blank">玄铁剑</a> 2010-06-27 11:38 <a href="http://www.cnitblog.com/MartinYao/articles/67054.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Asp.net mvc中创建Form令牌</title><link>http://www.cnitblog.com/MartinYao/articles/67053.html</link><dc:creator>玄铁剑</dc:creator><author>玄铁剑</author><pubDate>Sun, 27 Jun 2010 03:35:00 GMT</pubDate><guid>http://www.cnitblog.com/MartinYao/articles/67053.html</guid><description><![CDATA[<h1>在Asp.net mvc中创建Form令牌 </h1>
<div style="WIDTH: 545px" id=mainbar>
<table>
    <tbody>
        <tr>
            <td class=votecell>
            <div class=vote-buttons><img id=question-img-upvote-f5f2f9e3-fe2f-44c2-b7a4-ca24f80b7eb1 class=vote-up src="http://www.itstrike.cn/Content/Images/vote-arrow-up.png" width=40 height=25 jQuery1277609642437="3"> <span id=question-vote-number-f5f2f9e3-fe2f-44c2-b7a4-ca24f80b7eb1 class=vote-count-post>0 </span><img id=question-img-downvote-f5f2f9e3-fe2f-44c2-b7a4-ca24f80b7eb1 class=vote-down src="http://www.itstrike.cn/Content/Images/vote-arrow-down.png" width=40 height=25 jQuery1277609642437="4"> <img class=question-img-favorite alt=star src="http://www.itstrike.cn/Content/Images/vote-favorite-off.png" width=32 height=31 jQuery1277609642437="2">
            <div id=favorite-number><strong></strong></div>
            </div>
            </td>
            <td>
            <div>
            <div style="FLOAT: none" class=post-text>
            <p>Ruby中有提供表单的令牌token，<span style="FONT-FAMILY: Verdana">struts</span><span style="FONT-FAMILY: Verdana">中也有提供token<br>今天为了增加表单提交的安全性，于是也想着在mvc里面模拟一个类似token的东西。<br>做 法很简单，写两个filter就可以了，第一个用来产生token，并且将这个token存入Session或者Cookies中，这个filter在 action产生前触发，于是生成的页面就可以在表单里面写一个hidden域来存放这个生成的token；另外还要再写一个filter来验证表单提交 的时候hidden域中的token跟服务器端保存的token是否一致，这里可以在filter里面重写两个事件，一个在action触发之前判断，一 个在action之后，让这个token失效或者重置。<br></span><br><span style="FONT-FAMILY: Verdana">先在提交页的表单中写<br>&lt;%=Html.AntiForgeryToken() %&gt; <br>这样就会在客户端页面上生成一个类似</span><br><span style="FONT-FAMILY: Verdana"><br></span>&lt;input name="__MVC_AntiForgeryToken" type="hidden" value="FaSCzN4P+6Hg977mdOX4z9pCKOy4vlP6whi0RGD+2L9mbTNGGx4GmN36sE4klJZf" /&gt;<span style="FONT-FAMILY: Verdana"><br>的隐藏域。然后只需要在action头部加上一个系统提供的过滤器，就可以达到目的了，如：</span></p>
            <p>&nbsp;&nbsp; &nbsp; &nbsp;&nbsp; /// &lt;summary&gt;<br>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;/// /Home/Login 登陆<br>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;/// &lt;/summary&gt;<br>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;[Microsoft.Web.Mvc.ValidateAntiForgeryToken]<br>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;public void Login()<span style="FONT-FAMILY: Verdana"><br>其 实这种令牌形式只是让安全性稍稍提高了一下，如果别人要外部提交表单，实际上这种方式照样可以被拦截下来。所有Request信息都是可以伪造的，所以最 好的方法还是增加底层的安全性，如果用Linq to SQL生成实体的话，那么在model那里实际上就可以做过滤了，如：<br><br></span><span style="COLOR: #000000"><span style="FONT-FAMILY: Verdana">&nbsp; &nbsp; </span></span>partial class <span style="COLOR: #000000"><span style="FONT-FAMILY: Verdana">tips : ModelFilter<br>&nbsp; &nbsp; {<br><br>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;</span></span>partial void <span style="COLOR: #000000"><span style="FONT-FAMILY: Verdana">OntitleChanged</span></span><span style="COLOR: #000000"><span style="FONT-FAMILY: Verdana"><br>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;{<br>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;_title </span></span><span style="COLOR: #000000"><span style="FONT-FAMILY: Verdana">=</span></span><span style="COLOR: #000000"><span style="FONT-FAMILY: Verdana"> FilterContent(_title);<br>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;</span></span>if (_title == string.Empty)<span style="COLOR: #000000"><span style="FONT-FAMILY: Verdana"><br>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;{<br>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; </span></span>&nbsp; throw new Exception("not null!");<br>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;}<br>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;}<br><br>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;partial void OncontentChanged()<br>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;{<br>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;_content = FilterJS(_content);<br>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;if (_content == string.Empty)<br>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;{<br>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; throw new Exception("not null!");<br>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;}<span style="COLOR: #000000"><span style="FONT-FAMILY: Verdana"><br>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;}<br>&nbsp; &nbsp; }</span></span><br><span style="FONT-FAMILY: Verdana"><br>有 一个tips的实体，只需要写一个过滤的基类，然后tips实体继承这个基类就可以了，上面只是简单的把title过滤掉HTML标签，把content 过滤掉JS脚本，过滤一般是在Changed事件，如果放在changing事件中，赋值的时候又会把value重新赋给实体了。你也可以根据你的字段来 做过滤，如写邮件格式或者电话号码格式的验证，这些格式验证的话就可以写在changing事件。<br></span></p>
            </div>
            </div>
            </td>
        </tr>
    </tbody>
</table>
</div>
<img src ="http://www.cnitblog.com/MartinYao/aggbug/67053.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/MartinYao/" target="_blank">玄铁剑</a> 2010-06-27 11:35 <a href="http://www.cnitblog.com/MartinYao/articles/67053.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Excel中插入图</title><link>http://www.cnitblog.com/MartinYao/articles/59837.html</link><dc:creator>玄铁剑</dc:creator><author>玄铁剑</author><pubDate>Sat, 04 Jul 2009 13:19:00 GMT</pubDate><guid>http://www.cnitblog.com/MartinYao/articles/59837.html</guid><wfw:comment>http://www.cnitblog.com/MartinYao/comments/59837.html</wfw:comment><comments>http://www.cnitblog.com/MartinYao/articles/59837.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/MartinYao/comments/commentRss/59837.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/MartinYao/services/trackbacks/59837.html</trackback:ping><description><![CDATA[<p>Object o = Type.Missing; <br>Microsoft.Office.Interop.Excel.Application objExcelApp = new Microsoft.Office.Interop.Excel.Application(); <br>objExcelApp.DisplayAlerts = false; <br>objExcelApp.Visible = false; <br>Workbook objWorkbook = objExcelApp.Workbooks.Open("E:\\Book1.xls", o, o, o, o, o, o, o, o, o, o, o, o, o, o); <br>Worksheet objWorksheet = (Worksheet)objWorkbook.ActiveSheet; <br>Shapes objShapes = objWorksheet.Shapes; <br>int shapesCount = objShapes.Count; <br>PictureBox objPictureBox = new PictureBox(); <br>Range objRange; <br>for (int i = 1; i &lt; shapesCount; i++) <br>{ <br>&nbsp;&nbsp;&nbsp; objShapes.Item(i).CopyPicture(Appearance.Button, XlCopyPictureFormat.xlBITmap); <br>&nbsp;&nbsp;&nbsp; IDataObject objIDO = (IDataObject)CliPBoard.GetDataObject(); <br>&nbsp;&nbsp;&nbsp; if (objIDO.GetDataPresent(DataFormats.BITmap)) <br>&nbsp;&nbsp;&nbsp; { <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; objPictureBox.Image = (Bitmap)objIDO.GetData(DataFormats.BITmap); <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; objPictureBox.Image.Save("E:\\" + i+ ".jpg"); <br>&nbsp;&nbsp;&nbsp; } <br>&nbsp;&nbsp;&nbsp; else <br>&nbsp;&nbsp;&nbsp; { <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; objPictureBox.Image = null; <br>&nbsp;&nbsp;&nbsp; } <br>} <br>objExcelApp.QuIT(); <br>objExcelApp = null;&nbsp;&nbsp;&nbsp; .</p>
<p><br><span id=comment_body_1414631>worksheetGrid.Shapes.AddPicture(PicturePath, <br>Microsoft.Office.Core.MsoTriState.msoFalse, Microsoft.Office.Core.MsoTriState.msoTrue, <br>picLeft, picTop, 155, 155); <br>MS要将OFFICE11的参考加入.才能引用的 <br></span></p>
<p><strong></strong></p>
<p>调用方法：</p>
<p>
<table style="BORDER-BOTTOM-STYLE: dotted; BORDER-RIGHT-STYLE: dotted; BORDER-TOP-STYLE: dotted; BORDER-LEFT-STYLE: dotted" border=1 cellSpacing=0 borderColor=#cccccc cellPadding=3 width=550 bgColor=#f3f3f3 align=center heihgt="">
    <tbody>
        <tr>
            <td>&lt;!--StartFragment--&gt;MengXianhui.Utility.ExcelReport.InsertPictureToExcel ipt = new MengXianhui.Utility.ExcelReport.InsertPictureToExcel();<br>ipt.Open();<br>ipt.InsertPicture("B2", @"C:\Excellogo.gif");<br>ipt.InsertPicture("B8", @"C:\Excellogo.gif",120,80);<br>ipt.SaveFile(@"C:\ExcelTest.xls");<br>ipt.Dispose();</td>
        </tr>
    </tbody>
</table>
</p>
<strong>
<p>简单包装的类：</p>
<p>
<table style="BORDER-BOTTOM-STYLE: dotted; BORDER-RIGHT-STYLE: dotted; BORDER-TOP-STYLE: dotted; BORDER-LEFT-STYLE: dotted" border=1 cellSpacing=0 borderColor=#cccccc cellPadding=3 width=550 bgColor=#f3f3f3 align=center heihgt="">
    <tbody>
        <tr>
            <td>&lt;!--StartFragment--&gt;using System;<br>using System.<a class=channel_keylink href="http://windows.chinaitlab.com/" target=_blank><font color=#0000ff face=宋体>Windows</font></a>.Forms;<br>using Excel = Microsoft.Office.Interop.Excel;<br><br>namespace MengXianhui.Utility.ExcelReport<br>{<br>&nbsp; class InsertPictureToExcel<br>&nbsp; {<br>&nbsp;&nbsp;&nbsp; /// &lt;summary&gt;<br>&nbsp;&nbsp;&nbsp; /// 打开没有模板的操作。<br>&nbsp;&nbsp;&nbsp; /// &lt;/summary&gt;<br>&nbsp;&nbsp;&nbsp; public void Open( )<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.Open(String.Empty);<br>&nbsp;&nbsp;&nbsp; }<br><br>&nbsp;&nbsp;&nbsp; /// &lt;summary&gt;<br>&nbsp;&nbsp;&nbsp; /// 功能：实现Excel应用程序的打开<br>&nbsp;&nbsp;&nbsp; /// &lt;/summary&gt;<br>&nbsp;&nbsp;&nbsp; /// &lt;param name="TemplateFilePath"&gt;模板文件物理路径&lt;/param&gt;<br>&nbsp;&nbsp;&nbsp; public void Open( string TemplateFilePath )<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //打开对象<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_objExcel = new Excel.Application();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_objExcel.Visible = false;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_objExcel.DisplayAlerts = false;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (m_objExcel.Version != "11.0")<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MessageBox.Show("您的 Excel 版本不是 11.0 （Office 2003），操作可能会出现问题。");<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_objExcel.Quit();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_objBooks = (Excel.Workbooks)m_objExcel.Workbooks;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (TemplateFilePath.Equals(String.Empty))<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_objBook = (Excel._Workbook)(m_objBooks.Add(m_objOpt));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_objBook = m_objBooks.Open(TemplateFilePath, m_objOpt, m_objOpt, m_objOpt, m_objOpt, <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_objOpt, m_objOpt, m_objOpt, m_objOpt, m_objOpt, m_objOpt, m_objOpt, m_objOpt, m_objOpt, m_objOpt);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_objSheets = (Excel.Sheets)m_objBook.Worksheets;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_objSheet = (Excel._Worksheet)(m_objSheets.get_Item(1));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_objExcel.WorkbookBeforeClose += new Excel.AppEvents_WorkbookBeforeCloseEventHandler(m_objExcel_WorkbookBeforeClose);<br>&nbsp;&nbsp;&nbsp; }<br><br>&nbsp;&nbsp;&nbsp; private void m_objExcel_WorkbookBeforeClose( Excel.Workbook m_objBooks, ref bool _Cancel )<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MessageBox.Show("保存完毕！");<br>&nbsp;&nbsp;&nbsp; }<br><br>&nbsp;&nbsp;&nbsp; /// &lt;summary&gt;<br>&nbsp;&nbsp;&nbsp; /// 将图片插入到指定的单元格位置。<br>&nbsp;&nbsp;&nbsp; /// 注意：图片必须是绝对物理路径<br>&nbsp;&nbsp;&nbsp; /// &lt;/summary&gt;<br>&nbsp;&nbsp;&nbsp; /// &lt;param name="RangeName"&gt;单元格名称，例如：B4&lt;/param&gt;<br>&nbsp;&nbsp;&nbsp; /// &lt;param name="PicturePath"&gt;要插入图片的绝对路径。&lt;/param&gt;<br>&nbsp;&nbsp;&nbsp; public void InsertPicture( string RangeName, string PicturePath )<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_objRange = m_objSheet.get_Range(RangeName, m_objOpt);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_objRange.Select();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Excel.Pictures pics = (Excel.Pictures)m_objSheet.Pictures(m_objOpt);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pics.Insert(PicturePath, m_objOpt);<br>&nbsp;&nbsp;&nbsp; }<br><br>&nbsp;&nbsp;&nbsp; /// &lt;summary&gt;<br>&nbsp;&nbsp;&nbsp; /// 将图片插入到指定的单元格位置，并设置图片的宽度和高度。<br>&nbsp;&nbsp;&nbsp; /// 注意：图片必须是绝对物理路径<br>&nbsp;&nbsp;&nbsp; /// &lt;/summary&gt;<br>&nbsp;&nbsp;&nbsp; /// &lt;param name="RangeName"&gt;单元格名称，例如：B4&lt;/param&gt;<br>&nbsp;&nbsp;&nbsp; /// &lt;param name="PicturePath"&gt;要插入图片的绝对路径。&lt;/param&gt;<br>&nbsp;&nbsp;&nbsp; /// &lt;param name="PictuteWidth"&gt;插入后，图片在Excel中显示的宽度。&lt;/param&gt;<br>&nbsp;&nbsp;&nbsp; /// &lt;param name="PictureHeight"&gt;插入后，图片在Excel中显示的高度。&lt;/param&gt;<br>&nbsp;&nbsp;&nbsp; public void InsertPicture( string RangeName, string PicturePath, float PictuteWidth, float PictureHeight )<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_objRange = m_objSheet.get_Range(RangeName, m_objOpt);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_objRange.Select();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; float PicLeft, PicTop;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PicLeft = Convert.ToSingle(m_objRange.Left);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PicTop = Convert.ToSingle(m_objRange.Top);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //参数含义：<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //图片路径<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //是否链接到文件<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //图片插入时是否随文档一起保存<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //图片在文档中的坐标位置（单位：points）<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //图片显示的宽度和高度（单位：points）<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //参数详细信息参见：http://msdn2.microsoft.com/zh-cn/library/aa221765(office.11).aspx<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_objSheet.Shapes.AddPicture(PicturePath, Microsoft.Office.Core.MsoTriState.msoFalse, <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Microsoft.Office.Core.MsoTriState.msoTrue, PicLeft, PicTop, PictuteWidth, PictureHeight);<br>&nbsp;&nbsp;&nbsp; }<br><br>&nbsp;&nbsp;&nbsp; /// &lt;summary&gt;<br>&nbsp;&nbsp;&nbsp; /// 将Excel文件保存到指定的目录，目录必须事先存在，文件名称不一定要存在。<br>&nbsp;&nbsp;&nbsp; /// &lt;/summary&gt;<br>&nbsp;&nbsp;&nbsp; /// &lt;param name="OutputFilePath"&gt;要保存成的文件的全路径。&lt;/param&gt;<br>&nbsp;&nbsp;&nbsp; public void SaveFile( string OutputFilePath )<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_objBook.SaveAs(OutputFilePath, m_objOpt, m_objOpt,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_objOpt, m_objOpt, m_objOpt, Excel.XlSaveAsAccessMode.xlNoChange,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_objOpt, m_objOpt, m_objOpt, m_objOpt, m_objOpt);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.Close();<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; /// &lt;summary&gt;<br>&nbsp;&nbsp;&nbsp; /// 关闭应用程序<br>&nbsp;&nbsp;&nbsp; /// &lt;/summary&gt;<br>&nbsp;&nbsp;&nbsp; private void Close( )<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_objBook.Close(false, m_objOpt, m_objOpt);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_objExcel.Quit();<br>&nbsp;&nbsp;&nbsp; }<br><br>&nbsp;&nbsp;&nbsp; /// &lt;summary&gt;<br>&nbsp;&nbsp;&nbsp; /// 释放所引用的COM对象。注意：这个过程一定要执行。<br>&nbsp;&nbsp;&nbsp; /// &lt;/summary&gt;<br>&nbsp;&nbsp;&nbsp; public void Dispose( )<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ReleaseObj(m_objSheets);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ReleaseObj(m_objBook);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ReleaseObj(m_objBooks);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ReleaseObj(m_objExcel);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.GC.Collect();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.GC.WaitForPendingFinalizers();<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; /// &lt;summary&gt;<br>&nbsp;&nbsp;&nbsp; /// 释放对象，内部调用<br>&nbsp;&nbsp;&nbsp; /// &lt;/summary&gt;<br>&nbsp;&nbsp;&nbsp; /// &lt;param name="o"&gt;&lt;/param&gt;<br>&nbsp;&nbsp;&nbsp; private void ReleaseObj( object o )<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.Runtime.InteropServices.Marshal.ReleaseComObject(o);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; catch { }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; finally { o = null; }<br>&nbsp;&nbsp;&nbsp; }<br><br>&nbsp;&nbsp;&nbsp; private Excel.Application m_objExcel = null;<br>&nbsp;&nbsp;&nbsp; private Excel.Workbooks m_objBooks = null;<br>&nbsp;&nbsp;&nbsp; private Excel._Workbook m_objBook = null;<br>&nbsp;&nbsp;&nbsp; private Excel.Sheets m_objSheets = null;<br>&nbsp;&nbsp;&nbsp; private Excel._Worksheet m_objSheet = null;<br>&nbsp;&nbsp;&nbsp; private Excel.Range m_objRange = null;<br>&nbsp;&nbsp;&nbsp; private object m_objOpt = System.Reflection.Missing.Value;<br>&nbsp; }<br>}&nbsp;</td>
        </tr>
    </tbody>
</table>
</strong><span><br></span></p>
<p><span><br></span>&nbsp;</p>
<p><span>&nbsp;</p>
</span>
<img src ="http://www.cnitblog.com/MartinYao/aggbug/59837.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/MartinYao/" target="_blank">玄铁剑</a> 2009-07-04 21:19 <a href="http://www.cnitblog.com/MartinYao/articles/59837.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Generate Excel files without using Microsoft Excel</title><link>http://www.cnitblog.com/MartinYao/articles/59836.html</link><dc:creator>玄铁剑</dc:creator><author>玄铁剑</author><pubDate>Sat, 04 Jul 2009 13:18:00 GMT</pubDate><guid>http://www.cnitblog.com/MartinYao/articles/59836.html</guid><wfw:comment>http://www.cnitblog.com/MartinYao/comments/59836.html</wfw:comment><comments>http://www.cnitblog.com/MartinYao/articles/59836.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/MartinYao/comments/commentRss/59836.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/MartinYao/services/trackbacks/59836.html</trackback:ping><description><![CDATA[<ul class=download>
    <li><a href="http://www.codeproject.com/KB/office/biffcsharp/XLSExportDemo.zip"><u><font color=#0066cc>Download demo project and source files - 4.17 KB</font></u></a> </li>
</ul>
<h2>Introduction</h2>
<p>Generating Excel files from web pages or other applications is a major subject in many articles. I'd like to present here a solution that does not require MS Excel to be installed on the target computer. This article shows how to create Microsoft Excel <a href="http://en.wikipedia.org/wiki/Microsoft_Excel" target=_blank><u><font color=#0066cc>Binary Interchange File Format</font></u></a> (BIFF) without using Microsoft Excel. BIFF is the native file format for Excel data, and can be viewed and modified in Microsoft Excel 97 or later.</p>
<h2>Approaches and problems</h2>
<p>The technique that is most frequently used to transfer data to an Excel workbooks is <a href="http://www.codeproject.com/KB/office/Simple_Excel_Automation.aspx"><u><font color=#0066cc>Automation</font></u></a>. With Automation, you can call methods and properties that are specific to Excel tasks, but this solution has many drawbacks. Some of them are described in the <a href="http://support.microsoft.com/default.aspx?scid=kb;EN-US;q257757" target=_blank><u><font color=#0066cc>Microsoft Knowledge Base</font></u></a>. Additionally, you have to manage the lifetime of the temporary XLS files created on the server. Also, it is slow, because Excel runs in a separate process.</p>
<h2>ExcelWriter class</h2>
<p>The solution presented here is to write directly to a stream in Excel binary file format. For this purpose, I created very small C# class called <code>ExcelWriter</code>.</p>
<p><img src="http://www.codeproject.com/KB/office/biffcsharp/ExcelWriterDiagram.jpg" width=250 height=303></p>
<div class=caption>ExcelWriter class diagram</div>
<div style="WIDTH: 100%" id=premain0 class=SmallText><img style="CURSOR: pointer" id=preimg0 src="http://www.codeproject.com/images/minus.gif" width=9 height=9 preid="0"><span style="MARGIN-BOTTOM: 0px; CURSOR: pointer" id=precollapse0 preid="0"> Collapse</span><img style="MARGIN-LEFT: 35px" src="http://www.codeproject.com/images/copy_16.png" width=16 height=16><a href="http://www.codeproject.com/KB/office/biffcsharp.aspx#" preid="0"><u><font color=#0066cc> Copy Code</font></u></a></div>
<pre style="MARGIN-TOP: 0px" id=pre0 lang=cs><span class=code-SummaryComment>///</span><span class=code-comment> <span class=code-SummaryComment>&lt;</span><span class=code-SummaryComment>summary</span><span class=code-SummaryComment>&gt;</span></span>
<span class=code-SummaryComment>///</span><span class=code-comment> Produces Excel file without using Excel</span>
<span class=code-SummaryComment>///</span><span class=code-comment> <span class=code-SummaryComment>&lt;</span><span class=code-SummaryComment>/</span><span class=code-SummaryComment>summary</span><span class=code-SummaryComment>&gt;</span></span>
<span class=code-keyword>public</span> <span class=code-keyword>class</span> ExcelWriter
{
<span class=code-keyword>private</span> Stream stream;
<span class=code-keyword>private</span> BinaryWriter writer;
<span class=code-keyword>private</span> <span class=code-keyword>ushort</span>[] clBegin = { 0x0809, <span class=code-digit>8</span>, <span class=code-digit>0</span>, 0x10, <span class=code-digit>0</span>, <span class=code-digit>0</span> };
<span class=code-keyword>private</span> <span class=code-keyword>ushort</span>[] clEnd = { 0x0A, <span class=code-digit>00</span> };
<span class=code-keyword>private</span> <span class=code-keyword>void</span> WriteUshortArray(<span class=code-keyword>ushort</span>[] value)
{
<span class=code-keyword>for</span> (<span class=code-keyword>int</span> i = <span class=code-digit>0</span>; i &lt; value.Length; i++)
writer.Write(value[i]);
}
<span class=code-SummaryComment>///</span><span class=code-comment> <span class=code-SummaryComment>&lt;</span><span class=code-SummaryComment>summary</span><span class=code-SummaryComment>&gt;</span></span>
<span class=code-SummaryComment>///</span><span class=code-comment> Initializes a new instance of the <span class=code-SummaryComment>&lt;</span><span class=code-SummaryComment>see</span> <span class=code-SummaryComment>cref="ExcelWriter"</span><span class=code-SummaryComment>/</span><span class=code-SummaryComment>&gt;</span> class.</span>
<span class=code-SummaryComment>///</span><span class=code-comment> <span class=code-SummaryComment>&lt;</span><span class=code-SummaryComment>/</span><span class=code-SummaryComment>summary</span><span class=code-SummaryComment>&gt;</span></span>
<span class=code-SummaryComment>///</span><span class=code-comment> <span class=code-SummaryComment>&lt;</span><span class=code-SummaryComment>param</span> <span class=code-SummaryComment>name="stream"</span><span class=code-SummaryComment>&gt;</span>The stream.<span class=code-SummaryComment>&lt;</span><span class=code-SummaryComment>/</span><span class=code-SummaryComment>param</span><span class=code-SummaryComment>&gt;</span></span>
<span class=code-keyword>public</span> ExcelWriter(Stream stream)
{
<span class=code-keyword>this</span>.stream = stream;
writer = <span class=code-keyword>new</span> BinaryWriter(stream);
}
<span class=code-SummaryComment>///</span><span class=code-comment> <span class=code-SummaryComment>&lt;</span><span class=code-SummaryComment>summary</span><span class=code-SummaryComment>&gt;</span></span>
<span class=code-SummaryComment>///</span><span class=code-comment> Writes the text cell value.</span>
<span class=code-SummaryComment>///</span><span class=code-comment> <span class=code-SummaryComment>&lt;</span><span class=code-SummaryComment>/</span><span class=code-SummaryComment>summary</span><span class=code-SummaryComment>&gt;</span></span>
<span class=code-SummaryComment>///</span><span class=code-comment> <span class=code-SummaryComment>&lt;</span><span class=code-SummaryComment>param</span> <span class=code-SummaryComment>name="row"</span><span class=code-SummaryComment>&gt;</span>The row.<span class=code-SummaryComment>&lt;</span><span class=code-SummaryComment>/</span><span class=code-SummaryComment>param</span><span class=code-SummaryComment>&gt;</span></span>
<span class=code-SummaryComment>///</span><span class=code-comment> <span class=code-SummaryComment>&lt;</span><span class=code-SummaryComment>param</span> <span class=code-SummaryComment>name="col"</span><span class=code-SummaryComment>&gt;</span>The col.<span class=code-SummaryComment>&lt;</span><span class=code-SummaryComment>/</span><span class=code-SummaryComment>param</span><span class=code-SummaryComment>&gt;</span></span>
<span class=code-SummaryComment>///</span><span class=code-comment> <span class=code-SummaryComment>&lt;</span><span class=code-SummaryComment>param</span> <span class=code-SummaryComment>name="value"</span><span class=code-SummaryComment>&gt;</span>The string value.<span class=code-SummaryComment>&lt;</span><span class=code-SummaryComment>/</span><span class=code-SummaryComment>param</span><span class=code-SummaryComment>&gt;</span></span>
<span class=code-keyword>public</span> <span class=code-keyword>void</span> WriteCell(<span class=code-keyword>int</span> row, <span class=code-keyword>int</span> col, <span class=code-keyword>string</span> value)
{
<span class=code-keyword>ushort</span>[] clData = { 0x0204, <span class=code-digit>0</span>, <span class=code-digit>0</span>, <span class=code-digit>0</span>, <span class=code-digit>0</span>, <span class=code-digit>0</span> };
<span class=code-keyword>int</span> iLen = value.Length;
<span class=code-keyword>byte</span>[] plainText = Encoding.ASCII.GetBytes(value);
clData[<span class=code-digit>1</span>] = (<span class=code-keyword>ushort</span>)(<span class=code-digit>8</span> + iLen);
clData[<span class=code-digit>2</span>] = (<span class=code-keyword>ushort</span>)row;
clData[<span class=code-digit>3</span>] = (<span class=code-keyword>ushort</span>)col;
clData[<span class=code-digit>5</span>] = (<span class=code-keyword>ushort</span>)iLen;
WriteUshortArray(clData);
writer.Write(plainText);
}
<span class=code-SummaryComment>///</span><span class=code-comment> <span class=code-SummaryComment>&lt;</span><span class=code-SummaryComment>summary</span><span class=code-SummaryComment>&gt;</span></span>
<span class=code-SummaryComment>///</span><span class=code-comment> Writes the integer cell value.</span>
<span class=code-SummaryComment>///</span><span class=code-comment> <span class=code-SummaryComment>&lt;</span><span class=code-SummaryComment>/</span><span class=code-SummaryComment>summary</span><span class=code-SummaryComment>&gt;</span></span>
<span class=code-SummaryComment>///</span><span class=code-comment> <span class=code-SummaryComment>&lt;</span><span class=code-SummaryComment>param</span> <span class=code-SummaryComment>name="row"</span><span class=code-SummaryComment>&gt;</span>The row number.<span class=code-SummaryComment>&lt;</span><span class=code-SummaryComment>/</span><span class=code-SummaryComment>param</span><span class=code-SummaryComment>&gt;</span></span>
<span class=code-SummaryComment>///</span><span class=code-comment> <span class=code-SummaryComment>&lt;</span><span class=code-SummaryComment>param</span> <span class=code-SummaryComment>name="col"</span><span class=code-SummaryComment>&gt;</span>The column number.<span class=code-SummaryComment>&lt;</span><span class=code-SummaryComment>/</span><span class=code-SummaryComment>param</span><span class=code-SummaryComment>&gt;</span></span>
<span class=code-SummaryComment>///</span><span class=code-comment> <span class=code-SummaryComment>&lt;</span><span class=code-SummaryComment>param</span> <span class=code-SummaryComment>name="value"</span><span class=code-SummaryComment>&gt;</span>The value.<span class=code-SummaryComment>&lt;</span><span class=code-SummaryComment>/</span><span class=code-SummaryComment>param</span><span class=code-SummaryComment>&gt;</span></span>
<span class=code-keyword>public</span> <span class=code-keyword>void</span> WriteCell(<span class=code-keyword>int</span> row, <span class=code-keyword>int</span> col, <span class=code-keyword>int</span> value)
{
<span class=code-keyword>ushort</span>[] clData = { 0x027E, <span class=code-digit>10</span>, <span class=code-digit>0</span>, <span class=code-digit>0</span>, <span class=code-digit>0</span> };
clData[<span class=code-digit>2</span>] = (<span class=code-keyword>ushort</span>)row;
clData[<span class=code-digit>3</span>] = (<span class=code-keyword>ushort</span>)col;
WriteUshortArray(clData);
<span class=code-keyword>int</span> iValue = (value &lt;&lt; <span class=code-digit>2</span>) | <span class=code-digit>2</span>;
writer.Write(iValue);
}
<span class=code-SummaryComment>///</span><span class=code-comment> <span class=code-SummaryComment>&lt;</span><span class=code-SummaryComment>summary</span><span class=code-SummaryComment>&gt;</span></span>
<span class=code-SummaryComment>///</span><span class=code-comment> Writes the double cell value.</span>
<span class=code-SummaryComment>///</span><span class=code-comment> <span class=code-SummaryComment>&lt;</span><span class=code-SummaryComment>/</span><span class=code-SummaryComment>summary</span><span class=code-SummaryComment>&gt;</span></span>
<span class=code-SummaryComment>///</span><span class=code-comment> <span class=code-SummaryComment>&lt;</span><span class=code-SummaryComment>param</span> <span class=code-SummaryComment>name="row"</span><span class=code-SummaryComment>&gt;</span>The row number.<span class=code-SummaryComment>&lt;</span><span class=code-SummaryComment>/</span><span class=code-SummaryComment>param</span><span class=code-SummaryComment>&gt;</span></span>
<span class=code-SummaryComment>///</span><span class=code-comment> <span class=code-SummaryComment>&lt;</span><span class=code-SummaryComment>param</span> <span class=code-SummaryComment>name="col"</span><span class=code-SummaryComment>&gt;</span>The column number.<span class=code-SummaryComment>&lt;</span><span class=code-SummaryComment>/</span><span class=code-SummaryComment>param</span><span class=code-SummaryComment>&gt;</span></span>
<span class=code-SummaryComment>///</span><span class=code-comment> <span class=code-SummaryComment>&lt;</span><span class=code-SummaryComment>param</span> <span class=code-SummaryComment>name="value"</span><span class=code-SummaryComment>&gt;</span>The value.<span class=code-SummaryComment>&lt;</span><span class=code-SummaryComment>/</span><span class=code-SummaryComment>param</span><span class=code-SummaryComment>&gt;</span></span>
<span class=code-keyword>public</span> <span class=code-keyword>void</span> WriteCell(<span class=code-keyword>int</span> row, <span class=code-keyword>int</span> col, <span class=code-keyword>double</span> value)
{
<span class=code-keyword>ushort</span>[] clData = { 0x0203, <span class=code-digit>14</span>, <span class=code-digit>0</span>, <span class=code-digit>0</span>, <span class=code-digit>0</span> };
clData[<span class=code-digit>2</span>] = (<span class=code-keyword>ushort</span>)row;
clData[<span class=code-digit>3</span>] = (<span class=code-keyword>ushort</span>)col;
WriteUshortArray(clData);
writer.Write(value);
}
<span class=code-SummaryComment>///</span><span class=code-comment> <span class=code-SummaryComment>&lt;</span><span class=code-SummaryComment>summary</span><span class=code-SummaryComment>&gt;</span></span>
<span class=code-SummaryComment>///</span><span class=code-comment> Writes the empty cell.</span>
<span class=code-SummaryComment>///</span><span class=code-comment> <span class=code-SummaryComment>&lt;</span><span class=code-SummaryComment>/</span><span class=code-SummaryComment>summary</span><span class=code-SummaryComment>&gt;</span></span>
<span class=code-SummaryComment>///</span><span class=code-comment> <span class=code-SummaryComment>&lt;</span><span class=code-SummaryComment>param</span> <span class=code-SummaryComment>name="row"</span><span class=code-SummaryComment>&gt;</span>The row number.<span class=code-SummaryComment>&lt;</span><span class=code-SummaryComment>/</span><span class=code-SummaryComment>param</span><span class=code-SummaryComment>&gt;</span></span>
<span class=code-SummaryComment>///</span><span class=code-comment> <span class=code-SummaryComment>&lt;</span><span class=code-SummaryComment>param</span> <span class=code-SummaryComment>name="col"</span><span class=code-SummaryComment>&gt;</span>The column number.<span class=code-SummaryComment>&lt;</span><span class=code-SummaryComment>/</span><span class=code-SummaryComment>param</span><span class=code-SummaryComment>&gt;</span></span>
<span class=code-keyword>public</span> <span class=code-keyword>void</span> WriteCell(<span class=code-keyword>int</span> row, <span class=code-keyword>int</span> col)
{
<span class=code-keyword>ushort</span>[] clData = { 0x0201, <span class=code-digit>6</span>, <span class=code-digit>0</span>, <span class=code-digit>0</span>, 0x17 };
clData[<span class=code-digit>2</span>] = (<span class=code-keyword>ushort</span>)row;
clData[<span class=code-digit>3</span>] = (<span class=code-keyword>ushort</span>)col;
WriteUshortArray(clData);
}
<span class=code-SummaryComment>///</span><span class=code-comment> <span class=code-SummaryComment>&lt;</span><span class=code-SummaryComment>summary</span><span class=code-SummaryComment>&gt;</span></span>
<span class=code-SummaryComment>///</span><span class=code-comment> Must be called once for creating XLS file header</span>
<span class=code-SummaryComment>///</span><span class=code-comment> <span class=code-SummaryComment>&lt;</span><span class=code-SummaryComment>/</span><span class=code-SummaryComment>summary</span><span class=code-SummaryComment>&gt;</span></span>
<span class=code-keyword>public</span> <span class=code-keyword>void</span> BeginWrite()
{
WriteUshortArray(clBegin);
}
<span class=code-SummaryComment>///</span><span class=code-comment> <span class=code-SummaryComment>&lt;</span><span class=code-SummaryComment>summary</span><span class=code-SummaryComment>&gt;</span></span>
<span class=code-SummaryComment>///</span><span class=code-comment> Ends the writing operation, but do not close the stream</span>
<span class=code-SummaryComment>///</span><span class=code-comment> <span class=code-SummaryComment>&lt;</span><span class=code-SummaryComment>/</span><span class=code-SummaryComment>summary</span><span class=code-SummaryComment>&gt;</span></span>
<span class=code-keyword>public</span> <span class=code-keyword>void</span> EndWrite()
{
WriteUshortArray(clEnd);
writer.Flush();
}
}</pre>
<h2>Using the code</h2>
<p>The <code>ExcelWriter</code> sample is a console application that writes an XLS file to the name and location specified. The XLS file that is created is simply an empty spreadsheet. Then, you can start to write cell values by calling the <code>WriteCell</code> method with the row number, column number, and a cell value as parameters:</p>
<div style="WIDTH: 100%" id=premain1 class=SmallText><img style="CURSOR: pointer" id=preimg1 src="http://www.codeproject.com/images/minus.gif" width=9 height=9 preid="1"><span style="MARGIN-BOTTOM: 0px; CURSOR: pointer" id=precollapse1 preid="1"> Collapse</span><img style="MARGIN-LEFT: 35px" src="http://www.codeproject.com/images/copy_16.png" width=16 height=16><a href="http://www.codeproject.com/KB/office/biffcsharp.aspx#" preid="1"><u><font color=#0066cc> Copy Code</font></u></a></div>
<pre style="MARGIN-TOP: 0px" id=pre1 lang=cs><span class=code-keyword>namespace</span> XLSExportDemo
{
<span class=code-keyword>class</span> Program
{
<span class=code-keyword>static</span> <span class=code-keyword>void</span> Main(<span class=code-keyword>string</span>[] args)
{
FileStream stream = <span class=code-keyword>new</span> FileStream(<span class=code-string>"</span><span class=code-string>demo.xls"</span>, FileMode.OpenOrCreate);
ExcelWriter writer = <span class=code-keyword>new</span> ExcelWriter(stream);
writer.BeginWrite();
writer.WriteCell(<span class=code-digit>0</span>, <span class=code-digit>0</span>, <span class=code-string>"</span><span class=code-string>ExcelWriter Demo"</span>);
writer.WriteCell(<span class=code-digit>1</span>, <span class=code-digit>0</span>, <span class=code-string>"</span><span class=code-string>int"</span>);
writer.WriteCell(<span class=code-digit>1</span>, <span class=code-digit>1</span>, <span class=code-digit>10</span>);
writer.WriteCell(<span class=code-digit>2</span>, <span class=code-digit>0</span>, <span class=code-string>"</span><span class=code-string>double"</span>);
writer.WriteCell(<span class=code-digit>2</span>, <span class=code-digit>1</span>, <span class=code-digit>1</span>.<span class=code-digit>5</span>);
writer.WriteCell(<span class=code-digit>3</span>, <span class=code-digit>0</span>, <span class=code-string>"</span><span class=code-string>empty"</span>);
writer.WriteCell(<span class=code-digit>3</span>, <span class=code-digit>1</span>);
writer.EndWrite();
stream.Close();
}
}
}</pre>
<p>The picture below shows the final result of this sample:</p>
<p><img src="http://www.codeproject.com/KB/office/biffcsharp/ExcelWriterDemo.jpg" width=296 height=171></p>
<p>The <code>ExcelWriter</code> class supports numeric and text values for the cells, and can be used for exporting data from the database to an <a href="http://en.wikipedia.org/wiki/Microsoft_Excel_file_format#File_formats" target=_blank><u><font color=#0066cc>XLS</font></u></a> file, or for producing an XLS stream from a website on the fly.</p>
<p>I hope this article gives you a head start in working with Excel files from .NET and C#.</p>
<h2>History</h2>
<img src ="http://www.cnitblog.com/MartinYao/aggbug/59836.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/MartinYao/" target="_blank">玄铁剑</a> 2009-07-04 21:18 <a href="http://www.cnitblog.com/MartinYao/articles/59836.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>防止重复提交</title><link>http://www.cnitblog.com/MartinYao/articles/59096.html</link><dc:creator>玄铁剑</dc:creator><author>玄铁剑</author><pubDate>Sat, 06 Jun 2009 15:08:00 GMT</pubDate><guid>http://www.cnitblog.com/MartinYao/articles/59096.html</guid><wfw:comment>http://www.cnitblog.com/MartinYao/comments/59096.html</wfw:comment><comments>http://www.cnitblog.com/MartinYao/articles/59096.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/MartinYao/comments/commentRss/59096.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/MartinYao/services/trackbacks/59096.html</trackback:ping><description><![CDATA[<span style="COLOR: #0000ff">public</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">class</span><span style="COLOR: #000000">&nbsp;RefreshServe&nbsp;:&nbsp;System.Web.UI.Page<br>&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">private</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">static</span><span style="COLOR: #000000">&nbsp;ILog&nbsp;log&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;LogManager.GetLogger(</span><span style="COLOR: #0000ff">typeof</span><span style="COLOR: #000000">(RefreshServe));<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">private</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">readonly</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">string</span><span style="COLOR: #000000">&nbsp;REFRESH_TICKET_NAME&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #800000">"</span><span style="COLOR: #800000">__RefreshTicketArray</span><span style="COLOR: #800000">"</span><span style="COLOR: #000000">;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">private</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">readonly</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">string</span><span style="COLOR: #000000">&nbsp;HIDDEN_FIELD_NAME&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #800000">"</span><span style="COLOR: #800000">__RefreshHiddenField</span><span style="COLOR: #800000">"</span><span style="COLOR: #000000">;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">private</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">readonly</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">string</span><span style="COLOR: #000000">&nbsp;HIDDEN_PAGE_GUID&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #800000">"</span><span style="COLOR: #800000">__RefreshPageGuid</span><span style="COLOR: #800000">"</span><span style="COLOR: #000000">;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #808080">///</span><span style="COLOR: #008000">&nbsp;</span><span style="COLOR: #808080">&lt;summary&gt;</span><span style="COLOR: #008000"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #808080">///</span><span style="COLOR: #008000">&nbsp;为True表示页面刷新,False为正常提交<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #808080">///</span><span style="COLOR: #008000">&nbsp;</span><span style="COLOR: #808080">&lt;/summary&gt;</span><span style="COLOR: #808080"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">bool</span><span style="COLOR: #000000">&nbsp;IsPageRefreshed<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">get</span><span style="COLOR: #000000"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;(IsPostBack&nbsp;</span><span style="COLOR: #000000">&amp;&amp;</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">!</span><span style="COLOR: #000000">CheckRefreshFlag())<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;log.Debug(</span><span style="COLOR: #800000">"</span><span style="COLOR: #800000">刷新了页面</span><span style="COLOR: #800000">"</span><span style="COLOR: #000000">);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">true</span><span style="COLOR: #000000">;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">else</span><span style="COLOR: #000000"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;log.Debug(</span><span style="COLOR: #800000">"</span><span style="COLOR: #800000">正常提交</span><span style="COLOR: #800000">"</span><span style="COLOR: #000000">);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">false</span><span style="COLOR: #000000">;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #808080">///</span><span style="COLOR: #008000">&nbsp;</span><span style="COLOR: #808080">&lt;summary&gt;</span><span style="COLOR: #008000"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #808080">///</span><span style="COLOR: #008000">&nbsp;呈现前更新标识<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #808080">///</span><span style="COLOR: #008000">&nbsp;</span><span style="COLOR: #808080">&lt;/summary&gt;</span><span style="COLOR: #008000"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #808080">///</span><span style="COLOR: #008000">&nbsp;</span><span style="COLOR: #808080">&lt;param&nbsp;name="e"&gt;&lt;/param&gt;</span><span style="COLOR: #808080"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">protected</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">override</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;OnPreRender(EventArgs&nbsp;e)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;log.Debug(</span><span style="COLOR: #800000">"</span><span style="COLOR: #800000">执行OnPreRender</span><span style="COLOR: #800000">"</span><span style="COLOR: #000000">);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">base</span><span style="COLOR: #000000">.OnPreRender(e);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;UpdateRefreshFlag();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br><br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #808080">///</span><span style="COLOR: #008000">&nbsp;</span><span style="COLOR: #808080">&lt;summary&gt;</span><span style="COLOR: #008000"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #808080">///</span><span style="COLOR: #008000">&nbsp;更新标识,正常提交都删除该次提交的时间,并生产当前新的时间<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #808080">///</span><span style="COLOR: #008000">&nbsp;</span><span style="COLOR: #808080">&lt;/summary&gt;</span><span style="COLOR: #808080"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">private</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;UpdateRefreshFlag()<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">#region</span><span style="COLOR: #000000">&nbsp;Cookie模式</span><span style="COLOR: #000000"><br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">注册页面唯一标识并返回</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">string</span><span style="COLOR: #000000">&nbsp;pageGuid&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;SetCurPageGuid();<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;HttpCookie&nbsp;cookie&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;GetRefreshTicket();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;(cookie.Values.Count&nbsp;</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #800080">0</span><span style="COLOR: #000000">)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cookie.Values.Remove(pageGuid);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;log.Debug(</span><span style="COLOR: #800000">"</span><span style="COLOR: #800000">当前清除的cookie变是:</span><span style="COLOR: #800000">"</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">+</span><span style="COLOR: #000000">&nbsp;pageGuid);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">string</span><span style="COLOR: #000000">&nbsp;submitTime&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;DateTime.Now.ToString(</span><span style="COLOR: #800000">"</span><span style="COLOR: #800000">hhmmss.fffff</span><span style="COLOR: #800000">"</span><span style="COLOR: #000000">);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">当前提交时间保存到隐藏域</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ClientScript.RegisterHiddenField(HIDDEN_FIELD_NAME,&nbsp;submitTime);<br><br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;log.Debug(</span><span style="COLOR: #800000">"</span><span style="COLOR: #800000">即将要新增的时间:submitTime:</span><span style="COLOR: #800000">"</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">+</span><span style="COLOR: #000000">&nbsp;submitTime&nbsp;</span><span style="COLOR: #000000">+</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #800000">"</span><span style="COLOR: #800000">&nbsp;&nbsp;Guid:</span><span style="COLOR: #800000">"</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">+</span><span style="COLOR: #000000">&nbsp;pageGuid.ToString());<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cookie.Values.Add(pageGuid,&nbsp;submitTime);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;log.Debug(</span><span style="COLOR: #800000">"</span><span style="COLOR: #800000">UpdateRefreshFlag中当前Cookie中存在的记录数为:</span><span style="COLOR: #800000">"</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">+</span><span style="COLOR: #000000">&nbsp;cookie.Values.Count);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">for</span><span style="COLOR: #000000">&nbsp;(</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;i&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #800080">0</span><span style="COLOR: #000000">;&nbsp;i&nbsp;</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">&nbsp;cookie.Values.Count;&nbsp;i</span><span style="COLOR: #000000">++</span><span style="COLOR: #000000">)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;log.Info(</span><span style="COLOR: #800000">"</span><span style="COLOR: #800000">cookie[</span><span style="COLOR: #800000">"</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">+</span><span style="COLOR: #000000">&nbsp;cookie.Values.GetKey(i)&nbsp;</span><span style="COLOR: #000000">+</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #800000">"</span><span style="COLOR: #800000">]:</span><span style="COLOR: #800000">"</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">+</span><span style="COLOR: #000000">&nbsp;cookie.Values[i]);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Response.AppendCookie(cookie);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">#endregion</span><span style="COLOR: #000000"><br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br><br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #808080">///</span><span style="COLOR: #008000">&nbsp;</span><span style="COLOR: #808080">&lt;summary&gt;</span><span style="COLOR: #008000"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #808080">///</span><span style="COLOR: #008000">&nbsp;验证是否刷新<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #808080">///</span><span style="COLOR: #008000">&nbsp;</span><span style="COLOR: #808080">&lt;/summary&gt;</span><span style="COLOR: #008000"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #808080">///</span><span style="COLOR: #008000">&nbsp;</span><span style="COLOR: #808080">&lt;returns&gt;&lt;/returns&gt;</span><span style="COLOR: #808080"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">private</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">bool</span><span style="COLOR: #000000">&nbsp;CheckRefreshFlag()<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #000000">HttpCookie&nbsp;cookie&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;GetRefreshTicket();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">string</span><span style="COLOR: #000000">&nbsp;pageGuid&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;GetCurPageGuid();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;(cookie.Values.Count&nbsp;</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #800080">0</span><span style="COLOR: #000000">)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">bool</span><span style="COLOR: #000000">&nbsp;flag;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;(cookie.Values[pageGuid]&nbsp;</span><span style="COLOR: #000000">!=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">null</span><span style="COLOR: #000000">)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;flag&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;cookie.Values[pageGuid].IndexOf(GetCurSubmitTime())&nbsp;</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">-</span><span style="COLOR: #800080">1</span><span style="COLOR: #000000">;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">else</span><span style="COLOR: #000000"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;flag&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">true</span><span style="COLOR: #000000">;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">防止出现异常,总是可以提交</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;(flag)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;log.Debug(</span><span style="COLOR: #800000">"</span><span style="COLOR: #800000">提交时间存在,可以提交</span><span style="COLOR: #800000">"</span><span style="COLOR: #000000">);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">else</span><span style="COLOR: #000000"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;log.Debug(</span><span style="COLOR: #800000">"</span><span style="COLOR: #800000">无效的提交时间</span><span style="COLOR: #800000">"</span><span style="COLOR: #000000">);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000">&nbsp;flag;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">true</span><span style="COLOR: #000000">;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br><br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #808080">///</span><span style="COLOR: #008000">&nbsp;</span><span style="COLOR: #808080">&lt;summary&gt;</span><span style="COLOR: #008000"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #808080">///</span><span style="COLOR: #008000">&nbsp;得到已保存的提交时间,没有新建,有返回<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #808080">///</span><span style="COLOR: #008000">&nbsp;</span><span style="COLOR: #808080">&lt;/summary&gt;</span><span style="COLOR: #008000"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #808080">///</span><span style="COLOR: #008000">&nbsp;</span><span style="COLOR: #808080">&lt;returns&gt;&lt;/returns&gt;</span><span style="COLOR: #808080"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">private</span><span style="COLOR: #000000">&nbsp;HttpCookie&nbsp;GetRefreshTicket()<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #000000"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">#region</span><span style="COLOR: #000000">&nbsp;Cookie模式,返回值为Cookie</span><span style="COLOR: #000000"><br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;HttpCookie&nbsp;cookie;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;(Request.Cookies[REFRESH_TICKET_NAME]&nbsp;</span><span style="COLOR: #000000">==</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">null</span><span style="COLOR: #000000">)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cookie&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000">&nbsp;HttpCookie(REFRESH_TICKET_NAME);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Response.AppendCookie(cookie);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;log.Debug(</span><span style="COLOR: #800000">"</span><span style="COLOR: #800000">Cookie不存在，初始化</span><span style="COLOR: #800000">"</span><span style="COLOR: #000000">);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">else</span><span style="COLOR: #000000"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cookie&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;Request.Cookies[REFRESH_TICKET_NAME];<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;log.Debug(</span><span style="COLOR: #800000">"</span><span style="COLOR: #800000">读取已存在的Cookie,当前Cookie中存在的记录数为:</span><span style="COLOR: #800000">"</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">+</span><span style="COLOR: #000000">&nbsp;cookie.Values.Count&nbsp;</span><span style="COLOR: #000000">+</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #800000">"</span><span style="COLOR: #800000">具体有如下几条:</span><span style="COLOR: #800000">"</span><span style="COLOR: #000000">);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">for</span><span style="COLOR: #000000">&nbsp;(</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;i&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #800080">0</span><span style="COLOR: #000000">;&nbsp;i&nbsp;</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">&nbsp;cookie.Values.Count;&nbsp;i</span><span style="COLOR: #000000">++</span><span style="COLOR: #000000">)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;log.Info(</span><span style="COLOR: #800000">"</span><span style="COLOR: #800000">cookie[</span><span style="COLOR: #800000">"</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">+</span><span style="COLOR: #000000">&nbsp;cookie.Values.GetKey(i)&nbsp;</span><span style="COLOR: #000000">+</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #800000">"</span><span style="COLOR: #800000">]:</span><span style="COLOR: #800000">"</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">+</span><span style="COLOR: #000000">&nbsp;cookie.Values[i]);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000">&nbsp;cookie;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">#endregion</span><span style="COLOR: #000000"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br><br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #808080">///</span><span style="COLOR: #008000">&nbsp;</span><span style="COLOR: #808080">&lt;summary&gt;</span><span style="COLOR: #008000"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #808080">///</span><span style="COLOR: #008000">&nbsp;获取当前提交时间<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #808080">///</span><span style="COLOR: #008000">&nbsp;</span><span style="COLOR: #808080">&lt;/summary&gt;</span><span style="COLOR: #008000"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #808080">///</span><span style="COLOR: #008000">&nbsp;</span><span style="COLOR: #808080">&lt;returns&gt;&lt;/returns&gt;</span><span style="COLOR: #808080"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">private</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">string</span><span style="COLOR: #000000">&nbsp;GetCurSubmitTime()<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">string</span><span style="COLOR: #000000">&nbsp;submitTime&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;Request.Params[HIDDEN_FIELD_NAME]&nbsp;</span><span style="COLOR: #000000">==</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">null</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">?</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #800000">""</span><span style="COLOR: #000000">&nbsp;:&nbsp;Request.Params[HIDDEN_FIELD_NAME].ToString();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;log.Debug(</span><span style="COLOR: #800000">"</span><span style="COLOR: #800000">执行GetCurSubmitTime:submitTime为:</span><span style="COLOR: #800000">"</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">+</span><span style="COLOR: #000000">&nbsp;submitTime);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000">&nbsp;submitTime;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br><br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #808080">///</span><span style="COLOR: #008000">&nbsp;</span><span style="COLOR: #808080">&lt;summary&gt;</span><span style="COLOR: #008000"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #808080">///</span><span style="COLOR: #008000">&nbsp;设置页面唯一标识,通过Guid标识来区分每个页面自己的提交时间<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #808080">///</span><span style="COLOR: #008000">&nbsp;</span><span style="COLOR: #808080">&lt;/summary&gt;</span><span style="COLOR: #808080"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">private</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">string</span><span style="COLOR: #000000">&nbsp;SetCurPageGuid()<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">string</span><span style="COLOR: #000000">&nbsp;guid;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;(</span><span style="COLOR: #000000">!</span><span style="COLOR: #000000">IsPostBack)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;(Request.Params[HIDDEN_PAGE_GUID]&nbsp;</span><span style="COLOR: #000000">==</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">null</span><span style="COLOR: #000000">)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;guid&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;System.Guid.NewGuid().ToString();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;log.Debug(</span><span style="COLOR: #800000">"</span><span style="COLOR: #800000">SetCurPageGuid注册了一个新的标识:</span><span style="COLOR: #800000">"</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">+</span><span style="COLOR: #000000">&nbsp;guid);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">else</span><span style="COLOR: #000000"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;guid&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;GetCurPageGuid();<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">else</span><span style="COLOR: #000000"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;guid&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;GetCurPageGuid();&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;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ClientScript.RegisterHiddenField(HIDDEN_PAGE_GUID,&nbsp;guid);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000">&nbsp;guid;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #808080">///</span><span style="COLOR: #008000">&nbsp;</span><span style="COLOR: #808080">&lt;summary&gt;</span><span style="COLOR: #008000"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #808080">///</span><span style="COLOR: #008000">&nbsp;得到当前页面的唯一标识<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #808080">///</span><span style="COLOR: #008000">&nbsp;</span><span style="COLOR: #808080">&lt;/summary&gt;</span><span style="COLOR: #008000"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #808080">///</span><span style="COLOR: #008000">&nbsp;</span><span style="COLOR: #808080">&lt;returns&gt;&lt;/returns&gt;</span><span style="COLOR: #808080"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">private</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">string</span><span style="COLOR: #000000">&nbsp;GetCurPageGuid()<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">string</span><span style="COLOR: #000000">&nbsp;pageGuid&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;Request.Params[HIDDEN_PAGE_GUID]&nbsp;</span><span style="COLOR: #000000">==</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">null</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">?</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #800000">"</span><span style="COLOR: #800000">none</span><span style="COLOR: #800000">"</span><span style="COLOR: #000000">&nbsp;:&nbsp;Request.Params[HIDDEN_PAGE_GUID].ToString();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;log.Debug(</span><span style="COLOR: #800000">"</span><span style="COLOR: #800000">执行GetCurPageGuid()后Page_GUID为:</span><span style="COLOR: #800000">"</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">+</span><span style="COLOR: #000000">&nbsp;pageGuid);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000">&nbsp;pageGuid;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</span>
<p><span style="DISPLAY: none"><span style="COLOR: #000000">}<br></span></span></p>
<p>需要刷新判断功能时新页面只需继承该类就可,通过引用属性IsPageRefreshed识别"为真表示刷新,假则是正常提交",将数据库的操作写在<br>if(!IsPageRefreshed)<br>{<br>&nbsp;&nbsp; 数据库操作<br>}<br>即可,如果是刷新不会执行,代码中注释部分使用的是Session方式保存票证,因为session比较容易丢失且占内存,所以使用cookie,</p>
<img src ="http://www.cnitblog.com/MartinYao/aggbug/59096.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/MartinYao/" target="_blank">玄铁剑</a> 2009-06-06 23:08 <a href="http://www.cnitblog.com/MartinYao/articles/59096.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>代码生成器与 .NET</title><link>http://www.cnitblog.com/MartinYao/articles/52415.html</link><dc:creator>玄铁剑</dc:creator><author>玄铁剑</author><pubDate>Tue, 09 Dec 2008 13:40:00 GMT</pubDate><guid>http://www.cnitblog.com/MartinYao/articles/52415.html</guid><wfw:comment>http://www.cnitblog.com/MartinYao/comments/52415.html</wfw:comment><comments>http://www.cnitblog.com/MartinYao/articles/52415.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/MartinYao/comments/commentRss/52415.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/MartinYao/services/trackbacks/52415.html</trackback:ping><description><![CDATA[<p>Pierre Couzy<br>WinWise</p>
<p><strong>摘要</strong>：代码生成器是您日常生活中的一部分，即使您没有意识到这一点。Pierre Couzy 说明了如何在项目中利用它们。</p>
<h5>本页内容</h5>
<p><a href="http://msdn.microsoft.com/zh-cn/library/ms953317.aspx#EEAA"><img alt=简介 src="http://i.msdn.microsoft.com/ms953317.arrow_px_down(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/ms953317.aspx#EEAA"><font color=#0033cc>简介</font></a> <br><a href="http://msdn.microsoft.com/zh-cn/library/ms953317.aspx#EDAA"><img alt=工具和示例 src="http://i.msdn.microsoft.com/ms953317.arrow_px_down(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/ms953317.aspx#EDAA"><font color=#0033cc>工具和示例</font></a> <br><a href="http://msdn.microsoft.com/zh-cn/library/ms953317.aspx#ECAA"><img alt=版本控制 src="http://i.msdn.microsoft.com/ms953317.arrow_px_down(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/ms953317.aspx#ECAA"><font color=#0033cc>版本控制</font></a> <br><a href="http://msdn.microsoft.com/zh-cn/library/ms953317.aspx#EBAA"><img alt=小结 src="http://i.msdn.microsoft.com/ms953317.arrow_px_down(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/ms953317.aspx#EBAA"><font color=#0033cc>小结</font></a> <br></p>
<h3 id=EEAA>简介</h3>
<p>假设您在一家由 DBA 统治一切的公司里工作：您不能生成只是&#8220;到 Oracle 那里去取一些记录&#8221;的应用程序。您只能依靠存储过程，因为在该级别存在安全层。</p>
<p>生成应用程序通常涉及到下列步骤： </p>
<ol>
    <li>
    <p>创建一批存储过程。 </p>
    <li>
    <p>创建能够与存储过程通信的 C# 类。 </p>
    <li>
    <p>创建将管理 Microsoft ASP.NET 表单或 Microsoft Windows 窗体或者形成另一个层的更高级别的类。 </p>
    </li>
</ol>
<p>步骤 1 和步骤 2 是密切相关的：它们共享大量结构，它们消耗另一个步骤所产生的东西，等等。问题在于，您拥有两种不同的语言（在该示例中，为 PL-SQL 和 C#），并且无法从其中一种语言引用另一种语言。</p>
<p>您说这没有什么大不了的？团队中的第一个开发人员将使一切保持正常，因为他了解 .NET、PL/SQL 以及安全模型的本质。他可能将编写两个东西：首先是一组帮助器类，然后是一个有效的示例 — 于是，其他开发人员将使用这些帮助器类，复制/粘贴有效示例，并针对他们的需要进行修改。</p>
<p>如果您已经在软件行业中工作了足够长的时间，就会知道接下来将会发生什么事情：随着要求的增加，帮助器类将慢慢变大，并且其中一些将很快过时。当然，没有人敢于修改它们，因为这可能会毁坏较旧的项目。同时，开发人员将重用不再与新规则同步的有效示例，并且他们将没有办法了解哪些部分仍在使用，以及哪些部分可以毫无风险地丢弃。您将陷入一片混乱之中（复制/粘贴编程）。</p>
<p>有很多克服这一复杂性的办法，而我将介绍您可能已经忽视的一种有用的办法 — 代码生成器。其实您已经使用了它们，即使您没有意识到这一点：每当您创建一个新的 ASPX 页时，隐藏机制就会将它转换为 C# 或 Microsoft Visual Basic 类。当您在项目中引用 COM 组件、Web 服务甚至数据结构（类型化数据集）时，会发生相同的事情 — 有一个类被自动生成，它隐藏了您不希望了解的复杂性。 </p>
<div><a href="http://msdn.microsoft.com/zh-cn/library/ms953317.aspx#mainSection"><img alt="" src="http://i.msdn.microsoft.com/ms953317.arrow_px_up(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/ms953317.aspx#mainSection"><font color=#0033cc>返回页首</font></a> <br></div>
<h3 id=EDAA>工具和示例</h3>
<p>您将遇到的第一批代码生成器只是一些隐藏复杂性的工具。它们不允许您解释如何生成代码，并且不让您随后更改生成的代码（它们将清除您进行的更新）。</p>
<p>例如，&#8220;Add Web Reference Wizard&#8221;甚至不向您显示插入到项目中的代码（除非您请求它显示）。</p>
<p><img alt="" src="http://i.msdn.microsoft.com/ms953317.realworld11022004_fig01(zh-cn,MSDN.10).gif"> </p>
<div><strong>图 1. 显示为 Web 引用生成的代码</strong> </div>
<p>Windows 窗体设计器也是一个代码生成器。在这里，您可以查看代码，但最好不要动它。</p>
<pre>     #region Windows Form Designer generated code
/// &lt;summary&gt;
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// &lt;/summary&gt;
private void InitializeComponent()
{
this.button1 = new System.Windows.Forms.Button();
this.SuspendLayout();
</pre>
<p>让我们更进一步地探讨该问题：代码生成器可以提供格式规范的主干，以供您随后添加自己的代码。您可能知道 <a id=ctl00_rs1_mainContentContainer_ctl01 onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00|ctl00_rs1_mainContentContainer_ctl01',this);" href="http://www.aisto.com/roeder/dotnet/"><font color=#0033cc>Reflector</font></a>，但您是否知道 Reflector 的一些外接程序 (<a id=ctl00_rs1_mainContentContainer_ctl02 onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00|ctl00_rs1_mainContentContainer_ctl02',this);" href="http://www.dotnetwiki.org/Default.aspx?tabid=52"><font color=#0033cc>http://www.dotnetwiki.org/Default.aspx?tabid=52</font></a>) 包含代码生成器？它们使您可以执行以下工作：</p>
<p><img alt="" src="http://i.msdn.microsoft.com/ms953317.realworld11022004_fig02(zh-cn,MSDN.10).gif"> </p>
<div><strong>图 2. 使用 Reflector 生成代码</strong> </div>
<p>当您按下&#8220;Generate&#8221;按钮时，将创建一个新文件：</p>
<pre>// Generated by Refly
namespace MyTestNameSpace
{
using System;
/// &lt;summary&gt;Test fixture for the &lt;see cref="SomeBusinessClass"/&gt; class
///&lt;/summary&gt;
/// &lt;remarks /&gt;
[TestFixture()]
public class SomeBusinessClassTest
{
/// &lt;summary /&gt;
/// &lt;remarks /&gt;
private SomeBusinessClass _someBusinessClass = null;
/// &lt;summary /&gt;
/// &lt;remarks /&gt;
public virtual SomeBusinessClass SomeBusinessClass
{
get
{
return this._someBusinessClass;
}
}
/// &lt;summary&gt;Tests the EstimateSomeNumber method&lt;/summary&gt;
/// &lt;remarks&gt;
///   &lt;para&gt;Test coverage (estimated): 100,0%&lt;/para&gt;
///   &lt;para&gt;Target path:&lt;/para&gt;
///   &lt;code&gt;/* 0 */ return 0x2a;
/// &lt;/code&gt;
/// &lt;/remarks&gt;
[Test()]
[Ignore()]
public virtual void EstimateSomeNumber0()
{
throw new System.NotImplementedException();
}
/// &lt;summary&gt;Tests the IsSomethingAlreadyInDataBase method&lt;/summary&gt;
/// &lt;remarks&gt;
///   &lt;para&gt;Test coverage (estimated): 100,0%&lt;/para&gt;
///   &lt;para&gt;Target path:&lt;/para&gt;
///   &lt;code&gt;/* 0 */ return false;
/// &lt;/code&gt;
/// &lt;/remarks&gt;
[Test()]
[Ignore()]
public virtual void IsSomethingAlreadyInDataBase0()
{
throw new System.NotImplementedException();
}
/// &lt;summary&gt;Sets up the fixture&lt;/summary&gt;
/// &lt;remarks /&gt;
[SetUp()]
public virtual void SetUp()
{
throw new System.NotImplementedException();
}
/// &lt;summary&gt;Releases resource allocated in the fixture&lt;/summary&gt;
/// &lt;remarks /&gt;
[TearDown()]
public virtual void TearDown()
{
throw new System.NotImplementedException();
}
}
}
</pre>
<p>您不了解有关&#8220;单元测试&#8221;的任何内容，但是您仍然能够生成有效的测试类。与上一个示例的不同之处在于，这一次您必须在生成的类的内部编码。可是，我们仍然无法创建我们自己的模板。</p>
<p>下一个步骤是创建我们自己的代码生成器。我们需要的全部东西就是一种根据一组常用信息（例如，将要获得的列的名称、要用来请求信息的方式、要允许的事务种类等等）来生成文本文件（C#、PL-SQL 等等）的方式。当然，您无法生成所有东西，因此需要一个能够生成自定义主干的工具，而某个开发人员随后将添加特定的实现细节。 </p>
<p>该工具可以是 Perl、Microsoft VBScript 或普通的旧式 ASP（就本文而言）— 毕竟，它是一种可以根据参数生成文本文件的很好的工具。您可能使用 ASP 生成 HTML 文件，但是它还适用于生成 Microsoft Excel 或 CSV、WML — 因此，为什么不用它来生成 T-SQL 或 C# 呢？</p>
<p>您的 ASP 文件可能如下所示：</p>
<pre>&lt;%@ Language=VBScript %&gt;
&lt;%if request("Generate").count = 0 then%&gt;
&lt;HTML&gt;&lt;BODY&gt;
&lt;form method=post&gt;
&lt;P&gt;Property name :&lt;INPUT name=PropertyName&gt;&lt;/P&gt;
&lt;P&gt;What is the type of your property ? &lt;INPUT name=PropertyType&gt;&lt;/P&gt;
&lt;P&gt;Read only &lt;input type=checkbox name=ReadOnly value=true&gt;&lt;/P&gt;
&lt;P&gt;&lt;INPUT type=submit value="Generate !" name=Generate&gt;&lt;/P&gt;
&lt;/form&gt;
&lt;/BODY&gt;&lt;/HTML&gt;
&lt;% else
dim PropertyName, PropertyType, ReadOnly, PropertyModifier
PropertyName = Request("PropertyName")
PropertyType = Request("PropertyType")
ReadOnly = (Request("ReadOnly")="true")
if ReadOnly then PropertyModifier = "ReadOnly" else PropertyModifier = ""
Response.ContentType = "text/plain"
%&gt;
Private _&lt;%=PropertyName%&gt; as &lt;%=PropertyType%&gt;
Public &lt;%=propertyModifier%&gt; Property &lt;%=PropertyName%&gt; as &lt;%=PropertyType%&gt;
Get
Return _&lt;%=PropertyName%&gt;
End Get
&lt;%if not ReadOnly then%&gt;
Set (ByVal Value as &lt;%=PropertyType%&gt;)
_&lt;%=PropertyName%&gt; = Value
End Set
&lt;%end if%&gt;
End Property
&lt;%end if%&gt;
</pre>
<p>当您执行该文件时，您将获得如下所示的内容：</p>
<p><img alt="" src="http://i.msdn.microsoft.com/ms953317.realworld11022004_fig03(zh-cn,MSDN.10).gif"> </p>
<div><strong>图 3. 生成的 Web 页</strong> </div>
<p><img alt="" src="http://i.msdn.microsoft.com/ms953317.realworld11022004_fig04(zh-cn,MSDN.10).gif"> </p>
<div><strong>图 4. 生成的代码</strong> </div>
<p>您还可以在 Internet 上找到代码生成器。我通常使用 <a id=ctl00_rs1_mainContentContainer_ctl03 onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00|ctl00_rs1_mainContentContainer_ctl03',this);" href="http://www.ericjsmith.net/codesmith/"><font color=#0033cc>CodeSmith</font></a>；它是免费的并且易于理解，但是您可以找到很多其他的代码生成器。<strong>图 5</strong> 是一个屏幕截图，它显示了一种等待泛型的聪明方式；它生成一个强类型的哈希表。</p>
<p><img alt="" src="http://i.msdn.microsoft.com/ms953317.realworld11022004_fig05thumb(zh-cn,MSDN.10).gif"> </p>
<div><strong>图 5. CodeSmith</strong> </div>
<p>这些工具通常接受两个输入 — 一个模板文件（像我们的 ASP 文件）和一个 XML 参数文件（像表单的内容），并产生一个新文件。</p>
<p>开发人员被赋予这些模板，并且他们通常通过同一组参数来使用两个不同的模板。这样，他们将相同的信息给予这两个模板，并且获得共享信息的一个 T-SQL 文件和一个 C# 文件，同时无需承担遗忘或键入错误的风险；这样就为您完成了此工作的乏味部分。</p>
<div><a href="http://msdn.microsoft.com/zh-cn/library/ms953317.aspx#mainSection"><img alt="" src="http://i.msdn.microsoft.com/ms953317.arrow_px_up(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/ms953317.aspx#mainSection"><font color=#0033cc>返回页首</font></a> <br></div>
<h3 id=ECAA>版本控制</h3>
<p>当然，在使用模板和自动生成的代码时，事情会变得复杂。在现实生活中，模板会演化（例如，您可能希望赋予 C# 类一种调用某些存储过程的异步方式）；如果是这样，那么您需要一种相应的办法，以应付开发人员在主干生成之后放入的实现。</p>
<p>为此，您必须确保开发人员添加的代码独立于您的模板所生成的代码。实现此目的最简单的技术是创建两个不同的类：一个用于生成的代码，另一个从第一个继承，但将不生成。</p>
<p><img alt="" src="http://i.msdn.microsoft.com/ms953317.realworld11022004_fig06(zh-cn,MSDN.10).gif"> </p>
<div><strong>图 6. 对生成的代码进行版本控制</strong> </div>
<p>由于该技术所具有的继承机制，因此它在您生成 .NET 代码时非常适用；但是，如果您要生成其他种类的代码（VBScript、SQL），则将无法以这种方式工作。在那种情况下，您主要依靠自己来解决问题。我使用的机制很简单：我在生成的代码中提供了占位符（在 C# 中为区域；在 SQL 中为特定注释所围绕的块），并且当代码被重新生成时，只有这些区域中的代码被保留。传输自定义代码本身同样简单：模板始终接受 PreviousFile 参数。当该参数存在时，将分析以前的文件以获得自定义代码，并将其重新插入到当前的生成文件中。</p>
<p>如果没有办法恢复原样，则千万不要弄乱生成的代码：您将发现自己正在进行复制/粘贴编程。</p>
<div><a href="http://msdn.microsoft.com/zh-cn/library/ms953317.aspx#mainSection"><img alt="" src="http://i.msdn.microsoft.com/ms953317.arrow_px_up(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/ms953317.aspx#mainSection"><font color=#0033cc>返回页首</font></a> <br></div>
<h3 id=EBAA>小结</h3>
<p>下面是在使用代码生成器时需要记住的一些事情： </p>
<ul>
    <li>
    <p>如果不打算对生成的代码进行修改，请预先声明。 </p>
    <li>
    <p>不要忘记，开发人员没有时间了解生成代码的内部结构，因此请为他们提供帮助，并且尽可能详细地对入口点进行说明。 </p>
    <li>
    <p>考虑进行版本控制，并明确说明您将允许人们做什么。或许您希望开发人员只在代码的特定区域中添加自定义代码，或者您希望他们从您的类继承。 </p>
    <li>
    <p>模板的新版本应当始终能够重新生成旧版本所生成的代码。如果新版本需要比旧版本更多的代码以便正确工作，则请将需要代码的位置变得明显一些，并且插入一些能够生成错误的代码（在编译时 — 如果可能的话）。 </p>
    <li>
    <p>如果您决定中断模板的版本控制机制（例如，通过添加在应用新版本时无法恢复原样的代码），请保留该模板和您使用的参数的副本。如果您需要为某个旧项目生成另一个类似的代码文件，并且您已经丢失了在开发该旧项目时使用的模板版本，则会发生很糟糕的事情。我只是将模板和参数与项目一起放在 SourceSafe 中。 </p>
    <li>
    <p>在发布模板之前，对其进行彻底的测试。模板会在开发人员之间飞快地传播，因为他们无需多少知识就可以解决问题。如果更新生成的文件意味着丢失自定义，那么程序错误将很难查找，并且更加难以修复。 </p>
    </li>
</ul>
<p>使用该方法能够走多远？简单说来，代码生成器使开发人员可以获得一个抽象级别。他不必将精力集中于技术过程（因为该过程已嵌入到模板中），并且可以考虑集成该过程。它使开发人员能够更好地理解客户需求，并使技术过程拥有更好的质量。Microsoft Visual Studio 已经广泛使用了这些技术，并且即将问世的版本已经添加了很多改进（引人注目的是一个类建模程序和不完整类机制，可用于将生成的代码与自定义代码分隔开）。</p>
<p><strong>现实世界中的 .NET</strong> </p>
<p>Pierre Couzy 是一个专门研究分布式系统体系结构的培训师和顾问。作为 20 余部书籍的作者，他目前被 <a id=ctl00_rs1_mainContentContainer_ctl04 onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00|ctl00_rs1_mainContentContainer_ctl04',this);" href="http://www.winwise.fr/"><font color=#0033cc>WinWise</font></a> 聘为 ASP.NET 和 BizTalk 专家。他自 2003 年以来一直是地区主管，并且在法国的许多重要会议上发表了演讲。如果您要与他讨论法国爵士乐、桥牌（是的，就是那种简单的游戏）甚至计算机，请通过 <a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#80;&#105;&#101;&#114;&#114;&#101;&#46;&#99;&#111;&#117;&#122;&#121;&#64;&#119;&#105;&#110;&#119;&#105;&#115;&#101;&#46;&#102;&#114;"><font color=#0033cc>Pierre.couzy@winwise.fr</font></a> 与他联系。</p>
<img src ="http://www.cnitblog.com/MartinYao/aggbug/52415.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/MartinYao/" target="_blank">玄铁剑</a> 2008-12-09 21:40 <a href="http://www.cnitblog.com/MartinYao/articles/52415.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>创建自定义加密权限</title><link>http://www.cnitblog.com/MartinYao/articles/52413.html</link><dc:creator>玄铁剑</dc:creator><author>玄铁剑</author><pubDate>Tue, 09 Dec 2008 13:35:00 GMT</pubDate><guid>http://www.cnitblog.com/MartinYao/articles/52413.html</guid><wfw:comment>http://www.cnitblog.com/MartinYao/comments/52413.html</wfw:comment><comments>http://www.cnitblog.com/MartinYao/articles/52413.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/MartinYao/comments/commentRss/52413.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/MartinYao/services/trackbacks/52413.html</trackback:ping><description><![CDATA[<p>Microsoft Corporation</p>
<p>目标</p>
<p>使用本单元以： </p>
<ul>
    <li>
    <p>开发、实现并使用自定义 CAS 权限。 </p>
    <li>
    <p>使用自定义代码访问权限来控制到由 DPAPI 提供的非托管加密功能的访问。 </p>
    </li>
</ul>
<p>适用于</p>
<p>本单元适用于下列产品和技术： </p>
<ul>
    <li>
    <p>Microsoft&#174; Windows&#174; 2000 Server 和 Windows 2000 Professional、Windows Server&#8482; 2003、Windows XP Professional 操作系统 </p>
    <li>
    <p>Internet Information Server (IIS) </p>
    <li>
    <p>Microsoft .NET Framework 1.1 </p>
    </li>
</ul>
<p>如何使用本单元</p>
<p>要从本单元受益最多： </p>
<ul>
    <li>
    <p>您必须具备使用 Visual C# .NET 和 Visual Studio .NET 进行编程的经验。 </p>
    <li>
    <p>请阅读&#8220;<a id=ctl00_rs1_mainContentContainer_ctl02 onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00|ctl00_rs1_mainContentContainer_ctl02',this);" href="http://msdn.microsoft.com/library/en-us/secmod/html/secmod21.asp"><font color=#0033cc>如何创建 DPAPI 库</font></a>&#8221;。该单元演示了如何创建本单元所用的托管 DPAPI 包装程序集。 </p>
    </li>
</ul>
<h5>本页内容</h5>
<p><a href="http://msdn.microsoft.com/zh-cn/library/aa302362.aspx#EIAA"><img alt=摘要 src="http://i.msdn.microsoft.com/Aa302362.arrow_px_down(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/aa302362.aspx#EIAA"><font color=#0033cc>摘要</font></a> <br><a href="http://msdn.microsoft.com/zh-cn/library/aa302362.aspx#EHAA"><img alt=须知 src="http://i.msdn.microsoft.com/Aa302362.arrow_px_down(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/aa302362.aspx#EHAA"><font color=#0033cc>须知</font></a> <br><a href="http://msdn.microsoft.com/zh-cn/library/aa302362.aspx#EGAA"><img alt="创建 EncryptionPermission 类" src="http://i.msdn.microsoft.com/Aa302362.arrow_px_down(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/aa302362.aspx#EGAA"><font color=#0033cc>创建 EncryptionPermission 类</font></a> <br><a href="http://msdn.microsoft.com/zh-cn/library/aa302362.aspx#EFAA"><img alt="创建 EncryptionPermissionAttribute 类" src="http://i.msdn.microsoft.com/Aa302362.arrow_px_down(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/aa302362.aspx#EFAA"><font color=#0033cc>创建 EncryptionPermissionAttribute 类</font></a> <br><a href="http://msdn.microsoft.com/zh-cn/library/aa302362.aspx#EEAA"><img alt="在 GAC 中安装权限程序集" src="http://i.msdn.microsoft.com/Aa302362.arrow_px_down(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/aa302362.aspx#EEAA"><font color=#0033cc>在 GAC 中安装权限程序集</font></a> <br><a href="http://msdn.microsoft.com/zh-cn/library/aa302362.aspx#EDAA"><img alt="更新 DPAPI 托管包装代码" src="http://i.msdn.microsoft.com/Aa302362.arrow_px_down(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/aa302362.aspx#EDAA"><font color=#0033cc>更新 DPAPI 托管包装代码</font></a> <br><a href="http://msdn.microsoft.com/zh-cn/library/aa302362.aspx#ECAA"><img alt="从中等信任的 Web 应用程序调用 DPAPI" src="http://i.msdn.microsoft.com/Aa302362.arrow_px_down(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/aa302362.aspx#ECAA"><font color=#0033cc>从中等信任的 Web 应用程序调用 DPAPI</font></a> <br></p>
<h3 id=EIAA>摘要</h3>
<p>.NET Framework 的代码访问安全性 (CAS) 功能可扩展。这使您能够实现自己的权限以控制到重要自定义功能的访问。</p>
<p>本单元主要说明如何创建自定义 CAS 权限以控制到托管数据保护 API (DPAPI) 包装代码的编程访问。该代码在以下站点的 MSDN 库文章&#8220;如何：创建 DPAPI 库&#8221;中进行阐述：<a id=ctl00_rs1_mainContentContainer_ctl03 onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00|ctl00_rs1_mainContentContainer_ctl03',this);" href="http://msdn.microsoft.com/library/en-us/dnnetsec/html/SecNetHT07.asp"><font color=#0033cc>http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnnetsec/html/SecNetHT07.asp</font></a>。</p>
<div><a href="http://msdn.microsoft.com/zh-cn/library/aa302362.aspx#mainSection"><img alt="" src="http://i.msdn.microsoft.com/Aa302362.arrow_px_up(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/aa302362.aspx#mainSection"><font color=#0033cc>返回页首</font></a> <br></div>
<h3 id=EHAA>须知</h3>
<p>代码访问安全性权限必须源自 <strong>System.Security.CodeAccessPermission</strong>，它可实现由 <strong>IPermission</strong> 接口定义的 <strong>Demand</strong> 方法，同时还可实现其他方法，如 <strong>IStackWalk</strong> 接口定义的 <strong>Assert</strong>、<strong>Deny</strong> 以及 <strong>PermitOnly </strong>。</p>
<p>代码访问权限（不是身份权限）还可实现 <strong>IUnrestrictedPermission</strong> 接口，以指示该权限是不受限制的权限集的一部分。这意味着该权限会被自动授予任何具有完全信任的代码。本单元中使用的自定义 <strong>EncryptionPermission</strong> 的继承层次结构如图 1 所示。</p>
<p><img alt=fh15secmod01 src="http://i.msdn.microsoft.com/Aa302362.fh15secmod01(zh-cn,MSDN.10).gif"> </p>
<div><strong><strong>图 1. 自定义 EncryptionPermission 继承层次结构</strong> </strong></div>
<p>自定义 <strong>EncryptionPermission</strong> 类维护以下状态： </p>
<ul>
    <li>
    <p><strong>EncryptionPermissionFlag</strong>。确定被授予该权限的代码能够加密数据还是解密数据，或二者均可。 </p>
    <li>
    <p><strong>StorePermissionFlag</strong>。确定被授予该权限的代码能够通过机器存储还是当前用户存储使用 DPAPI，或二者均可。 </p>
    </li>
</ul>
<div><a href="http://msdn.microsoft.com/zh-cn/library/aa302362.aspx#mainSection"><img alt="" src="http://i.msdn.microsoft.com/Aa302362.arrow_px_up(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/aa302362.aspx#mainSection"><font color=#0033cc>返回页首</font></a> <br></div>
<h3 id=EGAA>创建 EncryptionPermission 类</h3>
<p><strong>EncryptionPermission</strong> 类是自定义权限实现，用于授权到非托管 DPAPI 功能的访问。</p>
<p><strong>创建 CustomPermission 类</strong> </p>
<ol>
    <li>
    <p>创建新的 Visual C#<sup>TM</sup> 开发工具&#8220;类库&#8221;项目 <strong>CustomPermission</strong>，并将 <strong>class1.cs</strong> 重命名为 <strong>EncryptionPermission.cs</strong>。 </p>
    <li>
    <p>向该程序集添加强名称，以便可将其安装在 GAC 中。使用 <strong>assemblyinfo.cs</strong> 中的以下属性： </p>
    <pre>[assembly: AssemblyKeyFile(@"..\..\CustomPermissions.snk")]
    </pre>
    <li>
    <p>使用固定的程序集版本。 </p>
    <pre>[assembly: AssemblyVersion("1.0.0.1")]
    </pre>
    <li>
    <p>将以下 <strong>using</strong> 语句添加到 EncryptionPermission.cs 顶端。 </p>
    <pre>using System.Security;
    using System.Security.Permissions;
    </pre>
    <li>
    <p>将以下枚举类型添加到 <strong>CustomPermissions</strong> 命名空间。 </p>
    <pre>[Flags, Serializable]
    public enum EncryptionPermissionFlag
    {Encrypt = 0x01, Decrypt = 0x02}
    [Flags, Serializable]
    public enum StorePermissionFlag
    {User = 0x01, Machine = 0x02}
    </pre>
    <li>
    <p>利用 [<strong>Serializable</strong>] 属性向 <strong>EncryptionPermission</strong> 类添加序列化支持，然后将其从 <strong>CodeAccessSecurity</strong> 和 <strong>IUnrestrictedPermission</strong> 导出。.并且，密封该类，如下所示。 </p>
    <pre>[Serializable]
    public sealed class EncryptionPermission : CodeAccessPermission,
    IUnrestrictedPermission
    </pre>
    <li>
    <p>添加两个私有成员变量以维持权限状态。 </p>
    <pre>private EncryptionPermissionFlag _permFlag;
    private StorePermissionFlag _storePermFlag;
    </pre>
    <li>
    <p>用以下构造函数替换默认的构造函数。 </p>
    <pre>// It is convention for permission types to provide a constructor
    // that accepts the PermissionState enumeration.
    public EncryptionPermission(PermissionState state)
    {
    if (state.Equals(PermissionState.Unrestricted))
    {
    _permFlag = EncryptionPermissionFlag.Encrypt |
    EncryptionPermissionFlag.Decrypt;
    _storePermFlag = StorePermissionFlag.User | StorePermissionFlag.Machine;
    }
    else
    {
    _permFlag &amp;= ~(EncryptionPermissionFlag.Encrypt |
    EncryptionPermissionFlag.Decrypt);
    _storePermFlag &amp;= ~(StorePermissionFlag.User |
    StorePermissionFlag.Machine);
    }
    }
    // This constructor allows you to specify the encryption type (encrypt
    // or decrypt) by using the EncryptionPermissionFlag enumeration and the DPAPI
    // key store to use (user or machine) as defined by the StorePermissionFlag
    // enumeration.
    public EncryptionPermission(EncryptionPermissionFlag cipher,
    StorePermissionFlag store)
    {
    _permFlag = cipher;
    _storePermFlag = store;
    }
    public EncryptionPermission()
    {
    _permFlag &amp;= ~EncryptionPermissionFlag.Encrypt |
    EncryptionPermissionFlag.Decrypt;
    _storePermFlag &amp;= ~(StorePermissionFlag.User | StorePermissionFlag.Machine);
    }
    </pre>
    <li>
    <p>添加以下公共属性以允许使用者应用程序设置权限类状态。 </p>
    <pre>// Set this property to true to allow encryption.
    public bool Encrypt
    {
    set {
    if(true == value)
    {
    _permFlag |= EncryptionPermissionFlag.Encrypt;
    }
    else
    {
    _permFlag &amp;= ~EncryptionPermissionFlag.Encrypt;
    }
    }
    get {
    return (_permFlag &amp; EncryptionPermissionFlag.Encrypt).Equals(
    EncryptionPermissionFlag.Encrypt);
    }
    }
    // Set this property to true to allow decryption.
    public bool Decrypt
    {
    set {
    if(true == value)
    {
    _permFlag |= EncryptionPermissionFlag.Decrypt;
    }
    else
    {
    _permFlag &amp;= ~EncryptionPermissionFlag.Decrypt;
    }
    }
    get {
    return (_permFlag &amp; EncryptionPermissionFlag.Decrypt).Equals(
    EncryptionPermissionFlag.Decrypt);
    }
    }
    // Set this property to true to use the DPAPI machine key.
    public bool MachineStore
    {
    set {
    if(true == value)
    {
    _storePermFlag |= StorePermissionFlag.Machine;
    }
    else
    {
    _storePermFlag &amp;= ~StorePermissionFlag.Machine;
    }
    }
    get {
    return (_storePermFlag &amp; StorePermissionFlag.Machine).Equals(
    StorePermissionFlag.Machine);
    }
    }
    // Set this property to true to use the DPAPI user key.
    public bool UserStore
    {
    set {
    if(true == value)
    {
    _storePermFlag |= StorePermissionFlag.User;
    }
    else
    {
    _storePermFlag &amp;= ~StorePermissionFlag.User;
    }
    }
    get {
    return (_storePermFlag &amp; StorePermissionFlag.User).Equals(
    StorePermissionFlag.User);
    }
    }
    </pre>
    <li>
    <p>实现 <strong>IPermission.Copy</strong>。这会创建与当前权限实例完全相同的副本，并将其返回到调用方。 </p>
    <pre>public override IPermission Copy()
    {
    return  new EncryptionPermission(_permFlag, _storePermFlag);
    }
    </pre>
    <li>
    <p>实现 <strong>IPermission.Intersect</strong>。这会返回一个权限对象，该对象即当前权限和提供权限之间的交集。 </p>
    <pre>public override IPermission Intersect(IPermission target)
    {
    // An input of null indicates a permission with no state.
    // There can be no common state, so the method returns null.
    if (target == null)
    return null;
    if (!(target.GetType().Equals(this.GetType())))
    throw new ArgumentException(
    "Argument must be of type EncryptionPermission.");
    // Cast target to an EncryptionPermission.
    EncryptionPermission targetPerm = (EncryptionPermission)target;
    EncryptionPermissionFlag intersectEncryption = this._permFlag &amp;
    targetPerm._permFlag;
    StorePermissionFlag intersectStore = this._storePermFlag &amp;
    targetPerm._storePermFlag;
    return new EncryptionPermission(intersectEncryption, intersectStore);
    }
    </pre>
    <li>
    <p>实现 <strong>IPermission.Union</strong>。这会返回一个权限对象，该对象即当前权限和提供权限的并集。 </p>
    <pre>public override IPermission Union(IPermission target)
    {
    if (target == null)
    return Copy();
    if (!(target.GetType().Equals(this.GetType())))
    throw new ArgumentException(
    "Argument must be of type EncryptionPermission.");
    // Cast the target to an EncryptionPermission.
    EncryptionPermission targetPerm = (EncryptionPermission)target;
    EncryptionPermissionFlag unionEncryption = this._permFlag |
    targetPerm._permFlag;
    StorePermissionFlag unionStore = this._storePermFlag |
    targetPerm._storePermFlag;
    return new EncryptionPermission(unionEncryption, unionStore);
    }
    </pre>
    <li>
    <p>实现 <strong>IPermission.IsSubsetOf</strong>。该方法会返回一个布尔值，以指示当前权限是否是提供权限的子集。要成为子集，则当前权限中状态的每一项也必须位于目标权限内。 </p>
    <pre>public override bool IsSubsetOf(IPermission target)
    {
    // An input of null indicates a permission with no state.
    // The permission can only be a subset if it's in a similar empty state.
    bool canEncrypt, canDecrypt;
    bool canUseMachineStore, canUseUserStore;
    bool canTargetEncrypt, canTargetDecrypt;
    bool canTargetUseMachineStore, canTargetUseUserStore;
    canEncrypt = (this._permFlag &amp;
    EncryptionPermissionFlag.Encrypt).
    Equals(EncryptionPermissionFlag.Encrypt);
    canDecrypt = (this._permFlag &amp;
    EncryptionPermissionFlag.Decrypt).
    Equals(EncryptionPermissionFlag.Decrypt);
    canUseMachineStore = (this._storePermFlag &amp;
    StorePermissionFlag.Machine).
    Equals(StorePermissionFlag.Machine);
    canUseUserStore = (this._storePermFlag &amp;
    StorePermissionFlag.User).
    Equals(StorePermissionFlag.User);
    if (target == null)
    {
    if ((canEncrypt == false &amp;&amp; canDecrypt == false) &amp;&amp; (canUseMachineStore ==
    false  &amp;&amp; canUseUserStore == false))
    return true;
    else
    return false;
    }
    if (!(target.GetType().Equals(this.GetType())))
    throw new ArgumentException(
    "Argument must be of type EncryptionPermission.");
    // Cast the target to an EncryptionPermission.
    EncryptionPermission targetPerm = (EncryptionPermission)target;
    canTargetEncrypt = (targetPerm._permFlag &amp;
    EncryptionPermissionFlag.Encrypt).
    Equals(EncryptionPermissionFlag.Encrypt);
    canTargetDecrypt = (targetPerm._permFlag &amp;
    EncryptionPermissionFlag.Decrypt).
    Equals(EncryptionPermissionFlag.Decrypt);
    canTargetUseMachineStore = (targetPerm._storePermFlag &amp;
    StorePermissionFlag.Machine).
    Equals(StorePermissionFlag.Machine);
    canTargetUseUserStore = (targetPerm._storePermFlag &amp;
    StorePermissionFlag.User).
    Equals(StorePermissionFlag.User);
    // Every value set (true) in this permission must be in the target.
    // The following code checks to see if the current permission is a subset
    // of the target. If the current permission has something that the target
    // does not have, it cannot be a subset.
    if(canEncrypt == true &amp;&amp; canTargetEncrypt == false)
    return false;
    if(canDecrypt == true &amp;&amp; canTargetDecrypt == false)
    return false;
    if(canUseMachineStore == true &amp;&amp; canTargetUseMachineStore == false)
    return false;
    if(canUseUserStore == true &amp;&amp; canTargetUseUserStore == false)
    return false;
    return true;
    }
    </pre>
    <li>
    <p>实现 <strong>ISecurityEncodable.ToXml</strong> 和 <strong>FromXml</strong>。这些方法将权限对象的实例转换为 XML 格式，反之亦然。这些方法用于支持序列化。例如，在将安全属性存储在程序集元数据中时使用这些方法。 </p>
    <pre>public override SecurityElement ToXml()
    {
    // Create a new element. The tag name must always be IPermission.
    SecurityElement elem = new SecurityElement("IPermission");
    // Determine the fully qualified type name (including the assembly name) of
    // the EncryptionPermission class. (The security system uses this name to
    // locate and load the class.)
    string name = typeof(EncryptionPermission).AssemblyQualifiedName;
    // Add attributes for the class name and protocol version.
    // The version must currently be 1.
    elem.AddAttribute("class", name);
    elem.AddAttribute("version", "1" );
    if (IsUnrestricted())
    {
    // Using the Unrestricted attribute is consistent with the
    // built-in .NET Framework permission types and helps keep
    // the encoding compact.
    elem.AddAttribute("Unrestricted", Boolean.TrueString);
    }
    else
    {
    // Encode each state field as an attribute of the Permission element.
    // To compact, encode only nondefault state parameters.
    elem.AddAttribute("Flags", this._permFlag.ToString());
    elem.AddAttribute("Stores", this._storePermFlag.ToString());
    }
    // Return the completed element.
    return elem;
    }
    // Converts a SecurityElement (or tree of elements) to a permission
    // instance.
    public override void FromXml(SecurityElement elem)
    {
    string attrVal = "";
    // Check for an unrestricted instance.
    attrVal = elem.Attribute("Unrestricted");
    if (attrVal != null)
    {
    if(attrVal.ToLower().Equals("true"))
    {
    this._permFlag = EncryptionPermissionFlag.Encrypt |
    EncryptionPermissionFlag.Decrypt;
    this._storePermFlag = StorePermissionFlag.Machine |
    StorePermissionFlag.User;
    }
    return;
    }
    //Turn off the permission and store flags.
    this._permFlag &amp;= ~(EncryptionPermissionFlag.Encrypt |
    EncryptionPermissionFlag.Decrypt);
    this._storePermFlag &amp;= ~(StorePermissionFlag.Machine |
    StorePermissionFlag.User);
    attrVal = elem.Attribute("Flags");
    if (attrVal != null)
    {
    if(!attrVal.Trim().Equals(""))
    {
    this._permFlag =
    (EncryptionPermissionFlag)Enum.Parse(typeof(EncryptionPermissionFlag),
    attrVal);
    }
    }
    attrVal = elem.Attribute("Stores");
    if (attrVal != null)
    {
    if(!attrVal.Trim().Equals(""))
    {
    this._storePermFlag =
    (StorePermissionFlag)Enum.Parse(typeof(StorePermissionFlag),
    attrVal);
    }
    }
    }
    </pre>
    <li>
    <p>实现 <strong>IUnrestrictedPermission.IsUnrestricted</strong>。如果权限实例处于不受限制的状态，则该方法返回 <strong>true</strong>。本例中，不受限制的 <strong>EncryptionPermission</strong> 实例允许代码使用 DPAPI 机器和用户存储来加密和解密数据。 </p>
    <pre>public bool IsUnrestricted()
    {
    bool canEncrypt, canDecrypt, canUseUserStore, canUseMachineStore;
    canEncrypt = (this._permFlag &amp;
    EncryptionPermissionFlag.Encrypt).
    Equals(EncryptionPermissionFlag.Encrypt);
    canDecrypt = (this._permFlag &amp;
    EncryptionPermissionFlag.Decrypt).
    Equals(EncryptionPermissionFlag.Decrypt);
    canUseUserStore = (this._storePermFlag &amp;
    StorePermissionFlag.User).
    Equals(StorePermissionFlag.User);
    canUseMachineStore = (this._storePermFlag &amp;
    StorePermissionFlag.Machine).
    Equals(StorePermissionFlag.Machine);
    return ((canEncrypt &amp;&amp; canDecrypt) &amp;&amp;
    (canUseUserStore &amp;&amp; canUseMachineStore));
    }
    </pre>
    </li>
</ol>
<div><a href="http://msdn.microsoft.com/zh-cn/library/aa302362.aspx#mainSection"><img alt="" src="http://i.msdn.microsoft.com/Aa302362.arrow_px_up(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/aa302362.aspx#mainSection"><font color=#0033cc>返回页首</font></a> <br></div>
<h3 id=EFAA>创建 EncryptionPermissionAttribute 类</h3>
<p>.NET Framework 使用与其合作伙伴权限类相关联的属性类来编码权限实例。您需要权限属性来支持声明性安全语法。</p>
<p><strong>创建 EncryptionPermissionAttribute 类</strong> </p>
<ol>
    <li>
    <p>将一个新类文件添加到当前项目，<strong>EncryptionPermissionAttribute.cs</strong>。 </p>
    <li>
    <p>将以下 <strong>using</strong> 语句添加到新文件的顶端。 </p>
    <pre>using System.Security;
    using System.Diagnostics;
    using System.Security.Permissions;
    </pre>
    <li>
    <p>从 <strong>CodeAccessSecurityAttribute</strong> 导出属性类，并将其密封。 </p>
    <pre>public sealed class EncryptionPermissionAttribute :
    CodeAccessSecurityAttribute
    </pre>
    <li>
    <p>向该类添加序列化支持，并使用 <strong>AttributeUsage</strong> 属性来指示可以使用自定义权限属性的位置。 </p>
    <pre>[Serializable,
    AttributeUsage(AttributeTargets.Method |      // Can use on methods
    AttributeTargets.Constructor | // Can use on constructors
    AttributeTargets.Class |       // Can use on classes
    AttributeTargets.Struct |      // Can use on structures
    AttributeTargets.Assembly,     // Can use at the assembly level
    AllowMultiple = true,          // Can use multiple attribute
    // instances per program element
    // (class, method and so on)
    Inherited = false)]            // Can not be inherited
    </pre>
    <li>
    <p>向该类添加私有成员变量以镜像由关联的权限类维持的状态。 </p>
    <pre>// The following state fields mirror those used in the associated
    // permission type.
    private bool _encrypt = false;
    private bool _decrypt = false;
    private bool _machineStore = false;
    private bool _userStore = false;
    </pre>
    <li>
    <p>用以下构造函数替换默认的构造函数。 </p>
    <pre>// Pass the action code back to the base class.
    public EncryptionPermissionAttribute(SecurityAction action) : base(action)
    {
    }
    </pre>
    <li>
    <p>添加以下公共属性以镜像这些由关联的权限类提供的各项。 </p>
    <pre>public bool Encrypt
    {
    get {
    return _encrypt;
    }
    set {
    _encrypt = value;
    }
    }
    public bool Decrypt
    {
    get {
    return _decrypt;
    }
    set {
    _decrypt = value;
    }
    }
    public bool UserStore
    {
    get {
    return _userStore;
    }
    set {
    _userStore = value;
    }
    }
    public bool MachineStore
    {
    get {
    return _machineStore;
    }
    set {
    _machineStore = value;
    }
    }
    </pre>
    <li>
    <p>实现 <strong>SecurityPermissionAttribute.CreatePermission</strong>。该方法会创建一个权限对象，该对象随后被序列化并通过程序集的元数据中指定的 <strong>SecurityAction</strong> 枚举进行保留。 </p>
    <pre>public override IPermission CreatePermission()
    {
    // The runtime automatically provides a property to indicate
    // whether or not an unrestricted instance is required.
    if((Unrestricted) || ((_encrypt &amp;&amp; _decrypt) &amp;&amp;
    (_userStore &amp;&amp; _machineStore)))
    {
    return new EncryptionPermission(PermissionState.Unrestricted);
    }
    // Copy the state from the attribute to the permission object
    EncryptionPermissionFlag cipher = 0x0;
    StorePermissionFlag store = 0x0;
    if(_encrypt)
    cipher |= EncryptionPermissionFlag.Encrypt;
    if(_decrypt)
    cipher |= EncryptionPermissionFlag.Decrypt;
    if(_userStore)
    store |= StorePermissionFlag.User;
    if(_machineStore)
    store |= StorePermissionFlag.Machine;
    // Return the final permission.
    return new EncryptionPermission(cipher, store);
    }
    </pre>
    <li>
    <p>生成解决方案。 </p>
    </li>
</ol>
<div><a href="http://msdn.microsoft.com/zh-cn/library/aa302362.aspx#mainSection"><img alt="" src="http://i.msdn.microsoft.com/Aa302362.arrow_px_up(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/aa302362.aspx#mainSection"><font color=#0033cc>返回页首</font></a> <br></div>
<h3 id=EEAA>在 GAC 中安装权限程序集</h3>
<p>您必须对实现自定义安全权限的所有程序集授予完全信任。实际上，这意味着您需要在使用程序集的计算机上安装该程序集，以确保默认的安全策略授予其完全信任。<strong>My_Computer_Zone</strong> 内的代码由默认的策略授予完全信任。</p>
<p>在 GAC 中安装程序集是确保代码访问安全策略授予其完全信任的一种方法。GAC 是适合于权限程序集的位置，原因在于该程序集由本地计算机上的代码访问安全策略使用，并且它对于本地计算机上安装的所有 .NET Framework 应用程序均可用。</p>
<p>要在本地计算机的 GAC 中安装自定义权限程序集，请运行以下命令。</p>
<pre>gacutil.exe /i custompermission.dll
</pre>
<div><a href="http://msdn.microsoft.com/zh-cn/library/aa302362.aspx#mainSection"><img alt="" src="http://i.msdn.microsoft.com/Aa302362.arrow_px_up(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/aa302362.aspx#mainSection"><font color=#0033cc>返回页首</font></a> <br></div>
<h3 id=EDAA>更新 DPAPI 托管包装代码</h3>
<p>.NET Framework 类库目前并未公开 DPAPI 功能。要从 .NET Framework 应用程序调用 DPAPI，必须使用 <strong>P/Invoke</strong>。有关演示如何创建托管 DPAPI 包装程序集的代码的信息，请参阅&#8220;<a id=ctl00_rs1_mainContentContainer_ctl04 onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00|ctl00_rs1_mainContentContainer_ctl04',this);" href="http://msdn.microsoft.com/library/en-us/secmod/html/secmod21.asp"><font color=#0033cc>How To Create a DPAPI Library</font></a>&#8221;。</p>
<p>不做进一步的修改，您只可从完全信任代码调用参考&#8220;如何&#8230;&#8230;&#8221;方法文章中的托管 DPAPI 包装。若要能够从部分信任代码（例如，中等信任的 ASP.NET Web 应用程序）调用 DPAPI 包装，您必须将对非托管 DPAPI 函数的调用置于沙箱内方可。为此，需进行以下修改： </p>
<ul>
    <li>
    <p>宣告 DPAPI 包装代码中的非托管代码权限。这意味着任何调用代码均不要求非托管代码权限。 </p>
    <li>
    <p>通过要求自定义 <strong>EncryptionPermission</strong> 在包装内授权调用代码。根据 Demand/Assert 使用模式，<strong>Demand</strong> 调用在 <strong>Assert</strong> 调用前出现。有关安全使用 <strong>Assert</strong> 的详细信息，请参阅单元&#8220;<a id=ctl00_rs1_mainContentContainer_ctl05 onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00|ctl00_rs1_mainContentContainer_ctl05',this);" href="http://msdn.microsoft.com/library/en-us/secmod/html/secmod81.asp"><font color=#0033cc>Code Access Security in Practice</font></a>&#8221;中的&#8220;Assert and RevertAssert&#8221;。 </p>
    </li>
</ul>
<p><strong>修改 DPAPI 托管包装</strong> </p>
<ol>
    <li>
    <p>通过&#8220;<a id=ctl00_rs1_mainContentContainer_ctl06 onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00|ctl00_rs1_mainContentContainer_ctl06',this);" href="http://msdn.microsoft.com/library/en-us/secmod/html/secmod21.asp"><font color=#0033cc>如何创建 DPAPI 库</font></a>&#8221;中的以下指令构建 DPAPI 托管包装。 </p>
    <li>
    <p>向 <strong>CustomPermission</strong> 程序集添加引用。 </p>
    <li>
    <p>从托管包装库中打开 <strong>dataprotection.cs</strong>，并在该文件顶端现有的 <strong>using</strong> 语句下添加以下 <strong>using</strong> 语句。 </p>
    <pre>using System.Security;
    using System.Security.Permissions;
    using CustomPermissions;
    </pre>
    <li>
    <p>在 <strong>dataprotection.cs</strong> 中找到 <strong>Encrypt</strong> 方法，并在该 <strong>Encrypt</strong> 方法中的外部 <strong>try</strong> 块顶端添加以下代码。 </p>
    <pre>// Set the storeFlag depending on how the caller uses
    // the managed DPAPI wrapper.
    StorePermissionFlag storeFlag;
    if(Store.USE_MACHINE_STORE == store)
    {
    storeFlag = StorePermissionFlag.Machine;
    }
    else
    {
    storeFlag = StorePermissionFlag.User;
    }
    // Demand custom EncryptionPermission.
    (new EncryptionPermission(EncryptionPermissionFlag.Encrypt, storeFlag)).
    Demand();
    // Assert the unmanaged code permission.
    (new SecurityPermission(SecurityPermissionFlag.UnmanagedCode)).Assert();
    // Now use P/Invoke to call the unmanaged DPAPI functions.
    </pre>
    <li>
    <p>将以下 <strong>finally</strong> 块添加到该 <strong>Encrypt</strong> 方法中的外部 try 块。 </p>
    <pre>finally
    {
    CodeAccessPermission.RevertAssert();
    }
    </pre>
    <li>
    <p>找到 <strong>dataprotection.cs</strong> 中的 <strong>Decrypt</strong> 方法，并将以下代码添加到外部 try 块的顶端。 </p>
    <pre>StorePermissionFlag storeFlag;
    if(Store.USE_MACHINE_STORE == store)
    {
    storeFlag = StorePermissionFlag.Machine;
    }
    else
    {
    storeFlag = StorePermissionFlag.User;
    }
    // Demand custom EncryptionPermission.
    (new EncryptionPermission(EncryptionPermissionFlag.Decrypt, storeFlag)).
    Demand();
    // Assert the unmanaged code permission.
    (new SecurityPermission(SecurityPermissionFlag.UnmanagedCode)).Assert();
    </pre>
    <li>
    <p>将以下 <strong>finally</strong> 块添加到该 <strong>Decrypt</strong> 方法中的外部 try 块。 </p>
    <pre>finally
    {
    CodeAccessPermission.RevertAssert();
    }
    </pre>
    </li>
</ol>
<div><a href="http://msdn.microsoft.com/zh-cn/library/aa302362.aspx#mainSection"><img alt="" src="http://i.msdn.microsoft.com/Aa302362.arrow_px_up(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/aa302362.aspx#mainSection"><font color=#0033cc>返回页首</font></a> <br></div>
<h3 id=ECAA>从中等信任的 Web 应用程序调用 DPAPI</h3>
<p>要从中等信任的 Web 应用程序或任何部分信任的代码使用 DPAPI 托管包装，您必须配置代码访问安全策略以授予代码自定义 <strong>EncryptionPermission</strong>。</p>
<p>本步骤中，将创建测试 Web 应用程序，然后修改中等信任的 Web 应用程序的 ASP.NET 代码访问安全策略以授予其 <strong>EncryptionPermission</strong>。</p>
<p><strong>创建测试 Web 应用程序</strong> </p>
<ol>
    <li>
    <p>将新的 C# ASP.NET Web 应用程序项目添加当前解决方案。 </p>
    <li>
    <p>向 Dataprotection.dll 程序集添加引用。 </p>
    <li>
    <p>将以下字段添加到 Webform1.aspx。 </p>
    <ul>
        <li>
        <p>一个用于要加密数据的输入字段。使用 ID <strong>txtDataToEncrypt</strong>。 </p>
        <li>
        <p>一个用于已加密数据的字段。使用 ID <strong>txtEncryptedData</strong>。 </p>
        <li>
        <p>一个用于已解密数据的字段。使用 ID <strong>txtDecryptedData</strong>。 </p>
        <li>
        <p>一个 <strong>Encrypt</strong> 按钮。使用 ID <strong>btnEncrypt</strong>。 </p>
        <li>
        <p>一个 <strong>Decrypt </strong>按钮。使用<strong> </strong>ID<strong>btnDecrypt</strong>。<strong> </strong></p>
        <li>
        <p>一个用于错误消息的标签。使用 ID<strong> lblError</strong>。 </p>
        </li>
    </ul>
    <li>
    <p>将以下 <strong>using</strong> 语句添加到现有 <strong>using</strong> 语句之下的 WebForm1.aspx.cs 顶端。 </p>
    <pre>using DataProtection;
    </pre>
    <li>
    <p>添加以下代码以用于 <strong>Encrypt</strong> 按钮单击事件处理程序。 </p>
    <pre>private void btnEncrypt_Click(object sender, System.EventArgs e)
    {
    DataProtector dp = new DataProtector(
    DataProtector.Store.USE_MACHINE_STORE );
    try
    {
    byte[] dataToEncrypt = Encoding.ASCII.GetBytes(txtDataToEncrypt.Text);
    // Not passing optional entropy in this example
    // Could pass random value (stored by the application) for added security
    // when using DPAPI with the machine store.
    txtEncryptedData.Text =
    Convert.ToBase64String(dp.Encrypt(dataToEncrypt,null));
    }
    catch(Exception ex)
    {
    lblError.ForeColor = Color.Red;
    lblError.Text = "Exception.&lt;br&gt;" + ex.Message;
    return;
    }
    lblError.Text = "";
    }
    </pre>
    <li>
    <p>添加以下代码以用于 <strong>Decrypt</strong> 按钮单击事件处理程序。 </p>
    <pre>private void btnDecrypt_Click(object sender, System.EventArgs e)
    {
    DataProtector dp = new DataProtector(DataProtector.Store.USE_MACHINE_STORE);
    try
    {
    byte[] dataToDecrypt = Convert.FromBase64String(txtEncryptedData.Text);
    // Optional entropy parameter is null.
    // If entropy was used within the Encrypt method, the same entropy
    // parameter must be supplied here.
    txtDecryptedData.Text =
    Encoding.ASCII.GetString(dp.Decrypt(dataToDecrypt,null));
    }
    catch(Exception ex)
    {
    lblError.ForeColor = Color.Red;
    lblError.Text = "Exception.&lt;br&gt;" + ex.Message;
    return;
    }
    lblError.Text = "";
    }
    </pre>
    <li>
    <p>通过将以下元素添加到 &lt;<strong>system.web</strong>&gt; 部分内应用程序的 Web.config 文件来配置 Web 应用程序以用于中等信任。 </p>
    <pre>&lt;trust level="Medium" /&gt;
    </pre>
    <li>
    <p>生成解决方案。 </p>
    </li>
</ol>
<p><strong>修改中等信任策略 </strong></p>
<ol>
    <li>
    <p>使用 Visual Studio&#174; .NET 或记事本打开中等信任策略文件。该策略文件位于以下位置。</p>
    <pre>%windir%\Microsoft.NET\Framework\{version}\CONFIG\web_mediumtrust.config</pre>
    <li>
    <p>通过将以下 &lt;<strong>SecurityClass</strong>&gt; 元素添加到 &lt;<strong>SecurityClasses</strong>&gt; 元素来声明 <strong>EncryptionPermission</strong>。 </p>
    <pre>&lt;SecurityClass Name="EncryptionPermission"
    Description="CustomPermission.EncryptionPermission,
    CustomPermission, Version=1.0.0.1,
    Culture=neutral,
    PublicKeyToken=951cd7d57a536a94"/&gt;
    </pre>
    <p>将 <strong>PublicKeyToken</strong> 属性值设为程序集指定的公钥标记。要提取自定义权限程序集的公钥标记，请使用以下命令。 </p>
    <pre>sn -T custompermission.dll</pre>
    <p><strong>注</strong> 使用大写 <strong>-T</strong> 开关项。</p>
    <li>
    <p>找到在中等信任策略文件中的 ASP.NET 命名权限集，并添加以下权限元素。 </p>
    <pre>&lt;IPermission class="EncryptionPermission"
    version="1" Flags="Encrypt,Decrypt"
    Stores="Machine,User"&gt;
    &lt;/IPermission&gt;
    </pre>
    <pre>该权限授予中等信任的 Web 应用程序不受限制的 EncryptionPermission，原因在于它允许代码加密和解密数据，以及使用 DPAPI 机器和用户存储。以上元素演示了支持的语法。它等同于以下内容：
    </pre>
    <pre>&lt;IPermission class="EncryptionPermission"
    version="1" Unrestricted="true" &gt;
    &lt;/IPermission&gt;
    </pre>
    <p>通过仅使用相关的属性，您可以授予代码限制的权限。例如，要限制代码仅使用机器存储中的机器密钥解密数据，请使用以下元素。 </p>
    <pre>&lt;IPermission class="EncryptionPermission"
    version="1" Flags="Decrypt"
    Stores="Machine"&gt;
    &lt;/IPermission&gt;
    </pre>
    <li>
    <p>保存策略文件。 </p>
    <p>现在您可以运行测试 Web 应用程序，并验证通过使用部分信任的 Web 应用程序的 DPAPI 可否加密和解密数据。 </p>
    </li>
</ol>
<p>有关将高权限代码置于沙箱内和使用 ASP.NET 代码访问安全策略的详细信息，请参阅单元&#8220;<a id=ctl00_rs1_mainContentContainer_ctl07 onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00|ctl00_rs1_mainContentContainer_ctl07',this);" href="http://msdn.microsoft.com/library/en-us/secmod/html/secmod82.asp"><font color=#0033cc>Using Code Access Security with ASP.NET</font></a>&#8221;。</p>
<img src ="http://www.cnitblog.com/MartinYao/aggbug/52413.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/MartinYao/" target="_blank">玄铁剑</a> 2008-12-09 21:35 <a href="http://www.cnitblog.com/MartinYao/articles/52413.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>单一登录 Web 应用程序的企业级安全</title><link>http://www.cnitblog.com/MartinYao/articles/52412.html</link><dc:creator>玄铁剑</dc:creator><author>玄铁剑</author><pubDate>Tue, 09 Dec 2008 13:34:00 GMT</pubDate><guid>http://www.cnitblog.com/MartinYao/articles/52412.html</guid><wfw:comment>http://www.cnitblog.com/MartinYao/comments/52412.html</wfw:comment><comments>http://www.cnitblog.com/MartinYao/articles/52412.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/MartinYao/comments/commentRss/52412.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/MartinYao/services/trackbacks/52412.html</trackback:ping><description><![CDATA[<p>Paul D. Sheriff<br>PDSA, Inc.</p>
<p>适用范围：<br>Microsoft&#174; ASP.NET</p>
<p><strong>摘要</strong>：揭示能够以单一登录的方式登录多个 Web 应用程序的技术。本文还提供了示例代码，使您能够在完全使用单一登录的情况下创建强大的企业安全系统方面有一个良好的开端。</p>
<p><a id=ctl00_rs1_mainContentContainer_ctl01 onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00|ctl00_rs1_mainContentContainer_ctl01',this);" href="http://download.microsoft.com/download/B/7/8/B78D1CED-2275-4AEE-B0BE-0DEA1A2A9581/MSDNEnterpriseSecurity.msi"><font color=#0033cc>Download the code samples for this article</font></a>（英文）。</p>
<h5>本页内容</h5>
<p><a href="http://msdn.microsoft.com/zh-cn/library/ms972971.aspx#XSLTsection124121120120"><img alt=引言 src="http://i.msdn.microsoft.com/ms972971.arrow_px_down(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/ms972971.aspx#XSLTsection124121120120"><font color=#0033cc>引言</font></a> <br><a href="http://msdn.microsoft.com/zh-cn/library/ms972971.aspx#XSLTsection127121120120"><img alt=单一登录解决方案概述 src="http://i.msdn.microsoft.com/ms972971.arrow_px_down(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/ms972971.aspx#XSLTsection127121120120"><font color=#0033cc>单一登录解决方案概述</font></a> <br><a href="http://msdn.microsoft.com/zh-cn/library/ms972971.aspx#XSLTsection130121120120"><img alt=类和页面 src="http://i.msdn.microsoft.com/ms972971.arrow_px_down(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/ms972971.aspx#XSLTsection130121120120"><font color=#0033cc>类和页面</font></a> <br><a href="http://msdn.microsoft.com/zh-cn/library/ms972971.aspx#XSLTsection133121120120"><img alt=表 src="http://i.msdn.microsoft.com/ms972971.arrow_px_down(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/ms972971.aspx#XSLTsection133121120120"><font color=#0033cc>表</font></a> <br><a href="http://msdn.microsoft.com/zh-cn/library/ms972971.aspx#XSLTsection134121120120"><img alt="AppLauncher Web 应用程序" src="http://i.msdn.microsoft.com/ms972971.arrow_px_down(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/ms972971.aspx#XSLTsection134121120120"><font color=#0033cc>AppLauncher Web 应用程序</font></a> <br><a href="http://msdn.microsoft.com/zh-cn/library/ms972971.aspx#XSLTsection137121120120"><img alt="Apps 类" src="http://i.msdn.microsoft.com/ms972971.arrow_px_down(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/ms972971.aspx#XSLTsection137121120120"><font color=#0033cc>Apps 类</font></a> <br><a href="http://msdn.microsoft.com/zh-cn/library/ms972971.aspx#XSLTsection140121120120"><img alt="在 Web 应用程序中检索令牌" src="http://i.msdn.microsoft.com/ms972971.arrow_px_down(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/ms972971.aspx#XSLTsection140121120120"><font color=#0033cc>在 Web 应用程序中检索令牌</font></a> <br><a href="http://msdn.microsoft.com/zh-cn/library/ms972971.aspx#XSLTsection143121120120"><img alt=增强您的单一登录系统 src="http://i.msdn.microsoft.com/ms972971.arrow_px_down(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/ms972971.aspx#XSLTsection143121120120"><font color=#0033cc>增强您的单一登录系统</font></a> <br><a href="http://msdn.microsoft.com/zh-cn/library/ms972971.aspx#XSLTsection146121120120"><img alt=安装示例 src="http://i.msdn.microsoft.com/ms972971.arrow_px_down(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/ms972971.aspx#XSLTsection146121120120"><font color=#0033cc>安装示例</font></a> <br><a href="http://msdn.microsoft.com/zh-cn/library/ms972971.aspx#XSLTsection149121120120"><img alt=结论 src="http://i.msdn.microsoft.com/ms972971.arrow_px_down(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/ms972971.aspx#XSLTsection149121120120"><font color=#0033cc>结论</font></a> <br><a href="http://msdn.microsoft.com/zh-cn/library/ms972971.aspx#XSLTsection152121120120"><img alt=参考资料 src="http://i.msdn.microsoft.com/ms972971.arrow_px_down(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/ms972971.aspx#XSLTsection152121120120"><font color=#0033cc>参考资料</font></a> <br></p>
<h3 id=XSLTsection124121120120>引言</h3>
<p>如果您的公司在运转方式上与其他绝大多数公司相同，那么您可能拥有许多用于支持公司业务的 Web 应用程序。绝大多数这种类型的应用程序需要采取安全措施，因为您肯定不希望用户随心所欲地进入任何 Web 应用程序。例如，只允许某些用户进入执行摘要决策支持系统，而其他用户则应只允许进入客户信息系统。有可能还有一些使用这些系统的外部用户，他们并不是您的 Microsoft&#174; Windows&#174; 域用户。可以通过安全机制强制每个用户登录系统，从而对此进行控制。但这样做的问题在于，访问不同系统时，每个系统都会不断地要求用户登录。对于仅仅具有五个 Web 应用程序的公司，如果某用户被允许访问其中的每一个程序，该用户会因为频繁地登录和注销而厌烦不已。所以必须找到一个更好的办法。 </p>
<p>在阅读本文后，您将了解一种在企业中应用单一登录解决方案的方法。公司的内部用户可以使用 Windows 身份验证访问应用程序，而外部用户将被强制进行登录（参见<strong>图</strong> <strong>1</strong>）。</p>
<p><strong>本文所讨论的主题</strong> </p>
<p>单一登录 Web 应用程序 </p>
<ul>
    <li>
    <p>生成单一登录令牌 </p>
    <li>
    <p>将用户映射至应用程序 </p>
    <li>
    <p>基于窗体的身份验证 </p>
    <li>
    <p>Windows 中集成的身份验证 </p>
    </li>
</ul>
<div><a href="http://msdn.microsoft.com/zh-cn/library/ms972971.aspx#mainSection"><img alt="" src="http://i.msdn.microsoft.com/ms972971.arrow_px_up(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/ms972971.aspx#mainSection"><font color=#0033cc>返回页首</font></a> <br></div>
<h3 id=XSLTsection127121120120>单一登录解决方案概述</h3>
<p><strong>图</strong> <strong>1</strong> 所示为内部用户和外部用户在登录到位于一个公司的 Intranet 上的网站时所执行的一系列步骤。在 50,000 英尺的级别上，这个系统仅有四个组件：通过 Windows 身份验证的网站、Active Directory、一个数据库服务器和一个或多个基于窗体进行身份验证的网站。</p>
<p><img alt=singlesignon_01 src="http://i.msdn.microsoft.com/ms972971.singlesignon_01(zh-cn,MSDN.10).gif"> </p>
<div><strong>图</strong> <strong>1</strong> <strong>：涵盖内部用户和外部用户的单一登录解决方案的示例</strong> </div>
<h4>Intranet 解决方案</h4>
<p>在<strong>图</strong> <strong>1</strong> 的左上角可以看到内部用户使用浏览器浏览到特定的网站（第 1 步）。这个网站验证（第 2 步）用户的 Windows 凭据（通过 Active Directory）。如果用户是有效的 Windows 用户，则允许其进入该站点。通过身份验证后，将检索用户的标识，并调用包含指定用户能够运行的应用程序列表的数据库表（第 3 步）。这些应用程序将显示在 <strong>DataGrid</strong> 中，以供用户选择。</p>
<p>用户单击希望运行的应用程序后，将生成一个唯一的、只能使用一次的令牌（第 4 步）。此令牌和用户标识将被存储在另一个数据库表中。然后此令牌被传递给用户要运行的 Web 应用程序中的一个特定页面（通过查询行）。此特殊页面从查询行读取该令牌，然后验证在数据库表中是否存在此令牌（第 5 步）。如果令牌存在，它将在数据库中检索登录 ID，然后删除存储此令牌的记录。此操作能够防止其他人再次使用此令牌并将登录 ID 发送回 Web 应用程序。</p>
<p>在了解了 Web 应用程序中的用户标识后，您需要生成一个 ASP.NET 窗体身份验证票据，因为在 Intranet 中，所有的 Web 应用程序都将使用基于窗体的身份验证。此票据将被正在浏览站点中所有的安全页面的用户所使用。</p>
<h4>Extranet 解决方案</h4>
<p>想要访问 Web 应用程序的外部用户（您所在域之外的用户）将被定向到与内部用户不同的起始页面（参见<strong>图</strong> <strong>2</strong>）。内部用户和外部用户被定向到的 Web 应用程序采取了基于窗体的身份验证的安全措施。当外部用户试图打开此 Web 应用程序中的任何页面时，他们将被自动重定向至登录页面。此登录页面与对内部用户进行身份验证的页面不同。用户必须输入其凭据，然后系统将调用同一个数据库，以确定该用户对此应用程序是否有效。如果有效，则将为此用户会话生成正常的基于窗体的身份验证 cookie。</p>
<div><a href="http://msdn.microsoft.com/zh-cn/library/ms972971.aspx#mainSection"><img alt="" src="http://i.msdn.microsoft.com/ms972971.arrow_px_up(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/ms972971.aspx#mainSection"><font color=#0033cc>返回页首</font></a> <br></div>
<h3 id=XSLTsection130121120120>类和页面</h3>
<p>您需要创建若干类和 Web 页面以支持企业级安全系统。图 2 显示了需要为此系统编写的每个类和 Web 页。本文稍后将讨论此图所显示的每个类。在此图之后，列出了每个类的说明信息以及您的系统中的 Web 页面主要功能。</p>
<p><img alt=singlesignon_02 src="http://i.msdn.microsoft.com/ms972971.singlesignon_02(zh-cn,MSDN.10).gif"> </p>
<div><strong>图</strong> <strong>2</strong> <strong>：设计和开发单一登录解决方案只需要几个类和页面</strong> </div>
<h4>Apps 类</h4>
<p>此类用于检索指定用户的应用程序列表。它还将生成一个新令牌、根据给定的令牌检索用户的标识信息以及删除令牌。</p>
<table>
    <strong>表 1：Apps 类的方法</strong>
    <tbody>
        <tr>
            <th>
            <p>方法名称</p>
            </th>
            <th>
            <p>说明</p>
            </th>
        </tr>
        <tr>
            <td>
            <p>GetAppsByLoginID</p>
            </td>
            <td>
            <p>根据给定用户的域登录 ID，查找与此用户关联的应用程序，并返回应用程序的 DataSet。</p>
            </td>
        </tr>
        <tr>
            <td>
            <p>CreateLoginToken</p>
            </td>
            <td>
            <p>创建并返回新登录令牌。</p>
            </td>
        </tr>
        <tr>
            <td>
            <p>GenerateToken</p>
            </td>
            <td>
            <p>生成新令牌的方法。在本版本中，将使用一个简单的 GUID 作为令牌。</p>
            </td>
        </tr>
        <tr>
            <td>
            <p>VerifyLoginToken</p>
            </td>
            <td>
            <p>根据给定令牌，此方法将验证数据库中是否存在此令牌。它将创建 AppToken 类的一个实例，在写入相应的信息后将其返回。</p>
            </td>
        </tr>
        <tr>
            <td>
            <p>DeleteToken</p>
            </td>
            <td>
            <p>删除表中的令牌。</p>
            </td>
        </tr>
    </tbody>
</table>
<table>
    <tbody>
    </tbody>
</table>
<h4>AppToken 类</h4>
<p>此类包含从 Apps 类的 VerifyLoginToken 方法返回令牌信息时所需的四个属性。表 2 说明了此类包含的四个属性。</p>
<table>
    <strong>表 2：AppToken 类的属性</strong>
    <tbody>
        <tr>
            <th>
            <p>属性</p>
            </th>
            <th>
            <p>说明</p>
            </th>
        </tr>
        <tr>
            <td>
            <p>LoginID</p>
            </td>
            <td>
            <p>字符串，其值表示用户的登录 ID。</p>
            </td>
        </tr>
        <tr>
            <td>
            <p>AppName</p>
            </td>
            <td>
            <p>字符串，其值表示与此 AppToken 记录有关的应用程序名称。</p>
            </td>
        </tr>
        <tr>
            <td>
            <p>LoginKey</p>
            </td>
            <td>
            <p>整数类型，其值表示 esUsers 表中用户的主键。</p>
            </td>
        </tr>
        <tr>
            <td>
            <p>AppKey</p>
            </td>
            <td>
            <p>整数类型，其值表示 esApps 表中应用程序的主键。</p>
            </td>
        </tr>
    </tbody>
</table>
<table>
    <tbody>
    </tbody>
</table>
<h4>AppUserRoles 类</h4>
<p>此类用于检索试图登录应用程序的用户的信息。其中的一个方法根据给定的登录 ID 和应用程序键值检查登录是否有效。另一个方法返回给定用户的角色集。还有一个方法根据登录 ID 和应用程序键值返回 esUser 表的主键。</p>
<h4>AppLauncher 应用程序中的 Default.aspx</h4>
<p>此 Web 页类将检索通过了 IIS 身份验证的 Windows 用户，并返回允许这些用户访问的应用程序的列表。它将在 <strong>DataGrid</strong> 中显示此列表（图 3），并允许用户单击其中特定的应用程序。用户单击应用程序后，此页将生成一个新令牌，并将此令牌和用户 ID 存储到 esAppToken 表，然后调用此应用程序将令牌传递给该 Web 应用程序中的 AppLogin.aspx 页。</p>
<p><img alt=singlesignon_03 src="http://i.msdn.microsoft.com/ms972971.singlesignon_03(zh-cn,MSDN.10).gif"> </p>
<div><strong>图</strong> <strong>3</strong> <strong>：应用程序启动器显示允许登录的用户运行的应用程序的列表</strong> </div>
<h4>每个网站中的 AppLogin.aspx</h4>
<p>只能从应用程序启动器调用此 Web 页类。若有任何其他的应用程序试图调用此页，它会将用户重定向至网站的 Default.aspx 页。因为每个 Web 应用程序都使用基于窗体的身份验证，所以此操作将强制 ASP.NET 将用户重定向至站点中的 Login.aspx 页。</p>
<p>如果使用令牌从应用程序启动器站点调用此页，那么此页将调用 Apps 类中的方法，以验证此令牌是否有效。如果令牌有效，则 Apps 类将返回一个 AppToken 对象，以便此页能够使用此对象中的信息创建基于窗体的身份验证的用户。</p>
<h4>每个网站中的 Login.aspx</h4>
<p>这是常规的 Web 登录页面，它要求用户输入凭据，并在数据库中检查这些凭据，以确保是有效用户；此外，如果是有效用户，则还将创建身份验证票据，并在用户进入站点时，重定向至用户请求的页面。</p>
<h4>每个网站中的 Default.aspx</h4>
<p>这是每个网站的主登陆页面。只有通过了 AppLogin 或 Login Web 页面的身份验证的用户才能够浏览此页以及站点中所有其他页。</p>
<div><a href="http://msdn.microsoft.com/zh-cn/library/ms972971.aspx#mainSection"><img alt="" src="http://i.msdn.microsoft.com/ms972971.arrow_px_up(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/ms972971.aspx#mainSection"><font color=#0033cc>返回页首</font></a> <br></div>
<h3 id=XSLTsection133121120120>表</h3>
<p>您还需要在数据库中创建若干表，用来支持此单一登录企业级安全系统。本文中的表所包含的字段不多，但就所述内容而言，已经足够。<strong>图</strong> <strong>4</strong> 显示了需要在数据库中创建的每个表之间的关系。在<strong>图</strong> <strong>4</strong> 之后，会看到这些表的列表，以及本解决方案中使用的每个表的说明信息。</p>
<p><img alt=singlesignon_04 src="http://i.msdn.microsoft.com/ms972971.singlesignon_04(zh-cn,MSDN.10).gif"> </p>
<div><strong>图</strong> <strong>4</strong> <strong>：实现单一登录系统的完整角色所需的若干表</strong> </div>
<h4>esApps</h4>
<p>此表包含企业中所有 Web 应用程序的列表。除应用程序的名称以外，表中还包含应用程序的详细说明信息以及应用程序的 URL。URL 是完整形式的 URL，并且应以 AppLogin.aspx 页结尾。应用程序启动器中的 default.aspx 页将负责在重定向至 Web 应用程序前向 URL 中添加字符串 &#8220;Token=&lt;GeneratedToken&gt;&#8221;。</p>
<p><img alt=singlesignon_05thumb src="http://i.msdn.microsoft.com/ms972971.singlesignon_05thumb(zh-cn,MSDN.10).gif"> </p>
<div><strong>图</strong> <strong>5</strong> <strong>：</strong> <strong>esApps </strong><strong>表的示例数据</strong> </div>
<h4>esUsers</h4>
<p>此表列出可以使用应用程序的所有用户。您需要为所有内部用户复制您的用户域登录 ID。您可能还希望为外部用户添加密码字段。</p>
<p><img alt=singlesignon_06 src="http://i.msdn.microsoft.com/ms972971.singlesignon_06(zh-cn,MSDN.10).gif"> </p>
<div><strong>图</strong> <strong>6</strong> <strong>：</strong> <strong>esUsers </strong><strong>表的示例数据</strong> </div>
<h4>esAppsUsers</h4>
<p>此表将 esUsers 表中的用户与 esApps 表中他们能够运行的应用程序关联起来。表中的数据只有 esUsers 表和 esApps 表的外键。</p>
<h4>esAppRoles</h4>
<p>此表包含每个应用程序的角色集。例如，一个应用程序可能包含&#8220;Admin&#8221;和&#8220;User&#8221;角色，而其他应用程序则可能包含&#8220;User&#8221;和&#8220;Supervisor&#8221;规则。</p>
<p><img alt=singlesignon_07thumb src="http://i.msdn.microsoft.com/ms972971.singlesignon_07thumb(zh-cn,MSDN.10).gif"> </p>
<div><strong>图</strong> <strong>7</strong> <strong>：</strong> <strong>esAppRoles </strong><strong>表的示例数据</strong> </div>
<h4>esAppUsersRoles</h4>
<p>此表包含应用程序中每个用户的每一个角色的列表。在应用程序&#8220;HR&#8221;中，用户&#8220;Joe&#8221;可能是&#8220;Supervisor&#8221;，但在应用程序&#8220;Payroll&#8221;中，则可能是&#8220;Admin&#8221;和&#8220;User&#8221;。</p>
<h4>esAppToken</h4>
<p>esAppToken 包含由登录门户应用程序生成并传递给单个 Web 应用程序的令牌。这些记录在通常情况下应只存在两秒（或更短的时间），因为正被调用的 Web 应用程序在从此表中收集信息之后，会立即删除此令牌。这样可以防止他人再次使用此令牌。</p>
<p><img alt=singlesignon_08thumb src="http://i.msdn.microsoft.com/ms972971.singlesignon_08thumb(zh-cn,MSDN.10).gif"> </p>
<div><strong>图</strong> <strong>8</strong> <strong>：</strong> <strong>esAppToken </strong><strong>表的示例数据</strong> </div>
<div><a href="http://msdn.microsoft.com/zh-cn/library/ms972971.aspx#mainSection"><img alt="" src="http://i.msdn.microsoft.com/ms972971.arrow_px_up(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/ms972971.aspx#mainSection"><font color=#0033cc>返回页首</font></a> <br></div>
<h3 id=XSLTsection134121120120>AppLauncher Web 应用程序</h3>
<p><strong>应用程序启动器解决方案（</strong> <strong>图</strong> <strong>9</strong>）由通过 Windows 身份验证的站点和类库项目组成。此通过 Windows 身份验证的站点将直接读取线程中的用户域 ID，并利用此用户域 ID 在用户表中查找用户。我们看一下显示用户能够运行的应用程序的 Web 页。</p>
<p><img alt=singlesignon_09 src="http://i.msdn.microsoft.com/ms972971.singlesignon_09(zh-cn,MSDN.10).gif"> </p>
<div><strong>图</strong> <strong>9</strong> <strong>：</strong> <strong>AppLauncherxx </strong><strong>解决方案包含</strong> <strong>AppLauncherxx </strong><strong>项目和对</strong> <strong>AppLauncherDataxx </strong><strong>项目的引用</strong> </div>
<h4>AppLauncherxx 中的 Default.aspx </h4>
<p>应用程序启动器网站只需要一个 Web 页：即 default.aspx。此 Web 页从页面的 User 对象中读取用户的 Windows 登录 ID，并加载数据库中为此用户定义的应用程序列表。以下是加载 default.aspx 页面时调用的 Page_Load 事件过程的代码。</p>
<pre>// C#
private void Page_Load(object sender, System.EventArgs e)
{
// 显示用户名称
// 不带域前缀
lblLogin.Text = "Applications Available for: " +
Apps.LoginIDNoDomain(User.Identity.Name);
AppLoad();
}
' VB.NET
Private Sub Page_Load(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles MyBase.Load
' 显示用户名称
' 不带域前缀
lblLogin.Text = "Applications Available for: " &amp; _
Apps.LoginIDNoDomain(User.Identity.Name)
AppLoad()
End Sub
</pre>
<p>调用 Apps 类的静态方法 LoginIDNoDomain 是为了去除域前缀。如果登录 ID 为&#8220;Ken&#8221;的用户通过了名为&#8220;PDSA&#8221;的域的身份验证，那么 User.Identity.Name 属性将返回&#8220;PDSA\Ken&#8221;。而此方法则仅返回字符串&#8220;Ken&#8221;。</p>
<h4>加载此用户能够运行的应用程序</h4>
<p>AppLoad 方法使用 Apps 类的一个实例检索允许此用户运行的应用程序的 <strong>DataSet</strong>。本文后面的内容将显示 Apps 类中的 GetAppsByLoginID 方法。</p>
<pre>// C#
private void AppLoad()
{
Apps app = new Apps();
try
{
// 为此用户加载应用程序
grdApps.DataSource =
app.GetAppsByLoginID(User.Identity.Name);
grdApps.DataBind();
}
catch (Exception ex)
{
lblMessage.Text = ex.Message;
}
}
' VB.NET
Private Sub AppLoad()
Dim app As New Apps
Try
' 为此用户加载应用程序
grdApps.DataSource = app.GetAppsByLoginID(User.Identity.Name)
grdApps.DataBind()
Catch ex As Exception
lblMessage.Text = ex.Message
End Try
End Sub
</pre>
<h4>LinkButton 控件</h4>
<p>default.aspx 页面的 <strong>DataGrid</strong> 控件显示指定用户的应用程序列表后（参见<strong>图</strong> <strong>3</strong>），用户就可以选择其中的应用程序。<strong>DataGrid</strong> 中用于显示超级链接以供用户单击的控件即为 <strong>LinkButton</strong>。在 Web 页面中，<strong>LinkButton</strong> 定义如下：</p>
<pre>&lt;asp:LinkButton id=lnkApp runat="server"
AppID='&lt;%# DataBinder.Eval(Container.DataItem, "iAppID") %&gt;'
UserID='&lt;%# DataBinder.Eval(Container.DataItem, "iUserID") %&gt;'
CommandArgument='&lt;%# DataBinder.Eval(Container.DataItem, "sURL") %&gt;'
Text='&lt;%# DataBinder.Eval(Container.DataItem, "sAppName") %&gt;'&gt;
&lt;/asp:LinkButton&gt;
</pre>
<p>从上述代码可以看出，增添了一些 esApps 表的主键 (iAppID) 和 esUsers 表的主键 (iUserID) 的附加属性。这些属性以及 CommandArgument 中的 URL 提供了可存储到数据库中的足够的用户信息。稍后您会看到，我们将在 <strong>ItemCommand</strong> 事件过程中检索这些附加属性。</p>
<p><strong>提示：</strong> 可以向服务器控件添加任何想要的属性。ASP.NET 将忽略这些属性，但是您可以使用服务器控件的 Attributes 属性检索它们的值。</p>
<h4>ItemCommand 事件</h4>
<p>用户单击 <strong>DataGrid</strong> 中的 <strong>LinkButton</strong> 控件后，将调用 <strong>ItemCommand</strong> 事件过程。在此方法中，需要创建 Apps 类的一个新实例，并检索 <strong>LinkButton</strong> 控件，以便获取属性，然后调用 Apps 类中的 CreateLoginToken 方法，以将此数据存储到数据库中的 esAppToken 表中。最后，从此方法检索出令牌后，此令牌将与 LinkButton 的 CommandArgument 属性中的 URL 连接，然后调用 Response.Redirect，以调用令牌所传递的 Web 应用程序。</p>
<pre>// C#
private void grdApps_ItemCommand(object source,
System.Web.UI.WebControls.DataGridCommandEventArgs e)
{
Apps app = new Apps();
bool redirect = false;
string token = String.Empty;
LinkButton lb;
try
{
lb = (LinkButton) e.Item.Cells[0].Controls[1];
// 为此用户或应用程序创建令牌
token = app.CreateLoginToken(
lb.Text,
User.Identity.Name,
Convert.ToInt32(lb.Attributes["UserID"]),
Convert.ToInt32(lb.Attributes["AppID"]));
redirect = true;
}
catch (Exception ex)
{
redirect = false;
lblMessage.Text = ex.Message;
}
if (redirect)
{
// 重定向至生成的令牌中
// 传递的 Web 应用程序
Response.Redirect(e.CommandArgument.ToString() +
"?Token=" + token, false);
}
}
' VB.NET
Private Sub grdApps_ItemCommand(ByVal source As Object, _
ByVal e As System.Web.UI.WebControls.DataGridCommandEventArgs) _
Handles grdApps.ItemCommand
Dim app As New Apps
Dim boolRedirect As Boolean
Dim token As String
Dim lb As LinkButton
Try
lb = DirectCast(e.Item.Cells(0).Controls(1), LinkButton)
' 为此用户或应用程序创建令牌
token = app.CreateLoginToken(lb.Text, _
User.Identity.Name, _
Convert.ToInt32(lb.Attributes("UserID")), _
Convert.ToInt32(lb.Attributes("AppID")))
boolRedirect = True
Catch ex As Exception
boolRedirect = False
lblMessage.Text = ex.Message
End Try
If boolRedirect Then
' 重定向至生成的令牌中
' 传递的 Web 应用程序
Response.Redirect(e.CommandArgument.ToString() &amp; _
"?Token=" &amp; token, False)
End If
End Sub
</pre>
<p>您会注意到，在上面的代码中，检索了 e.Item 参数中的 LinkButton 控件。e.Item 用于引用您在 DataGrid 中单击的行。您可以在 LinkButton 所在的列后检索所单击的 LinkButton 控件的特定实例。在本例中，它在 Cells(0) 中。在此单元格中，可以在 Controls(1) 后取得该控件。该控件位于位置一 (1) 的原因是 DataGrid 中所使用的、允许我们将 LinkButton 置于单元格中的 ItemTemplate 被看作是元素零 (0)。</p>
<p>检索出 LinkButton 后，就可以使用 Attributes 属性检索在设置 LinkButton 时存储的 UserID 和 AppID。Attributes 属性是您向服务器控件添加的所有附加属性的集合，这些附加属性不是初始控件定义的组成部分。</p>
<h4>修改 Web.Config</h4>
<p>在 Web 应用程序对用户进行身份验证之前，必须将 Web.Config 文件中的 &lt;authentication&gt; 元素设置为&#8220;Windows&#8221;。此外，还必须拒绝 Web.config 中的 &lt;authorization&gt; 元素中的匿名用户。</p>
<pre>&lt;authorization&gt;
&lt;deny users="?" /&gt;
&lt;/authorization&gt;
</pre>
<p>设置这两个元素可从浏览器强制服务器检索用户的 Windows 凭据。当然，只有当您在用户所登录的域中使用 Internet Explorer 时，它才会起作用。</p>
<p>最后一项要完成的工作是存储连接字符串，以便从数据库的表中获取。在本文的示例中，我只使用了 Web.config 中的 &lt;appSettings&gt; 部分存储连接字符串。</p>
<pre>&lt;appSettings&gt;
&lt;add key="eSecurityConnectString"
value="server=(local);Database=eSecurity;uid=myUserID;pwd=myPassword" /&gt;
&lt;/appSettings&gt;
</pre>
<p>在本文中，我在 SQL Server&#8482; 中创建了名为 eSecurity 的数据库，并创建了本文前面内容中所述的所有的表。本文的示例代码包含一个 SQL 脚本，可以运行此脚本以在 SQL Server 数据库中创建表。如果使用其他数据库系统，则需要根据您的数据库对脚本进行适当的修改。</p>
<div><a href="http://msdn.microsoft.com/zh-cn/library/ms972971.aspx#mainSection"><img alt="" src="http://i.msdn.microsoft.com/ms972971.arrow_px_up(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/ms972971.aspx#mainSection"><font color=#0033cc>返回页首</font></a> <br></div>
<h3 id=XSLTsection137121120120>Apps 类</h3>
<p>现在看一下在此企业级安全系统中用于实现主要功能的 <strong>Apps</strong> 类。AppLauncherData 程序集包含的三个类中的每一个类都可负责为用户加载应用程序、加载用户角色以及操作安全令牌。较好的做法是保留与数据库进行交互的功能，以及保留在用户界面层以外操作令牌的功能。这可以使您不必更改用户界面就可以修改令牌的创建方法，以及修改与数据库交互的方式。</p>
<p>Apps 类负责操作令牌以及为用户加载应用程序。我们看一下这个类的定义。</p>
<pre>// C#
public class Apps
{
string mConnectString;
public Apps()
{
mConnectString = ConfigurationSettings.
AppSettings["eSecurityConnectString"];
}
...
}
' VB.NET
Public Class Apps
Private mConnectString As String
Public Sub New()
mConnectString = ConfigurationSettings. _
AppSettings("eSecurityConnectString")
End Sub
...
End Class
</pre>
<p>可以看到这个类首先使用从 Web.config 文件中检索出的连接字符串加载成员变量。</p>
<h4>GetAppsByLoginID 方法</h4>
<p>要为特定的用户加载应用程序，应将用户的登录 ID 传递给 GetAppsByLoginID 方法。此方法负责执行联接，以检索所有相关的信息。使用 SQL 联接需要从 esApps 和 esAppsUsers 表中获取信息。此外，还需要联接 esUsers 表，因为只需要检索特定用户的应用程序，而所有我们可用的信息只有用户的 Login ID，所以必须在 esUsers 表中查找用户的主键，以便联接到其他表。</p>
<p><strong>注意：</strong> 本文使用动态 SQL 只是为了显示概念。在实际的企业级安全系统中，需要对所有的 SQL 调用使用存储过程。</p>
<pre>// C#
public DataSet GetAppsByLoginID(string loginID)
{
DataSet ds = new DataSet();
SqlCommand cmd;
SqlDataAdapter da;
string sql;
sql = "SELECT esApps.iAppID, esAppsUsers.iUserID, ";
sql += " esApps.sAppName, esApps.sDesc, esApps.sURL ";
sql += " FROM esApps";
sql += " INNER JOIN esAppsUsers ";
sql += " ON esApps.iAppID = esAppsUsers.iAppID ";
sql += " INNER JOIN esUsers ";
sql += " ON esAppsUsers.iUserID = esUsers.iUserID ";
sql += " WHERE sLoginID = @sLoginID ";
sql = String.Format(sql, Apps.LoginIDNoDomain(loginID));
try
{
cmd = new SqlCommand(sql);
cmd.Parameters.Add(new
SqlParameter("@sLoginID", SqlDbType.Char));
cmd.Parameters["@sLoginID"].Value =
Apps.LoginIDNoDomain(loginID);
cmd.Connection = new SqlConnection(mConnectString);
da = new SqlDataAdapter(cmd);
da.Fill(ds);
return ds;
}
catch (Exception ex)
{
throw ex;
}
}
' VB.NET
Public Function GetAppsByLoginID(ByVal LoginID As String) _
As DataSet
Dim ds As New DataSet
Dim cmd As SqlCommand
Dim da As SqlDataAdapter
Dim sql As String
sql = "SELECT esApps.iAppID, esAppsUsers.iUserID, "
sql &amp;= " esApps.sAppName, esApps.sDesc, esApps.sURL "
sql &amp;= " FROM esApps"
sql &amp;= " INNER JOIN esAppsUsers "
sql &amp;= " ON esApps.iAppID = esAppsUsers.iAppID "
sql &amp;= " INNER JOIN esUsers "
sql &amp;= " ON esAppsUsers.iUserID = esUsers.iUserID "
sql &amp;= " WHERE sLoginID = @sLoginID "
sql = String.Format(sql, Apps.LoginIDNoDomain(LoginID))
Try
cmd = New SqlCommand(sql)
cmd.Parameters.Add(New _
SqlParameter("@sLoginID", SqlDbType.Char))
cmd.Parameters("@sLoginID").Value = _
Apps.LoginIDNoDomain(LoginID)
cmd.Connection = New SqlConnection(mConnectString)
da = New SqlDataAdapter(cmd)
da.Fill(ds)
Return ds
Catch ex As Exception
Throw ex
End Try
End Function
</pre>
<h4>CreateLoginToken 方法</h4>
<p>用户单击在 DataGrid 中的应用程序后，必须生成一个新令牌。CreateLoginToken 方法负责执行此任务。</p>
<pre>// C#
public string CreateLoginToken(string appName,
string loginID, int userID, int appID)
{
SqlCommand cmd = new SqlCommand();
SqlParameter param;
string token;
string sql;
// 生成新令牌
token = GenerateToken();
sql = "INSERT INTO esAppToken(sToken, sAppName, ";
sql += " sLoginID, iUserID, iAppID, dtCreated) ";
sql += " VALUES(@sToken, @sAppName, @sLoginID, ";
sql += "        @iUserID, @iAppID, @dtCreated) ";
param = new SqlParameter("@sToken", SqlDbType.Char);
param.Value = token;
cmd.Parameters.Add(param);
param = new SqlParameter("@sAppName", SqlDbType.Char);
param.Value = appName;
cmd.Parameters.Add(param);
param = new SqlParameter("@sLoginID", SqlDbType.Char);
param.Value = Apps.LoginIDNoDomain(loginID);
cmd.Parameters.Add(param);
param = new SqlParameter("@iUserID", SqlDbType.Int);
param.Value = userID;
cmd.Parameters.Add(param);
param = new SqlParameter("@iAppID", SqlDbType.Int);
param.Value = appID;
cmd.Parameters.Add(param);
param = new SqlParameter("@dtCreated", SqlDbType.DateTime);
param.Value = DateTime.Now;
cmd.Parameters.Add(param);
try
{
cmd.CommandType = CommandType.Text;
cmd.CommandText = sql;
cmd.Connection = new SqlConnection(mConnectString);
cmd.Connection.Open();
cmd.ExecuteNonQuery();
}
catch (Exception ex)
{
throw ex;
}
finally
{
if (cmd.Connection.State != ConnectionState.Closed)
{
cmd.Connection.Close();
cmd.Connection.Dispose();
}
}
return token;
}
' VB.NET
Public Function CreateLoginToken(ByVal AppName As String, _
ByVal LoginID As String, ByVal UserID As Integer, _
ByVal AppID As Integer) As String
Dim cmd As New SqlCommand
Dim param As SqlParameter
Dim token As String
Dim sql As String
' 生成新令牌
token = GenerateToken()
sql = "INSERT INTO esAppToken(sToken, sAppName, "
sql &amp;= " sLoginID, iUserID, iAppID, dtCreated) "
sql &amp;= " VALUES(@sToken, @sAppName, @sLoginID, "
sql &amp;= " @iUserID, @iAppID, @dtCreated)"
sql = String.Format(sql, token, AppName, _
Apps.LoginIDNoDomain(LoginID), UserID, AppID, _
DateTime.Now.ToString())
param = New SqlParameter("@sToken", SqlDbType.Char)
param.Value = token
cmd.Parameters.Add(param)
param = New SqlParameter("@sAppName", SqlDbType.Char)
param.Value = AppName
cmd.Parameters.Add(param)
param = New SqlParameter("@sLoginID", SqlDbType.Char)
param.Value = Apps.LoginIDNoDomain(LoginID)
cmd.Parameters.Add(param)
param = New SqlParameter("@iUserID", SqlDbType.Int)
param.Value = UserID
cmd.Parameters.Add(param)
param = New SqlParameter("@iAppID", SqlDbType.Int)
param.Value = AppID
cmd.Parameters.Add(param)
param = New SqlParameter("@dtCreated", SqlDbType.DateTime)
param.Value = DateTime.Now
cmd.Parameters.Add(param)
Try
cmd.CommandType = CommandType.Text
cmd.CommandText = sql
cmd.Connection = New SqlConnection(mConnectString)
cmd.Connection.Open()
cmd.ExecuteNonQuery()
Catch ex As Exception
Throw ex
Finally
If cmd.Connection.State &lt;&gt; ConnectionState.Closed Then
cmd.Connection.Close()
cmd.Connection.Dispose()
End If
End Try
Return token
End Function
</pre>
<p>要创建令牌，应调用 GenerateToken 方法。此方法独立于 CreateLoginToken 方法的原因是，它允许您更改以后要生成的令牌的类型。在本文的末尾，提出了有关的设想。此方法使用 Guid 类生成一个新的 GUID 作为令牌。</p>
<pre>// C#
public string GenerateToken()
{
return System.Guid.NewGuid().ToString();
}
' VB.NET
Public Function GenerateToken() As String
Return System.Guid.NewGuid().ToString()
End Function
</pre>
<div><a href="http://msdn.microsoft.com/zh-cn/library/ms972971.aspx#mainSection"><img alt="" src="http://i.msdn.microsoft.com/ms972971.arrow_px_up(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/ms972971.aspx#mainSection"><font color=#0033cc>返回页首</font></a> <br></div>
<h3 id=XSLTsection140121120120>在 Web 应用程序中检索令牌</h3>
<p>要从应用程序启动器测试启动应用程序，应创建一个可从启动器调用的用于测试的 Web 应用程序（参见<strong>图</strong> <strong>10</strong>）。这个用于测试的 Web 应用程序将使用前面所讨论的 AppLauncherDataxx 项目。</p>
<p><img alt=singlesignon_10 src="http://i.msdn.microsoft.com/ms972971.singlesignon_10(zh-cn,MSDN.10).gif"> </p>
<div><strong>图</strong> <strong>10</strong> <strong>：每个</strong> <strong>Web </strong><strong>应用程序都将使用结合了</strong> <strong>AppLogin</strong> <strong>、</strong> <strong>Login </strong><strong>和</strong> <strong>Default </strong><strong>页面的</strong> <strong>AppLauncherDataxx </strong><strong>项目</strong> </div>
<h4>修改 Web.Config</h4>
<p>要创建集成了单一登录系统的 Web 应用程序，首先需要修改 Web.config 文件，并创建 &lt;appSettings&gt; 区段，以存储连接字符串，从而与 eSecurity 数据库进行交互。还需要存储应用程序 ID 和应用程序名称，以供外部用户进入时使用。因为外部用户进入时，尚未创建令牌，这时需要知道用户选择的应用程序，以便在建立用户角色时加载要使用的 AppToken 对象。在本文后面的内容中，您会了解如何这样做。</p>
<pre>&lt;appSettings&gt;
&lt;add key="eSecurityConnectString"
value="server=(local);Database=
eSecurity;uid=mUserID;pwd=myPassword"&gt;&lt;/add&gt;
&lt;add key="eSecurityAppID" value="1"&gt;&lt;/add&gt;
&lt;add key="eSecurityAppName" value="Payroll"&gt;&lt;/add&gt;
&lt;/appSettings&gt;
</pre>
<p>您还需要设置 Web 应用程序，以使用基于窗体的身份验证。要进行此操作，请修改 &lt;authentication&gt; 元素，如下所示。</p>
<pre>&lt;authentication mode="Forms"&gt;
&lt;forms name="AppTest" loginUrl="Login.aspx" /&gt;
&lt;/authentication&gt;
</pre>
<p>最后，为了拒绝匿名用户访问，还需要修改 &lt;authorization&gt; 元素。</p>
<pre>&lt;authorization&gt;
&lt;deny users="?" /&gt;
&lt;/authorization&gt;
</pre>
<h4>AppLogin.aspx 页</h4>
<p>请记住，从应用程序启动器调用的每个应用程序都会调用 AppLogin 页，并向此页传递生成的令牌。AppLogin 页会验证此令牌是否正确，从 esAppToken 表检索相关的信息，然后删除 esAppToken 中的记录，以使该令牌不被重复使用。</p>
<pre>// C#
private void Page_Load(object sender, System.EventArgs e)
{
VerifyToken();
}
private void VerifyToken()
{
Apps app = new Apps();
AppToken al;
try
{
al = app.VerifyLoginToken(
Request.QueryString["Token"].ToString());
if(al.LoginID.Trim() == "")
{
// 非有效登录
// 将其重定向至默认页面
// 这将使用户转至登录页面
Response.Redirect("default.aspx");
}
else
{
// 创建窗体身份验证 Cookie
// 设置窗体身份验证变量
FormsAuthentication.Initialize();
FormsAuthentication.SetAuthCookie(
al.LoginID.ToString(), false);
// 设置应用程序令牌对象
Application["AppToken"] = al;
// 重定向至默认页面
Response.Redirect("default.aspx");
}
}
catch
{
// 通过默认页面将用户重定向至登录页面
Response.Redirect("default.aspx");
}
}
' VB.NET
Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
VerifyToken()
End Sub
Private Sub VerifyToken()
Dim app As New Apps
Dim al As AppToken
Try
al = app.VerifyLoginToken( _
Request.QueryString("Token").ToString())
If al.LoginID.Trim() = "" Then
' 非有效登录
' 将其重定向至默认页面
' 这将使用户转至登录页面
Response.Redirect("default.aspx")
Else
' 创建窗体身份验证 Cookie
' 设置窗体身份验证变量
FormsAuthentication.Initialize()
FormsAuthentication.SetAuthCookie( _
al.LoginID.ToString(), False)
' 设置应用程序令牌对象
Application("AppToken") = al
' 重定向至默认页面
Response.Redirect("default.aspx")
End If
Catch
' 通过默认页面将用户重定向至登录页面
Response.Redirect("default.aspx")
End Try
End Sub
</pre>
<p>在 VerifyLogin 方法中，首先检查令牌是否有效。这一操作通过调用 Apps 类中的 VerifyLoginToken 方法完成。此方法返回 AppToken 类的一个实例。如果此类的 LoginID 属性存在值，就表明此用户是有效用户。如果没有值，则令牌无效。在令牌无效的情况下，此方法将用户重定向到网站中的 default.aspx 页面。当然，如果打开了基于窗体的身份验证，用户会被强制重定向至 Login.aspx 页面，被请求重新登录。</p>
<p>调用 FormsAuthentication.Initialize 和 FormsAuthentication.SetAuthCookie 方法可以向外发送窗体验证 cookie。这会将内存中的 cookie 发送到浏览器。每次用户返回此站点时，ASP.NET 运行库都会检查此 cookie。</p>
<h4>VerifyLoginToken 方法</h4>
<p>此方法通过查询行接受生成的令牌，并对其进行检查，以确保令牌有效。此方法会转到 esAppToken 表检查此令牌。如果在表中找到此令牌，则将表中所有的值置于 AppToken 对象的各个属性中，然后从此方法返回该 AppToken 对象。</p>
<pre>// C#
public AppToken VerifyLoginToken(string Token)
{
AppToken al = new AppToken();
DataSet ds = new DataSet();
SqlCommand cmd;
DataRow dr;
SqlDataAdapter da;
string sql;
sql = "SELECT iAppTokenID, sAppName, sLoginID, ";
sql += " iAppID, iUserID ";
sql += " FROM esAppToken";
sql += " WHERE sToken = @sToken ";
try
{
cmd = new SqlCommand(sql);
cmd.Parameters.Add(new
SqlParameter("@sToken", SqlDbType.Char));
cmd.Parameters["@sToken"].Value = Token;
cmd.Connection = new SqlConnection(mConnectString);
da = new SqlDataAdapter(cmd);
da.Fill(ds);
if (ds.Tables[0].Rows.Count &gt; 0)
{
dr = ds.Tables[0].Rows[0];
al.LoginID = dr["sLoginID"].ToString();
al.AppName = dr["sAppName"].ToString();
al.AppKey = Convert.ToInt32(dr["iAppID"]);
al.LoginKey = Convert.ToInt32(dr["iUserID"]);
DeleteToken(Convert.ToInt32(dr["iAppTokenID"]));
}
}
catch (Exception ex)
{
throw ex;
}
return al;
}
' VB.NET
Public Function VerifyLoginToken(ByVal Token As String) As AppToken
Dim al As New AppToken
Dim ds As New DataSet
Dim cmd As SqlCommand
Dim dr As DataRow
Dim da As SqlDataAdapter
Dim sql As String
sql = "SELECT iAppTokenID, sAppName, sLoginID, "
sql &amp;= " iAppID, iUserID "
sql &amp;= " FROM esAppToken"
sql &amp;= " WHERE sToken = @sToken "
Try
cmd = New SqlCommand(sql)
cmd.Parameters.Add(New _
SqlParameter("@sToken", SqlDbType.Char))
cmd.Parameters("@sToken").Value = Token
cmd.Connection = New SqlConnection(mConnectString)
da = New SqlDataAdapter(cmd)
da.Fill(ds)
If ds.Tables(0).Rows.Count &gt; 0 Then
dr = ds.Tables(0).Rows(0)
al.LoginID = dr("sLoginID").ToString()
al.AppName = dr("sAppName").ToString()
al.AppKey = Convert.ToInt32(dr("iAppID"))
al.LoginKey = Convert.ToInt32(dr("iUserID"))
DeleteToken(Convert.ToInt32(dr("iAppTokenID")))
End If
Catch ex As Exception
Throw ex
End Try
Return al
End Function
</pre>
<h4>基于角色的安全性</h4>
<p>用户通过 Web 应用程序的验证后，将被重定向至 default.aspx 页面。因为已经向浏览器发送了身份验证 cookie，每次都会将此 cookie 发送回来。在向用户传送请求的页面之前，将调用 global.asax 文件中的 Application_AuthenticateRequest 方法。只有在这个方法中才能够建立要与此用户关联的任何角色。我不打算在这里显示此方法的用法，因为专门有文章对其进行讨论。您可以查看示例代码中使用此方法的例子。基于角色的安全性的代码十分简单。您完全可以改进此代码，使其能够使用缓存。它的目的只是告诉您如何着手去做。</p>
<div><a href="http://msdn.microsoft.com/zh-cn/library/ms972971.aspx#mainSection"><img alt="" src="http://i.msdn.microsoft.com/ms972971.arrow_px_up(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/ms972971.aspx#mainSection"><font color=#0033cc>返回页首</font></a> <br></div>
<h3 id=XSLTsection143121120120>增强您的单一登录系统</h3>
<p>本文介绍了创建企业级安全系统方面的很多内容。但若要介绍创建企业级安全系统的所有方面，则超出了本文的范围。例如，您还应当添加一系列的维护屏幕，以允许管理员添加用户，并且建立用户和应用程序之间的映射关系。也需要对这组屏幕采取安全措施，以便只有特定角色内的用户才能够打开它们。</p>
<p>系统另外一个需要增强的地方是自动添加不在系统中的域用户的能力。它能够帮助您向系统中添加用户，而不必手工输入用户信息，要么您也可以编写添加用户的应用程序。您一般会将这些用户指定为默认角色，以便只允许他们运行某些应用程序而不是全部应用程序。此外，如果以这种方式添加了新用户，管理员还应当收到电子邮件形式的通知信息。</p>
<p>因为这种安全系统依赖于生成的特定的令牌，而在创建令牌之后，会立即调用 Web 应用程序，所以如果应用程序未启动以响应请求，就可能出现问题。如果出现这种情况，那么令牌就会留在数据库中，从而导致潜在的安全性风险。所以还需要预先安排一项工作，用来删除超过指定时间长度的令牌。也可以创建自己特定的令牌，该令牌由令牌和创建此令牌的时间组成。这样就只需修改 VerifyLoginToken 方法，以检查时间，确保它小于指定的时间长度。</p>
<p>本文中使用的令牌为 GUID 类型，它强制您回调数据库，以检索用户的配置文件信息。本系统的一个较好的增强方案是使用基于 WS-Security 增强标准的令牌加密配置文件信息，并仅将其作为令牌由应用程序启动器传递给每个 Web 应用程序。这样能够避免每次往返数据库。</p>
<p>当然，你可能希望更改代码中的所有动态 SQL 调用，以使用存储过程和带参数的命令对象来避免遭受任何 SQL 流量注入攻击，彻底保证数据表的安全。对于命令对象，应使用存储过程和参数。</p>
<div><a href="http://msdn.microsoft.com/zh-cn/library/ms972971.aspx#mainSection"><img alt="" src="http://i.msdn.microsoft.com/ms972971.arrow_px_up(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/ms972971.aspx#mainSection"><font color=#0033cc>返回页首</font></a> <br></div>
<h3 id=XSLTsection146121120120>安装示例</h3>
<p>本文附带两个示例应用程序。它们都是 Web 应用程序。解决方案中还包含为每个 Web 应用程序提供的类库。示例具有 Microsoft Visual Basic&#174; .NET 和 C# 两种版本，可以选择希望使用的版本。解决方案文件中还有一个 .SQL 文件，可用来帮助您在数据库中创建所需的表。.SQL 文件用于 SQL Server，但经过简单的修改，也可用于其他数据库系统。以下是安装示例应用程序应执行的步骤。 </p>
<ul>
    <li>
    <p>在 DBMS 中创建名为 eSecurity 的数据库。 </p>
    <li>
    <p>执行 .SQL 文件，在 eSecurity 数据库中创建表。 </p>
    <li>
    <p>将提供的 .ZIP 文件解压到文件夹中。 </p>
    <li>
    <p>创建虚拟目录，使其指向希望使用的每个文件夹。例如，如果使用的是 VB.NET 版本的示例，那么请创建两个名称分别为 AppLauncherVB 和 AppTestVB 的虚拟目录，并使其指向这两个文件夹。 </p>
    <li>
    <p>修改 .SLN 文件，使其指向您创建的虚拟目录文件夹。 </p>
    </li>
</ul>
<div><a href="http://msdn.microsoft.com/zh-cn/library/ms972971.aspx#mainSection"><img alt="" src="http://i.msdn.microsoft.com/ms972971.arrow_px_up(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/ms972971.aspx#mainSection"><font color=#0033cc>返回页首</font></a> <br></div>
<h3 id=XSLTsection149121120120>结论</h3>
<p>Web 应用程序的单一登录系统有助于节省用户时间，并减轻在企业中因需要记忆登录 ID 和密码所带来的不便。此外，它还允许外部用户使用内部 Web 应用程序，而不必将这些用户添加到域中。实现这样一个系统只需创建几个简单的表和类。但要创建一个完全使用单一登录方式的可靠的企业级安全系统，还需要做许多工作，本文中的示例可以使您有一个良好的开端。</p>
<img src ="http://www.cnitblog.com/MartinYao/aggbug/52412.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/MartinYao/" target="_blank">玄铁剑</a> 2008-12-09 21:34 <a href="http://www.cnitblog.com/MartinYao/articles/52412.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>ASP.NET 的客户端证书调用 Web 服务</title><link>http://www.cnitblog.com/MartinYao/articles/52410.html</link><dc:creator>玄铁剑</dc:creator><author>玄铁剑</author><pubDate>Tue, 09 Dec 2008 13:32:00 GMT</pubDate><guid>http://www.cnitblog.com/MartinYao/articles/52410.html</guid><wfw:comment>http://www.cnitblog.com/MartinYao/comments/52410.html</wfw:comment><comments>http://www.cnitblog.com/MartinYao/articles/52410.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/MartinYao/comments/commentRss/52410.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/MartinYao/services/trackbacks/52410.html</trackback:ping><description><![CDATA[<p>Microsoft Corporation</p>
<p><strong>目标</strong> </p>
<p>本模块用于： </p>
<ul>
    <li>
    <p>使用服务组件使 Web 应用程序调用要求客户端使用客户端证书进行身份验证的 Web 服务。 </p>
    </li>
</ul>
<p><strong>适用于：</strong> </p>
<p>本模块适用于下列产品和技术： </p>
<ul>
    <li>
    <p>Microsoft&#174; Windows&#174; XP 或 Windows 2000 Server（带 Service Pack 3）以及更高版本的操作系统 </p>
    <li>
    <p>Microsoft Internet 信息服务 5.0 </p>
    <li>
    <p>Microsoft 证书服务（如果需要生成自己的客户端证书） </p>
    <li>
    <p>Microsoft .NET Framework 版本 1.0（带 Service Pack 2） </p>
    <li>
    <p>Microsoft SQL Server&#8482; 2000（带 Service Pack 2）以及更高版本 </p>
    <li>
    <p>Microsoft Visual C#&#174; .NET 开发工具 </p>
    </li>
</ul>
<p><strong>本模块的使用方法</strong> </p>
<p>要最大程度的使用本模块： </p>
<ul>
    <li>
    <p>必须有使用 Visual C# .NET 和 Microsoft Visual Studio_ .NET 开发系统的经验。 </p>
    <li>
    <p>必须有使用 ASP.NET 开发 Web 应用程序的经验。 </p>
    <li>
    <p>必须有开发和实现在 Enterprise Services (COM+) 上下文中运行的服务组件的经验。 </p>
    <li>
    <p>必须有开发和实现 Windows 服务的经验。 </p>
    <li>
    <p>必须了解如何使用 Windows 管理工具创建 Windows 用户帐户。 </p>
    <li>
    <p>必须能够访问证书颁发机构 (CA)，如 Microsoft 证书服务（如果要生成新的客户端证书）。 </p>
    <li>
    <p>如果不愿生成自己的证书，则必须决定从哪个商业 CA 申请一个客户端证书。某些 CA 将对此服务收费。 </p>
    <li>
    <p>必须有一个安装了 SSL 证书的 Web 服务器。有关详细信息，请参阅&#8220;<a id=ctl00_rs1_mainContentContainer_ctl02 onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00|ctl00_rs1_mainContentContainer_ctl02',this);" href="http://msdn.microsoft.com/library/en-us/secmod/html/secmod30.asp"><font color=#0033cc>How To Set Up SSL on a Web Server</font></a>&#8221;。 </p>
    <li>
    <p>阅读模块&#8220;<a id=ctl00_rs1_mainContentContainer_ctl03 onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00|ctl00_rs1_mainContentContainer_ctl03',this);" href="http://msdn.microsoft.com/library/en-us/secmod/html/secmod02.asp"><font color=#0033cc>Security Model for ASP.NET Applications</font></a>&#8221;，此模块介绍了客户端证书并将其与其他形式的身份验证进行比较。 </p>
    </li>
</ul>
<h5>本页内容</h5>
<p><a href="http://msdn.microsoft.com/zh-cn/library/aa302408.aspx#E0CB0AA"><img alt=摘要 src="http://i.msdn.microsoft.com/Aa302408.arrow_px_down(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/aa302408.aspx#E0CB0AA"><font color=#0033cc>摘要</font></a> <br><a href="http://msdn.microsoft.com/zh-cn/library/aa302408.aspx#E6AA"><img alt=预备知识 src="http://i.msdn.microsoft.com/Aa302408.arrow_px_down(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/aa302408.aspx#E6AA"><font color=#0033cc>预备知识</font></a> <br><a href="http://msdn.microsoft.com/zh-cn/library/aa302408.aspx#E3AA"><img alt="创建一个简单的 Web 服务" src="http://i.msdn.microsoft.com/Aa302408.arrow_px_down(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/aa302408.aspx#E3AA"><font color=#0033cc>创建一个简单的 Web 服务</font></a> <br><a href="http://msdn.microsoft.com/zh-cn/library/aa302408.aspx#EZAA"><img alt="将 Web 服务虚拟目录配置为需要客户端证书" src="http://i.msdn.microsoft.com/Aa302408.arrow_px_down(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/aa302408.aspx#EZAA"><font color=#0033cc>将 Web 服务虚拟目录配置为需要客户端证书</font></a> <br><a href="http://msdn.microsoft.com/zh-cn/library/aa302408.aspx#EWAA"><img alt=创建运行服务组件的自定义帐户 src="http://i.msdn.microsoft.com/Aa302408.arrow_px_down(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/aa302408.aspx#EWAA"><font color=#0033cc>创建运行服务组件的自定义帐户</font></a> <br><a href="http://msdn.microsoft.com/zh-cn/library/aa302408.aspx#ETAA"><img alt=为自定义帐户申请一个客户端证书 src="http://i.msdn.microsoft.com/Aa302408.arrow_px_down(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/aa302408.aspx#ETAA"><font color=#0033cc>为自定义帐户申请一个客户端证书</font></a> <br><a href="http://msdn.microsoft.com/zh-cn/library/aa302408.aspx#EQAA"><img alt=使用浏览器测试客户端证书 src="http://i.msdn.microsoft.com/Aa302408.arrow_px_down(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/aa302408.aspx#EQAA"><font color=#0033cc>使用浏览器测试客户端证书</font></a> <br><a href="http://msdn.microsoft.com/zh-cn/library/aa302408.aspx#ENAA"><img alt=将客户端证书导出到文件 src="http://i.msdn.microsoft.com/Aa302408.arrow_px_down(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/aa302408.aspx#ENAA"><font color=#0033cc>将客户端证书导出到文件</font></a> <br><a href="http://msdn.microsoft.com/zh-cn/library/aa302408.aspx#EKAA"><img alt="开发用于调用 Web 服务的服务组件" src="http://i.msdn.microsoft.com/Aa302408.arrow_px_down(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/aa302408.aspx#EKAA"><font color=#0033cc>开发用于调用 Web 服务的服务组件</font></a> <br><a href="http://msdn.microsoft.com/zh-cn/library/aa302408.aspx#EHAA"><img alt=配置和安装服务组件 src="http://i.msdn.microsoft.com/Aa302408.arrow_px_down(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/aa302408.aspx#EHAA"><font color=#0033cc>配置和安装服务组件</font></a> <br><a href="http://msdn.microsoft.com/zh-cn/library/aa302408.aspx#EEAA"><img alt="开发一个 Web 应用程序来调用服务组件" src="http://i.msdn.microsoft.com/Aa302408.arrow_px_down(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/aa302408.aspx#EEAA"><font color=#0033cc>开发一个 Web 应用程序来调用服务组件</font></a> <br><a href="http://msdn.microsoft.com/zh-cn/library/aa302408.aspx#EBAA"><img alt=其他资源 src="http://i.msdn.microsoft.com/Aa302408.arrow_px_down(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/aa302408.aspx#EBAA"><font color=#0033cc>其他资源</font></a> <br></p>
<h3 id=E0CB0AA>摘要</h3>
<p>客户端证书为 Web 应用程序提供了一种出色的身份验证机制。浏览器或其他客户端应用程序必须提供有效的证书才能被授予对应用程序的访问权，从而使客户端无需再提供用户名和密码。这就使得在创建由其他客户端应用程序访问的安全 Web 服务时，客户端证书非常有用。</p>
<p>本模块描述了如何调用一个配置为需要 Web 应用程序颁发客户端证书的 Web 服务。</p>
<div><a href="http://msdn.microsoft.com/zh-cn/library/aa302408.aspx#mainSection"><img alt="" src="http://i.msdn.microsoft.com/Aa302408.arrow_px_up(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/aa302408.aspx#mainSection"><font color=#0033cc>返回页首</font></a> <br></div>
<h3 id=E6AA>预备知识</h3>
<p>在使用客户端证书时，您的应用程序还可从创建客户端应用程序和 Web 服务之间的安全信道（使用安全套接字层 [SSL]）中获益。这使您能够安全地将保密信息发送给 Web 服务，或从 Web 服务送回。SSL 确保了消息的完整性和保密性。</p>
<p><strong>注</strong> 本模块中的信息还适用于 ASP.NET 和 IIS 宿主的远程组件。<strong> </strong></p>
<h4>为什么使用服务组件？</h4>
<p>本模块中提供的解决方案使用了一个服务组件，该服务组件配置为使用自定义服务帐户在 Enterprise Services 服务器应用程序中运行。ASP.NET Web 应用程序调用服务组件，这会导致对 Web 服务的调用（传递一个客户端证书）。此解决方案的配置如<strong>图</strong> <strong>1 </strong>所示。</p>
<p><img alt=fh13sn01 src="http://i.msdn.microsoft.com/Aa302408.fh13sn01(zh-cn,MSDN.10).gif"> </p>
<div><strong>图</strong> <strong>1. ASP.NET </strong><strong>调用一个服务组件来调用</strong> <strong>Web </strong><strong>服务</strong> </div>
<p>这种安排确保了系统在与 Web 服务通信时有权访问用户配置文件。最初的 SSL 握手需要这种方式。</p>
<p><strong>注</strong> 用于运行 Web 应用程序的 ASPNET 帐户具有&#8220;拒绝交互登录&#8221;特权，从而禁止使用此帐户进行交互式登录。因此，此帐户没有用户配置文件。不要向 ASPNET 帐户（或用于运行 Web 应用程序的任何帐户）授予交互登录功能。在配置帐户来运行 Web 应用程序时，应始终遵循最小特权原则，尽可能少地向帐户授予特权。有关详细信息，请参阅&#8220;<a id=ctl00_rs1_mainContentContainer_ctl04 onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00|ctl00_rs1_mainContentContainer_ctl04',this);" href="http://msdn.microsoft.com/library/en-us/secmod/html/secmod15.asp"><font color=#0033cc>How To Create a Custom Account to Run ASP.NET</font></a>&#8221;。 </p>
<h4>为什么需要用户配置文件？</h4>
<p>在您申请使用需要客户端证书的 Web 服务时，客户端和服务器之间会发生 SSL 握手。要交换的一些组件有服务器证书、客户端证书以及由客户端生成的&#8220;预主机密 (pre-master secret)&#8221;。稍后在协议中使用此机密来生成&#8220;主机密 (master secret)&#8221;。</p>
<p>为了让服务器验证证书的提交者是否确实是私钥的持有者，客户端必须使用私钥加密预主机密，并将已加密的预主机密发送给服务器。为了使系统获得客户端的私钥来对预主机密进行签名，系统必须从客户端的密钥存储区中获得私钥。密钥存储区位于客户端的配置文件中，必须加载此配置文件。</p>
<p><strong>注</strong> 在本模块中，Web 服务计算机（它宿主 Web服务）被命名为&#8220;WSServer&#8221;，Web 服务客户端计算机（它宿主 ASP.NET Web 应用程序和服务组件）被命名为&#8220;WSClient&#8221;。<strong> </strong></p>
<div><a href="http://msdn.microsoft.com/zh-cn/library/aa302408.aspx#mainSection"><img alt="" src="http://i.msdn.microsoft.com/Aa302408.arrow_px_up(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/aa302408.aspx#mainSection"><font color=#0033cc>返回页首</font></a> <br></div>
<h3 id=E3AA>创建一个简单的 Web 服务</h3>
<p><strong>要在</strong> <strong>Web </strong><strong>服务宿主计算机上创建一个简单的</strong> <strong>Web </strong><strong>服务，请执行下列操作：</strong> </p>
<ol>
    <li>
    <p>启动 Visual Studio .NET 并创建一个新的名为 <strong>SecureMath</strong> 的 Visual C# ASP.NET Web 服务应用程序。 </p>
    <li>
    <p>将 service1.asmx 重命名为 math.asmx。 </p>
    <li>
    <p>打开 math.asmx.cs 并将 <strong>Service1</strong> 类重命名为 <strong>math</strong>。 </p>
    <li>
    <p>将下列 Web 方法添加到 <strong>math</strong> 类。 </p>
    <pre> [WebMethod]
    public long Add(long operand1, long operand2)
    {
    return (operand1 + operand2);
    }
    </pre>
    <li>
    <p>在 <strong>Build</strong> 菜单上，单击 <strong>Build</strong> <strong>Solution</strong> 来创建 Web 服务。 </p>
    </li>
</ol>
<div><a href="http://msdn.microsoft.com/zh-cn/library/aa302408.aspx#mainSection"><img alt="" src="http://i.msdn.microsoft.com/Aa302408.arrow_px_up(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/aa302408.aspx#mainSection"><font color=#0033cc>返回页首</font></a> <br></div>
<h3 id=EZAA>将 Web 服务虚拟目录配置为需要客户端证书</h3>
<p>此过程使用 Internet 信息服务将 Web 服务的虚拟目录配置为用于 SSL 并需要证书。</p>
<p>此过程假定您在 Web 服务器上安装了有效的证书。有关安装 Web 服务器证书的详细信息，请参阅&#8220;<a id=ctl00_rs1_mainContentContainer_ctl05 onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00|ctl00_rs1_mainContentContainer_ctl05',this);" href="http://msdn.microsoft.com/library/en-us/secmod/html/secmod30.asp"><font color=#0033cc>How To Setup SSL on a Web Server</font></a>&#8221;。 </p>
<p><strong>要将</strong> <strong>Web </strong><strong>服务虚拟目录配置为需要客户端证书，请执行下列操作：</strong> </p>
<ul>
    <li>
    <p>启动 Web 服务宿主计算机上的 Internet 信息服务。 </p>
    <li>
    <p>导航到 <strong>SecureMath</strong> 虚拟目录。 </p>
    <li>
    <p>右键单击 <strong>SecureMath</strong>，然后单击 <strong>Properties</strong>。 </p>
    <li>
    <p>单击 <strong>Directory Security</strong> 选项卡。 </p>
    <li>
    <p>在 <strong>Secure communications</strong> 下，单击 <strong>Edit</strong>。 </p>
    <p>如果 <strong>Edit</strong> 不可用，最可能的原因是尚未安装 Web 服务器证书。 </p>
    <li>
    <p>选择 <strong>Require secure channel (SSL)</strong> 复选框。 </p>
    <li>
    <p>选择 <strong>Require client certificates</strong> 选项。 </p>
    <li>
    <p>单击 <strong>OK</strong>，然后再次单击 <strong>OK</strong>。 </p>
    <li>
    <p>在 <strong>Inheritance Overrides</strong> 对话框中，单击 <strong>Select All</strong>，然后单击 <strong>OK</strong> 关闭 <strong>SecureMath</strong> 属性对话框。 </p>
    </li>
</ul>
<p>此操作将新的安全性设置应用到虚拟目录根下的所有子目录。 </p>
<div><a href="http://msdn.microsoft.com/zh-cn/library/aa302408.aspx#mainSection"><img alt="" src="http://i.msdn.microsoft.com/Aa302408.arrow_px_up(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/aa302408.aspx#mainSection"><font color=#0033cc>返回页首</font></a> <br></div>
<h3 id=EWAA>创建运行服务组件的自定义帐户</h3>
<p>此过程在 Web 服务客户端计算机上创建一个新的用户帐户，您将使用这个帐户运行调用 Web 服务的服务组件。</p>
<p><strong>要创建运行服务组件的自定义帐户，请执行下列操作：</strong> </p>
<ol>
    <li>
    <p>用强密码在客户端计算机上创建一个新的用户帐户。清除 <strong>User must change password at next logon</strong> 复选框，然后选择 <strong>Password never expires</strong> 选项。 </p>
    <li>
    <p>将此帐户添加到 <strong>Administrators</strong> 组。 </p>
    </li>
</ol>
<p>用于加载用户配置文件的帐户必须是本地计算机上的管理员。 </p>
<div><a href="http://msdn.microsoft.com/zh-cn/library/aa302408.aspx#mainSection"><img alt="" src="http://i.msdn.microsoft.com/Aa302408.arrow_px_up(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/aa302408.aspx#mainSection"><font color=#0033cc>返回页首</font></a> <br></div>
<h3 id=ETAA>为自定义帐户申请一个客户端证书</h3>
<p>在此过程中，您将使用新的自定义帐户登录到客户端计算机。然后，将发出要获得证书的申请。此过程假定您使用 Microsoft 证书服务。如果您没有使用 Microsoft 证书服务创建新证书，则在使用自定义帐户登录的同时，向您希望的 CA 发出要获得客户端证书的申请并安装证书。</p>
<p>此过程还假定已将 Microsoft 证书服务配置为自动颁发证书来响应证书申请。还可以将其配置为挂起请求，这要求管理员明确颁发证书。</p>
<p><strong>要检查</strong> <strong>Microsoft </strong><strong>证书服务设置，请执行下列操作：</strong> </p>
<ol>
    <li>
    <p>在 Microsoft 证书服务计算机上，单击 <strong>Administrative</strong> <strong>Tools</strong> 程序组中的 <strong>Certification</strong> <strong>Authority</strong>。 </p>
    <li>
    <p>展开 <strong>Certification</strong> <strong>Authority (Local)</strong>，右键单击证书颁发机构，然后单击 <strong>Properties</strong>。 </p>
    <li>
    <p>单击 <strong>Policy</strong> <strong>Module</strong> 选项卡，然后单击 <strong>Configure</strong>。 </p>
    <li>
    <p>检查默认操作。 </p>
    </li>
</ol>
<p>下列过程假定已选中 <strong>Always issue the certificate</strong>。</p>
<p><strong>要为自定义帐户请求一个客户端证书，请执行下列操作：</strong> </p>
<ol>
    <li>
    <p>注销客户端计算机并使用自定义帐户重新登录。 </p>
    <p>此操作强制为自定义帐户创建用户配置文件。 </p>
    <li>
    <p>为了申请客户端证书，请浏览 CA。例如，如果您的 CA 位于 CAServer 计算机上，则浏览到下列位置。 </p>
    <pre>http://caserver/certsrv
    </pre>
    <li>
    <p>单击 <strong>Request a certificate</strong>，然后单击 <strong>Next</strong>。 </p>
    <li>
    <p>确保选中 <strong>User Certificate</strong>，然后单击 <strong>Next</strong>。 </p>
    <li>
    <p>单击 <strong>Submit</strong>。 </p>
    <p>生成申请并发送到 CA 进行处理。 </p>
    <li>
    <p>在证书颁发且您收到来自 CA 服务器的响应后，单击 <strong>Install this certificate</strong>。 </p>
    <li>
    <p>确保颁发 CA 的证书作为受信任的根证书颁发机构安装在本地计算机上。 </p>
    <p>要确认此操作，请执行下列步骤： </p>
    <ol>
        <li>
        <p>在任务栏上，单击 <strong>Start</strong> 按钮，然后单击 <strong>Run</strong>。 </p>
        <li>
        <p>键入 <strong>mmc</strong>，然后单击 <strong>OK</strong>。 </p>
        <li>
        <p>在 <strong>File</strong> 菜单上，单击 <strong>Add/Remove Snap-in</strong>。 </p>
        <li>
        <p>单击 <strong>Add</strong>。 </p>
        <li>
        <p>单击 <strong>Certificates</strong>，然后单击 <strong>Add</strong>。 </p>
        <li>
        <p>单击 <strong>Computer account</strong>，然后单击 <strong>Next</strong>。 </p>
        <li>
        <p>单击 <strong>Local Computer: </strong>(the computer this console is running on)，然后单击 <strong>Finish</strong>。 </p>
        <li>
        <p>单击 <strong>Close</strong>，然后单击 <strong>OK</strong>。 </p>
        <li>
        <p>在 MMC 管理单元的左侧窗格中，展开 <strong>Certificates </strong>(Local Computer)。 </p>
        <li>
        <p>展开 <strong>Trusted Root Certification Authorities</strong>，然后单击 <strong>Certificates</strong>。 </p>
        <li>
        <p>确认已列出了 CA 证书。 </p>
        <p>如果未列出 CA，则执行下列步骤： </p>
        <li>
        <p>浏览到 http://caserver/certsrv。 </p>
        <li>
        <p>单击 <strong>Retrieve the CA certificate or certificate revocation list</strong>，然后单击 <strong>Next</strong>。 </p>
        <li>
        <p>单击 <strong>Install this CA certification path</strong>。</p>
        </li>
    </ol>
    </li>
</ol>
<div><a href="http://msdn.microsoft.com/zh-cn/library/aa302408.aspx#mainSection"><img alt="" src="http://i.msdn.microsoft.com/Aa302408.arrow_px_up(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/aa302408.aspx#mainSection"><font color=#0033cc>返回页首</font></a> <br></div>
<h3 id=EQAA>使用浏览器测试客户端证书</h3>
<p>在此过程中，您将浏览到 Web 服务，以便确认服务器证书或客户端证书没有问题。</p>
<p><strong>要使用浏览器测试客户端证书，请执行下列操作：</strong> </p>
<ul>
    <li>
    <p>使用 Internet Explorer 并导航到 https://server/SecureMath/Math.asmx。 </p>
    <p>确保您指定了&#8220;https&#8221;，因为此站点配置为需要 SSL。 </p>
    <li>
    <p><strong>Client Authentication</strong> 对话框应显示出来。选择客户端证书，然后单击 <strong>OK</strong>。 </p>
    <li>
    <p>确认在浏览器内成功地显示了 Web 服务测试页。 </p>
    <p>如果看到<strong>图</strong> <strong>2</strong> 所示的对话框，则需要将证书颁发机构的证书安装到如前一过程中所述的 <strong>Trusted Root Certification Authorities</strong> 存储中。 </p>
    <p><img alt=fh13sn02 src="http://i.msdn.microsoft.com/Aa302408.fh13sn02(zh-cn,MSDN.10).gif"> </p>
    <div><strong>图</strong> <strong>2. Security Alert </strong><strong>对话框</strong> </div>
    </li>
</ul>
<div><a href="http://msdn.microsoft.com/zh-cn/library/aa302408.aspx#mainSection"><img alt="" src="http://i.msdn.microsoft.com/Aa302408.arrow_px_up(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/aa302408.aspx#mainSection"><font color=#0033cc>返回页首</font></a> <br></div>
<h3 id=ENAA>将客户端证书导出到文件</h3>
<p>此过程将客户端证书导出到文件。随后，当服务组件在需要将证书传递给 Web 服务时，将检索此文件。</p>
<p><strong>要将客户端证书导出到文件，请执行下列操作：</strong> </p>
<ol>
    <li>
    <p>在 Internet Explorer 中，单击 <strong>Tools</strong> 菜单上的 <strong>Internet Options</strong>。 </p>
    <li>
    <p>单击 <strong>Content</strong> 选项卡。 </p>
    <li>
    <p>单击 <strong>Certificates</strong>。 </p>
    <li>
    <p>单击客户端证书，然后单击 <strong>Export</strong>。 </p>
    <li>
    <p>单击 <strong>Next</strong> 跳过&#8220;证书导出向导&#8221;的欢迎对话框。 </p>
    <li>
    <p>确保选中了 <strong>No, do not export the private key</strong>，然后单击 <strong>Next</strong>。 </p>
    <li>
    <p>确保选中了 <strong>DER encoded binary X.509 (.CER)</strong>，然后单击 <strong>Next</strong>。 </p>
    <p>您必须使用此格式，因为 .NET Framework 不支持 Base-64 或 PKCS #7 格式。 </p>
    <li>
    <p>输入一个导出文件名。注意 .cer 导出文件的位置，因为在后续过程中将再次需要此位置。 </p>
    <li>
    <p>单击 <strong>Next</strong>，然后单击 <strong>Finish</strong> 导出此证书。 </p>
    <li>
    <p>关闭 Internet Explorer。 </p>
    <li>
    <p>注销此计算机并使用常规开发帐户重新登录。 </p>
    </li>
</ol>
<div><a href="http://msdn.microsoft.com/zh-cn/library/aa302408.aspx#mainSection"><img alt="" src="http://i.msdn.microsoft.com/Aa302408.arrow_px_up(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/aa302408.aspx#mainSection"><font color=#0033cc>返回页首</font></a> <br></div>
<h3 id=EKAA>开发用于调用 Web 服务的服务组件</h3>
<p>此过程创建了一个新的 Visual C# 类库应用程序以及用于调用 Web 服务的服务组件。此过程假定您正在使用客户端计算机。</p>
<p><strong>要开发用于调用</strong> <strong>Web </strong><strong>服务的服务组件，请执行下列操作：</strong> </p>
<ol>
    <li>
    <p>启动 Visual Studio.NET 并创建一个新的名为 <strong>WebServiceRequestor</strong> 的 Visual C# 类库项目。 </p>
    <li>
    <p>添加一个对 <strong>SecureMath</strong> Web 服务的 Web 引用。 </p>
    <p><strong>重要事项</strong> 在添加 Web 引用前，必须临时地将 Web 服务的虚拟目录重新配置为不需要客户端证书（尽管仍需要 SSL）。在成功添加 Web 引用后，将虚拟目录配置更改回需要客户端证书。</p>
    <p>实际上，如果站点需要客户端证书，则服务的发行商将 WSDL 作为独立的脱机文件使用，服务的使用者可以使用此文件来创建代理。</p>
    <p>在 <strong>Add Web Reference</strong> 对话框中，确保在指定 Web 服务位置时指定 <strong>https</strong>。如果失败，则会产生错误，因为 Web 服务虚拟目录配置为需要 SSL。 </p>
    <li>
    <p>添加一个到 System.EnterpriseServices 程序集的引用。 </p>
    <li>
    <p>将 class1.cs 重命名为 ProfileManager.cs。 </p>
    <li>
    <p>将下列类定义添加到 ProfileManager.cs（替换主干 class1 类）。<strong>ProfileManager</strong> 类使用 P/Invoke 调用 <strong>LoadUserProfile</strong> 和 <strong>UnloadUserProfile</strong> Win32 API。 </p>
    <pre>internal class ProfileManager
    {
    [DllImport("Userenv.dll", SetLastError=true,
    CharSet=System.Runtime.InteropServices.CharSet.Auto)]
    internal static extern bool LoadUserProfile(IntPtr hToken,
    ref PROFILEINFO lpProfileInfo);
    [DllImport("Userenv.dll", SetLastError=true,
    CharSet=System.Runtime.InteropServices.CharSet.Auto)]
    internal static extern bool  UnloadUserProfile(IntPtr hToken,
    IntPtr hProfile);
    [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi)]
    public struct PROFILEINFO
    {
    public int dwSize;
    public int dwFlags;
    public String lpUserName;
    public String lpProfilePath;
    public String lpDefaultPath;
    public String lpServerName;
    public String lpPolicyPath;
    public IntPtr hProfile;
    }
    }
    </pre>
    <li>
    <p>将另一个名为 MathServiceComponent.cs 的类文件添加到项目中。 </p>
    <li>
    <p>将下列 <strong>using</strong> 语句添加到 MathServiceComponent.cs 的现有 <strong>using</strong> 语句下。 </p>
    <pre>using System.Net;
    using System.Web.Services;
    using System.Security.Principal;
    using System.EnterpriseServices;
    using System.Runtime.InteropServices;
    using System.Security.Cryptography.X509Certificates;
    using WebServiceRequestor.WebReference1;
    </pre>
    <li>
    <p>添加下列类定义，它提供了一个公共 <strong>CallMathWebService</strong> 方法。在稍后的过程中，您将从客户端 ASP.NET Web 应用程序中调用此方法。 </p>
    <p><strong>注</strong> 在下列代码中，用在步骤 3&#8220;<a id=ctl00_rs1_mainContentContainer_ctl06 onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00|ctl00_rs1_mainContentContainer_ctl06',this);" href="http://msdn.microsoft.com/library/en-us/secmod/html/secmod27.asp?frame=true"><font color=#0033cc>Create a Custom Account for Running the Serviced Componen</font></a>&#8221;中创建的自定义帐户的名称替换用于加载用户配置文件的帐户名。</p>
    <pre>// This class calls the web service that requires a certificate.
    public class MathServiceComponent : ServicedComponent
    {
    [DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)]
    private extern static bool DuplicateToken(IntPtr ExistingTokenHandle,
    int SECURITY_IMPERSONATION_LEVEL,
    ref IntPtr DuplicateTokenHandle);
    [DllImport("kernel32.dll", CharSet=CharSet.Auto)]
    private extern static bool CloseHandle(IntPtr handle);
    // Calls the Web service that requires client certificates
    // certFilepath points to the .cer file to use
    // url is the Web service url
    // operand1 and operand2 are the parameters to pass to the Web service
    public long CallMathWebService(String certFilepath,
    String url, int operand1, int operand2)
    {
    bool retVal = false;
    // Need to duplicate the token. LoadUserProfile needs a token with
    // TOKEN_IMPERSONATE and TOKEN_DUPLICATE.
    const int SecurityImpersonation = 2;
    IntPtr dupeTokenHandle = DupeToken(WindowsIdentity.GetCurrent().Token,
    SecurityImpersonation);
    if(IntPtr.Zero == dupeTokenHandle)
    {
    throw new Exception("Unable to duplicate token.");
    }
    // Load the profile.
    ProfileManager.PROFILEINFO profile = new ProfileManager.PROFILEINFO();
    profile.dwSize = 32;
    //TODO: Replace with custom account name created in step 3.
    profile.lpUserName = @"machinename\customaccountname";
    retVal = ProfileManager.LoadUserProfile(dupeTokenHandle, ref profile);
    if(false == retVal)
    {
    throw new Exception("Error loading user profile. " +
    Marshal.GetLastWin32Error());
    }
    // Instantiate the Web service proxy
    math mathservice = new math();
    mathservice.Url = url;
    String certPath = certFilepath;
    mathservice.ClientCertificates.Add(
    X509Certificate.CreateFromCertFile(certPath));
    long lngResult = 0;
    try
    {
    lngResult = mathservice.Add(operand1, operand2);
    }
    catch(Exception ex)
    {
    if(ex is WebException)
    {
    WebException we = ex as WebException;
    WebResponse webResponse = we.Response;
    throw new Exception("Exception calling method. " + ex.Message);
    }
    }
    ProfileManager.UnloadUserProfile(WindowsIdentity.GetCurrent().Token,
    profile.hProfile);
    CloseHandle(dupeTokenHandle);
    return lngResult;
    }
    private IntPtr DupeToken(IntPtr token, int Level)
    {
    IntPtr dupeTokenHandle = new IntPtr(0);
    bool retVal = DuplicateToken(token, Level, ref dupeTokenHandle);
    if (false == retVal)
    {
    return IntPtr.Zero;
    }
    return dupeTokenHandle;
    }
    } // end class
    </pre>
    <li>
    <p>在 <strong>Build</strong> 菜单上，单击 <strong>Build</strong> <strong>Solution</strong>。 </p>
    </li>
</ol>
<div><a href="http://msdn.microsoft.com/zh-cn/library/aa302408.aspx#mainSection"><img alt="" src="http://i.msdn.microsoft.com/Aa302408.arrow_px_up(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/aa302408.aspx#mainSection"><font color=#0033cc>返回页首</font></a> <br></div>
<h3 id=EHAA>配置和安装服务组件</h3>
<p>此过程将配置服务组件，生成强名称，将其安装到全局程序集缓存中并注册到 COM+。 </p>
<ol>
    <li>
    <p>打开 assemblyinfo.cs 并将下列 <strong>using</strong> 语句添加到现有 <strong>using</strong> 语句下。 </p>
    <pre>using System.EnterpriseServices;
    </pre>
    <li>
    <p>将下列程序集级属性添加到 assemblyinfo.cs，将服务组件配置为在 COM+ 服务器应用程序中运行。 </p>
    <pre> [assembly: ApplicationActivation(ActivationOption.Server)]
    </pre>
    <li>
    <p>打开一个命令提示符窗口并更改为当前项目的目录。 </p>
    <li>
    <p>使用 sn.exe 实用工具来生成含有公共-私有密钥对的密钥文件。 </p>
    <pre>sn.exe -k WebServiceRequestor.snk
    </pre>
    <li>
    <p>返回到 Visual Studio .NET。 </p>
    <li>
    <p>定位到 assemblyinfo.cs 内的 [<strong>AssemblyKeyFile</strong>] 属性，并将其修改为引用项目目录内的密钥文件，如下所示。 </p>
    <pre> [assembly: AssemblyKeyFile(@"..\..\WebServiceRequestor.snk")]
    </pre>
    <li>
    <p>在 <strong>Build</strong> 菜单上，单击 <strong>Build</strong> <strong>Solution</strong>。 </p>
    <li>
    <p>返回到命令提示符，然后运行下列命令将程序集添加到全局程序集缓存。 </p>
    <pre>gacutil.exe /i bin\debug\webservicerequestor.dll
    </pre>
    <li>
    <p>运行下列命令来将此程序集注册到 COM+。 </p>
    <pre>regsvcs bin\debug\webservicerequestor.dll
    </pre>
    <li>
    <p>启动 <strong>Component Services</strong>（位于 <strong>Administrative Tools</strong> 程序组下）。 </p>
    <li>
    <p>展开 <strong>Component</strong> <strong>Services</strong>、<strong>Computers</strong> 和 <strong>My</strong> <strong>Computer</strong> 节点。 </p>
    <li>
    <p>展开 <strong>COM+ Applications</strong> 文件夹。 </p>
    <li>
    <p>右键单击 <strong>WebServiceRequestor</strong>，然后单击 <strong>Properties</strong>。 </p>
    <li>
    <p>单击 <strong>Identity</strong> 选项卡。 </p>
    <li>
    <p>选择 <strong>This user:</strong> 选项并输入对应于此前创建的自定义帐户的帐户详细信息。 </p>
    <p>此操作将 COM+ 应用程序配置为使用自定义帐户来运行。 </p>
    <li>
    <p>单击 <strong>OK</strong> 关闭 <strong>Properties</strong> 对话框。 </p>
    <li>
    <p>关闭组件服务。 </p>
    </li>
</ol>
<div><a href="http://msdn.microsoft.com/zh-cn/library/aa302408.aspx#mainSection"><img alt="" src="http://i.msdn.microsoft.com/Aa302408.arrow_px_up(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/aa302408.aspx#mainSection"><font color=#0033cc>返回页首</font></a> <br></div>
<h3 id=EEAA>开发一个 Web 应用程序来调用服务组件</h3>
<p>此过程创建一个简单的 ASP.NET Web 应用程序，可将其用作客户端应用程序来调用 Web 服务（通过服务组件）。</p>
<p><strong>要开发一个</strong> <strong>Web </strong><strong>应用程序来调用服务组件，请执行下列操作：</strong> </p>
<ol>
    <li>
    <p>在 Web 服务客户端计算机上，创建一个新的名为 <strong>SecureMathClient</strong> 的 Visual C# ASP.NET Web 应用程序。 </p>
    <li>
    <p>添加一个对 System.EnterpriseServices 的引用。 </p>
    <li>
    <p>添加一个对 <strong>WebServiceRequestor</strong> 服务组件的引用。 </p>
    <p>浏览到位于 bin\debug 文件夹内 <strong>WebServiceRequestor</strong> 项目目录下的 WebServiceRequestor.dll。 </p>
    <li>
    <p>打开 WebForm1.aspx.cs 并将下列 <strong>using</strong> 语句添加到现有的 <strong>using</strong> 语句下。 </p>
    <pre>using WebServiceRequestor;
    </pre>
    <li>
    <p>在 Designer 模式下查看 WebForm1.aspx 并使用下列 ID 创建一个如<strong>图</strong> <strong>3 </strong>所示的窗体： </p>
    <ul>
        <li>
        <p>operand1 </p>
        <li>
        <p>operand2 </p>
        <li>
        <p>result </p>
        <li>
        <p>add </p>
        </li>
    </ul>
    <p><img alt=fh13sn03 src="http://i.msdn.microsoft.com/Aa302408.fh13sn03(zh-cn,MSDN.10).gif"> </p>
    <div><strong>图</strong> <strong>3. Web </strong><strong>窗体控件排列</strong> </div>
    <li>
    <p>双击 <strong>Add</strong> 创建一个按钮单击事件处理程序。 </p>
    <li>
    <p>将下列代码添加到事件处理程序中： </p>
    <p><strong>注</strong> 将 <strong>certPath</strong> 字符串设置为在步骤 6&#8220;将客户端证书导出到文件&#8221;期间导出的证书文件的位置。</p>
    <p>将带有 HTTPS URL 的 <strong>url</strong> 字符串设置为 Web 服务。<strong> </strong></p>
    <pre>private void add_Click(object sender, System.EventArgs e)
    {
    // TODO: Replace with a valid path to your certificate
    string certPath = @"C:\CustomAccountCert.cer";
    // TODO: Replace with a valid URL to your Web service
    string url = "https://wsserver/SecureMath/math.asmx";
    MathServiceComponent mathComp = new MathServiceComponent();
    long addResult = mathComp.CallMathWebService( certPath,
    url,
    Int32.Parse(operand1.Text),
    Int32.Parse(operand2.Text));
    result.Text = addResult.ToString();
    }
    </pre>
    <li>
    <p>在 <strong>Build</strong> 菜单上，单击 <strong>Build</strong> <strong>Solution</strong>。 </p>
    <li>
    <p>运行此应用程序。输入要进行加法运算的两个数，然后单击 <strong>Add</strong>。 </p>
    <p>Web 应用程序将调用服务组件，此服务组件使用 SSL 并传递客户端证书来调用 Web 服务。 </p>
    </li>
</ol>
<img src ="http://www.cnitblog.com/MartinYao/aggbug/52410.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/MartinYao/" target="_blank">玄铁剑</a> 2008-12-09 21:32 <a href="http://www.cnitblog.com/MartinYao/articles/52410.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>如何建立客户端证书 </title><link>http://www.cnitblog.com/MartinYao/articles/52409.html</link><dc:creator>玄铁剑</dc:creator><author>玄铁剑</author><pubDate>Tue, 09 Dec 2008 13:27:00 GMT</pubDate><guid>http://www.cnitblog.com/MartinYao/articles/52409.html</guid><wfw:comment>http://www.cnitblog.com/MartinYao/comments/52409.html</wfw:comment><comments>http://www.cnitblog.com/MartinYao/articles/52409.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/MartinYao/comments/commentRss/52409.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/MartinYao/services/trackbacks/52409.html</trackback:ping><description><![CDATA[<p>Microsoft Corporation</p>
<p><strong>目标</strong> </p>
<p>本模块用于： </p>
<ul>
    <li>
    <p>获取并安装客户端证书。 </p>
    <li>
    <p>创建一个需要客户端证书的 ASP.NET Web 应用程序。 </p>
    </li>
</ul>
<p><strong>适用于</strong> </p>
<p>本模块适用于下列产品和技术： </p>
<ul>
    <li>
    <p>Microsoft&#174; Windows&#174; XP 或 Windows 2000 Server（带 Service Pack 3）以及更高版本的操作系统。 </p>
    <li>
    <p>Microsoft Internet 信息服务 (IIS) 5.0 </p>
    <li>
    <p>Microsoft 证书服务（如果您需要生成自己的证书的话） </p>
    <li>
    <p>Microsoft .NET Framework 版本 1.0（带 Service Pack 2）以及更高版本 </p>
    <li>
    <p>Microsoft Visual C#&#174; .NET 开发工具 </p>
    </li>
</ul>
<p><strong>本模块的使用方法</strong> </p>
<p>要最大程度地利用本模块： </p>
<ul>
    <li>
    <p>必须有使用 Visual C# .NET 和 Microsoft Visual Studio&#174; .NET 开发系统的经验。 </p>
    <li>
    <p>必须有使用 ASP.NET 开发 Web 应用程序的经验。 </p>
    <li>
    <p>必须有配置 IIS 的经验。 </p>
    <li>
    <p>必须能访问某个证书颁发机构 (CA)，如 Microsoft 证书服务（如果要生成新的客户端证书）。 </p>
    <li>
    <p>如果您不想生成自己的证书，则必须决定要从哪个商业 CA 来申请客户端证书。某些 CA 将对此服务收费。 </p>
    <li>
    <p>必须有一个安装了 SSL 证书的 Web 服务器。有关详细信息，请阅读&#8220;<a id=ctl00_rs1_mainContentContainer_ctl02 onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00|ctl00_rs1_mainContentContainer_ctl02',this);" href="http://msdn.microsoft.com/library/en-us/secmod/html/secmod30.asp"><font color=#0033cc>如何在 Web 服务器上建立 SSL</font></a>&#8221;。 </p>
    <li>
    <p>有关客户端证书与其他形式的身份验证的比较，请阅读模块&#8220;<a id=ctl00_rs1_mainContentContainer_ctl03 onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00|ctl00_rs1_mainContentContainer_ctl03',this);" href="http://msdn.microsoft.com/library/en-us/secmod/html/secmod02.asp"><font color=#0033cc>Security Model for ASP.NET Applications</font></a>&#8221;。 </p>
    </li>
</ul>
<h5>本页内容</h5>
<p><a href="http://msdn.microsoft.com/zh-cn/library/aa302412.aspx#EOAA"><img alt=摘要 src="http://i.msdn.microsoft.com/Aa302412.arrow_px_down(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/aa302412.aspx#EOAA"><font color=#0033cc>摘要</font></a> <br><a href="http://msdn.microsoft.com/zh-cn/library/aa302412.aspx#ELAA"><img alt="创建一个简单的 Web 应用程序" src="http://i.msdn.microsoft.com/Aa302412.arrow_px_down(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/aa302412.aspx#ELAA"><font color=#0033cc>创建一个简单的 Web 应用程序</font></a> <br><a href="http://msdn.microsoft.com/zh-cn/library/aa302412.aspx#EIAA"><img alt="将 Web 应用程序配置为需要客户端证书" src="http://i.msdn.microsoft.com/Aa302412.arrow_px_down(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/aa302412.aspx#EIAA"><font color=#0033cc>将 Web 应用程序配置为需要客户端证书</font></a> <br><a href="http://msdn.microsoft.com/zh-cn/library/aa302412.aspx#EHAA"><img alt=申请并安装客户端证书 src="http://i.msdn.microsoft.com/Aa302412.arrow_px_down(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/aa302412.aspx#EHAA"><font color=#0033cc>申请并安装客户端证书</font></a> <br><a href="http://msdn.microsoft.com/zh-cn/library/aa302412.aspx#EEAA"><img alt=验证客户端证书操作 src="http://i.msdn.microsoft.com/Aa302412.arrow_px_down(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/aa302412.aspx#EEAA"><font color=#0033cc>验证客户端证书操作</font></a> <br><a href="http://msdn.microsoft.com/zh-cn/library/aa302412.aspx#EBAA"><img alt=其他资源 src="http://i.msdn.microsoft.com/Aa302412.arrow_px_down(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/aa302412.aspx#EBAA"><font color=#0033cc>其他资源</font></a> <br></p>
<h3 id=EOAA>摘要</h3>
<p>客户端证书为 Web 应用程序提供了一种出色的身份验证机制。浏览器或其他客户端应用程序必须提供有效的证书才能被授予对应用程序的访问权，从而使客户端无需再提供用户名和密码。这就使得在创建由其他客户端应用程序访问的安全的 Web 服务时，客户端证书非常有用。</p>
<p>本模块描述如何获取和安装客户端证书，并说明如何创建需要客户端证书的 Web 应用程序。</p>
<div><a href="http://msdn.microsoft.com/zh-cn/library/aa302412.aspx#mainSection"><img alt="" src="http://i.msdn.microsoft.com/Aa302412.arrow_px_up(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/aa302412.aspx#mainSection"><font color=#0033cc>返回页首</font></a> <br></div>
<h3 id=ELAA>创建一个简单的 Web 应用程序</h3>
<p>在此过程中，您将创建一个简单的用于测试目的的 Web 应用程序。</p>
<p><strong>要创建一个简单的</strong> <strong>Web </strong><strong>应用程序，请执行下列操作：</strong> </p>
<ol>
    <li>
    <p>启动 Visual Studio .NET 并创建一个新的名为 <strong>SecureApp</strong> 的 Visual C# ASP.NET Web 应用程序。 </p>
    <li>
    <p>将标签控件从工具箱拖到 WebForm1.aspx Web 窗体，然后将其 ID 属性设置为 <strong>message</strong>。 </p>
    <li>
    <p>再将一个标签拖到 WebForm1.aspx，并将其 ID 属性设置为 <strong>certData</strong>。 </p>
    <li>
    <p>将下列代码添加到 <strong>Page_Load</strong> 事件过程中。 </p>
    <pre>string username;
    username = User.Identity.Name;
    message.Text = "Welcome " + username;
    HttpClientCertificate cert = Request.ClientCertificate;
    if (cert.IsPresent)
    {
    certData.Text = "Client certificate retrieved";
    }
    else
    {
    certData.Text = "No client certificate";
    }
    </pre>
    <li>
    <p>在 <strong>Build</strong> 菜单上，单击 <strong>Build</strong> <strong>Solution</strong>。 </p>
    <li>
    <p>启动 Internet Explorer 并导航到 http://localhost/SecureApp/WebForm1.aspx。 </p>
    <p>应显示带有消息&#8220;Welcome&#8221;（不显示用户名，因为用户尚未进行身份验证）和&#8220;No client certificate&#8221;的页。</p>
    <li>
    <p>关闭 Internet Explorer。<strong> </strong></p>
    </li>
</ol>
<div><a href="http://msdn.microsoft.com/zh-cn/library/aa302412.aspx#mainSection"><img alt="" src="http://i.msdn.microsoft.com/Aa302412.arrow_px_up(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/aa302412.aspx#mainSection"><font color=#0033cc>返回页首</font></a> <br></div>
<h3 id=EIAA>将 Web 应用程序配置为需要客户端证书</h3>
<p>此过程使用 Internet 信息服务 (IIS) 将 Web 应用程序的虚拟目录配置为需要证书。</p>
<p>此过程假定您在 Web 服务器上安装了有效的证书。有关安装 Web 服务器证书的详细信息，请参阅&#8220;<a id=ctl00_rs1_mainContentContainer_ctl04 onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00|ctl00_rs1_mainContentContainer_ctl04',this);" href="http://msdn.microsoft.com/library/en-us/secmod/html/secmod30.asp"><font color=#0033cc>如何在 Web 服务器上建立 SSL</font></a>&#8221;。</p>
<p><strong>要将</strong> <strong>Web </strong><strong>应用程序的虚拟目录配置为需要证书，请执行下列操作：</strong> </p>
<ol>
    <li>
    <p>在 Web 服务主机上，启动 IIS。 </p>
    <li>
    <p>导航到 <strong>SecureApp</strong> 虚拟目录。 </p>
    <li>
    <p>右击 <strong>SecureApp</strong>，然后单击 <strong>Properties</strong>。 </p>
    <li>
    <p>单击 <strong>Directory Security</strong> 选项卡。 </p>
    <li>
    <p>在 <strong>Secure communications</strong> 下，单击 <strong>Edit</strong>。 </p>
    <p>如果 <strong>Edit</strong> 不可用，最可能的原因是未安装 Web 服务器证书。 </p>
    <li>
    <p>选择 <strong>Require secure channel (SSL)</strong> 复选框。 </p>
    <li>
    <p>选择 <strong>Require client certificates</strong> 选项。 </p>
    <li>
    <p>单击 <strong>OK</strong>，然后再次单击 <strong>OK</strong>。 </p>
    <li>
    <p>在 <strong>Inheritance Overrides</strong> 对话框中，单击 <strong>Select All</strong>，然后单击 <strong>OK</strong> 关闭 <strong>SecureApp</strong> 属性对话框。 </p>
    <p>此操作将新安全性设置应用到虚拟目录根下的所有子目录。 </p>
    <li>
    <p>要确认已正确配置了 Web 站点，请启动 Internet Explorer 并浏览（使用 HTTPS）到 https://localhost/SecureApp/webform1.aspx。 </p>
    <li>
    <p>Internet Explorer 显示出一个 <strong>Client Authentication</strong> 对话框，要求您选择一个客户端证书。由于您尚未安装客户端证书，因此请单击 <strong>OK</strong>，并确认显示出一个通知您此页需要客户端证书的错误页。 </p>
    <li>
    <p>关闭 Internet Explorer。 </p>
    </li>
</ol>
<div><a href="http://msdn.microsoft.com/zh-cn/library/aa302412.aspx#mainSection"><img alt="" src="http://i.msdn.microsoft.com/Aa302412.arrow_px_up(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/aa302412.aspx#mainSection"><font color=#0033cc>返回页首</font></a> <br></div>
<h3 id=EHAA>申请并安装客户端证书</h3>
<p><strong>此过程将安装客户端证书。您可以使用任何证书颁发机构的证书，也可以使用</strong> Microsoft 证书服务生成自己的证书，如以下几部分所述。</p>
<p>此过程假定 Microsoft 证书服务已针对挂起申请进行了配置，它需要管理员显式颁发证书。它还可以配置为自动颁发证书来响应证书申请。</p>
<p><strong>要检查证书申请状态设置，请执行下列操作：</strong> </p>
<ol>
    <li>
    <p>在 Microsoft 证书服务计算机上，从 <strong>Administrative</strong> <strong>Tools</strong> 程序组中选择 <strong>Certification</strong> <strong>Authority</strong>。 </p>
    <li>
    <p>展开 <strong>Certification</strong> <strong>Authority</strong> (Local)，右击证书颁发机构并单击 <strong>Properties</strong>。 </p>
    <li>
    <p>单击 <strong>Policy</strong> <strong>Module</strong> 选项卡，然后单击 <strong>Configure</strong>。 </p>
    <li>
    <p>检查默认操作。 </p>
    <p>下面的过程假定已选中 <strong>Set the certificate request status to pending. Administrator must explicitly issue the certificate</strong>。 </p>
    </li>
</ol>
<p><strong>要申请客户端证书，请执行下列操作：</strong> </p>
<ol>
    <li>
    <p>启动 Internet Explorer 并导航到 http://<em> hostname</em>/certsrv，其中 <em>hostname</em> 是安装 Microsoft 证书服务的计算机的名称。 </p>
    <li>
    <p>单击 <strong>Request a certificate</strong>，然后单击 <strong>Next</strong>。 </p>
    <li>
    <p>在 <strong>Choose Request Type</strong> 页上，单击 <strong>User Certificate</strong>，然后单击 <strong>Next</strong>。 </p>
    <li>
    <p>单击 <strong>Submit</strong> 来完成申请。 </p>
    <li>
    <p>关闭 Internet Explorer。 </p>
    </li>
</ol>
<p><strong>要颁发客户端证书，请执行下列操作：</strong> </p>
<ol>
    <li>
    <p>从 <strong>Administrative Tools</strong> 程序组中，启动 <strong>Certification</strong> <strong>Authority</strong> 工具。 </p>
    <li>
    <p>展开证书颁发机构，然后选择 <strong>Pending</strong> <strong>Requests</strong> 文件夹。 </p>
    <li>
    <p>选择刚提交的证书申请，指向 <strong>Action</strong> 菜单上的 <strong>All Tasks</strong>，然后单击 <strong>Issue</strong>。 </p>
    <li>
    <p>确认证书在 <strong>Issued</strong> <strong>Certificates</strong> 文件夹中显示出来，然后双击它以查看其内容。 </p>
    <li>
    <p>在 <strong>Details</strong> 选项卡上，单击 <strong>Copy to File</strong> 将此证书保存为 Base-64 编码的 X.509 证书。 </p>
    <li>
    <p>关闭此证书的属性窗口。 </p>
    <li>
    <p>关闭&#8220;证书颁发机构&#8221;工具。 </p>
    </li>
</ol>
<p><strong>要安装客户端证书，请执行下列操作：</strong> </p>
<ol>
    <li>
    <p>要查看证书，请启动 Windows Explorer，导航到上一过程中保存的 .cer 文件，然后双击此文件。 </p>
    <li>
    <p>单击 <strong>Install</strong> <strong>Certificate</strong>，然后单击 <strong>Certificate</strong> <strong>Import</strong> <strong>Wizard</strong> 的第一页上的 <strong>Next</strong>。 </p>
    <li>
    <p>选择 <strong>Automatically select the certificate store based on the type of certificate</strong>，然后单击 <strong>Next</strong>。 </p>
    <li>
    <p>单击 <strong>Finish</strong> 完成此向导。取消确认消息框，然后单击 <strong>OK</strong> 关闭证书。</p>
    </li>
</ol>
<div><a href="http://msdn.microsoft.com/zh-cn/library/aa302412.aspx#mainSection"><img alt="" src="http://i.msdn.microsoft.com/Aa302412.arrow_px_up(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/aa302412.aspx#mainSection"><font color=#0033cc>返回页首</font></a> <br></div>
<h3 id=EEAA>验证客户端证书操作</h3>
<p>本过程验证您是否可以使用客户端证书来访问 <strong>SecureApp</strong> 应用程序。</p>
<p><strong>要验证客户端证书操作，请执行下列操作：</strong> </p>
<ul>
    <li>
    <p>启动 Internet Explorer 并导航到 https://localhost/SecureApp/webform1.aspx。 </p>
    <li>
    <p>确认 Web 页是否成功显示出来。 </p>
    </li>
</ul>
<img src ="http://www.cnitblog.com/MartinYao/aggbug/52409.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/MartinYao/" target="_blank">玄铁剑</a> 2008-12-09 21:27 <a href="http://www.cnitblog.com/MartinYao/articles/52409.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>WEB威胁建模 </title><link>http://www.cnitblog.com/MartinYao/articles/52407.html</link><dc:creator>玄铁剑</dc:creator><author>玄铁剑</author><pubDate>Tue, 09 Dec 2008 13:12:00 GMT</pubDate><guid>http://www.cnitblog.com/MartinYao/articles/52407.html</guid><wfw:comment>http://www.cnitblog.com/MartinYao/comments/52407.html</wfw:comment><comments>http://www.cnitblog.com/MartinYao/articles/52407.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/MartinYao/comments/commentRss/52407.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/MartinYao/services/trackbacks/52407.html</trackback:ping><description><![CDATA[<p><strong>摘要：</strong>本文简要介绍主要的输入、输出以及为 Web 应用程序创建威胁模型的步骤。</p>
<h5>本页内容</h5>
<p><a href="http://msdn.microsoft.com/zh-cn/library/ms978523.aspx#XSLTsection130121120120"><img alt=活动概述 src="http://i.msdn.microsoft.com/ms978523.arrow_px_down(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/ms978523.aspx#XSLTsection130121120120"><font color=#0033cc>活动概述</font></a> <br><a href="http://msdn.microsoft.com/zh-cn/library/ms978523.aspx#XSLTsection133121120120"><img alt=活动摘要表 src="http://i.msdn.microsoft.com/ms978523.arrow_px_down(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/ms978523.aspx#XSLTsection133121120120"><font color=#0033cc>活动摘要表</font></a> <br></p>
<p><strong>活动：</strong>Web 应用程序的威胁建模</p>
<p><strong>目的：</strong>确定方案中的相关威胁和漏洞，以帮助您构建应用程序的安全设计。</p>
<p><strong>输入：</strong> </p>
<ul>
    <li>
    <p>主要用例和使用方案 </p>
    <li>
    <p>数据流 </p>
    <li>
    <p>数据架构 </p>
    <li>
    <p>部署关系图 </p>
    </li>
</ul>
<p><strong>输出：</strong> </p>
<ul>
    <li>
    <p>威胁列表 </p>
    <li>
    <p>漏洞列表 </p>
    </li>
</ul>
<h3 id=XSLTsection130121120120>活动概述</h3>
<p>威胁建模的五个主要步骤如图 1 所示。应该通过重复执行步骤 2 至步骤 5 来逐步细化威胁模型。随着应用程序开发周期的向前推进，您会发现有关应用程序设计的更多内容，并能够添加更多细节。 </p>
<p><img alt="" src="http://i.msdn.microsoft.com/ms978523.f01tmwa01(zh-cn,MSDN.10).gif"> </p>
<div><strong>图</strong> <strong>1. </strong><strong>重复执行的威胁建模过程</strong> </div>
<p>威胁建模的五个步骤是： </p>
<ul>
    <li>
    <p><strong>步骤</strong> <strong>1</strong> <strong>：确定安全目标。</strong>目标清晰有助于您将注意力集中在威胁建模活动上，以及确定后续步骤要做多少工作。 </p>
    <li>
    <p><strong>步骤</strong> <strong>2</strong> <strong>：创建应用程序概述。</strong>逐条列出应用程序的重要特征和参与者有助于在步骤 4 中确定相关威胁。 </p>
    <li>
    <p><strong>步骤</strong> <strong>3</strong> <strong>：分解应用程序。</strong>全面了解应用程序的结构可以使您更轻松地发现更相关、更具体的威胁。 </p>
    <li>
    <p><strong>步骤</strong> <strong>4</strong> <strong>：确定威胁。</strong>使用步骤 2 和 3 中的详细信息来确定与您的应用程序方案和上下文相关的威胁。 </p>
    <li>
    <p><strong>步骤</strong> <strong>5</strong> <strong>：确定漏洞。</strong>检查应用程序的各层以确定与威胁有关的弱点。使用漏洞类别来帮助您关注最常出现错误的区域。 </p>
    </li>
</ul>
<div><a href="http://msdn.microsoft.com/zh-cn/library/ms978523.aspx#mainSection"><img alt="" src="http://i.msdn.microsoft.com/ms978523.arrow_px_up(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/ms978523.aspx#mainSection"><font color=#0033cc>返回页首</font></a> <br></div>
<h3 id=XSLTsection133121120120>活动摘要表</h3>
<p>表 1 归纳威胁建模活动，并展示每个步骤的输入和输出。</p>
<table>
    <strong>表 1：活动摘要及输入和输出</strong>
    <tbody>
        <tr>
            <th>
            <p>输入</p>
            </th>
            <th>
            <p>步骤</p>
            </th>
            <th>
            <p>输出</p>
            </th>
        </tr>
        <tr>
            <td>
            <ul>
                <li>
                <p>业务需求</p>
                <li>
                <p>安全策略</p>
                <li>
                <p>兼容性要求</p>
                </li>
            </ul>
            </td>
            <td>
            <p><strong>步骤</strong> <strong>1</strong> <strong>：确定安全目标</strong> </p>
            </td>
            <td>
            <ul>
                <li>
                <p>主要安全目标</p>
                </li>
            </ul>
            </td>
        </tr>
        <tr>
            <td>
            <ul>
                <li>
                <p>部署关系图</p>
                <li>
                <p>用例</p>
                <li>
                <p>功能说明</p>
                </li>
            </ul>
            </td>
            <td>
            <p><strong>步骤</strong> <strong>2</strong> <strong>：创建应用程序概述</strong> </p>
            </td>
            <td>
            <ul>
                <li>
                <p>带有端对端部署方案的白板样式关系图</p>
                <li>
                <p>主要方案</p>
                <li>
                <p>角色</p>
                <li>
                <p>技术</p>
                <li>
                <p>应用程序安全机制</p>
                </li>
            </ul>
            </td>
        </tr>
        <tr>
            <td>
            <ul>
                <li>
                <p>部署关系图</p>
                <li>
                <p>用例</p>
                <li>
                <p>功能说明</p>
                <li>
                <p>数据流关系图</p>
                </li>
            </ul>
            </td>
            <td>
            <p><strong>步骤</strong> <strong>3</strong> <strong>：分解应用程序</strong> </p>
            </td>
            <td>
            <ul>
                <li>
                <p>信任边界</p>
                <li>
                <p>入口点</p>
                <li>
                <p>出口点</p>
                <li>
                <p>数据流</p>
                </li>
            </ul>
            </td>
        </tr>
        <tr>
            <td>
            <ul>
                <li>
                <p>常见威胁</p>
                </li>
            </ul>
            </td>
            <td>
            <p><strong>步骤</strong> <strong>4</strong> <strong>：确定威胁</strong> </p>
            </td>
            <td>
            <ul>
                <li>
                <p>威胁列表</p>
                </li>
            </ul>
            </td>
        </tr>
        <tr>
            <td>
            <p>常见漏洞</p>
            </td>
            <td>
            <p><strong>步骤</strong> <strong>5</strong> <strong>：确定漏洞</strong> </p>
            </td>
            <td>
            <ul>
                <li>
                <p>漏洞列表</p>
                </li>
            </ul>
            </td>
        </tr>
    </tbody>
</table>
<img src ="http://www.cnitblog.com/MartinYao/aggbug/52407.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/MartinYao/" target="_blank">玄铁剑</a> 2008-12-09 21:12 <a href="http://www.cnitblog.com/MartinYao/articles/52407.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>SOA（面向服务架构）: 实现</title><link>http://www.cnitblog.com/MartinYao/articles/52406.html</link><dc:creator>玄铁剑</dc:creator><author>玄铁剑</author><pubDate>Tue, 09 Dec 2008 13:06:00 GMT</pubDate><guid>http://www.cnitblog.com/MartinYao/articles/52406.html</guid><wfw:comment>http://www.cnitblog.com/MartinYao/comments/52406.html</wfw:comment><comments>http://www.cnitblog.com/MartinYao/articles/52406.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/MartinYao/comments/commentRss/52406.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/MartinYao/services/trackbacks/52406.html</trackback:ping><description><![CDATA[<p>Principal, EDS</p>
<p>本文讨论了企业实施SOA时遇到的八个重要挑战的不同的解决方法，同时根据EDS与客户的经验给出了一些例子。</p>
<h5>本页内容</h5>
<p><a href="http://msdn.microsoft.com/zh-cn/library/aa480029.aspx#EGAA"><img alt=介绍 src="http://i.msdn.microsoft.com/Aa480029.arrow_px_down(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/aa480029.aspx#EGAA"><font color=#0033cc>介绍</font></a> <br><a href="http://msdn.microsoft.com/zh-cn/library/aa480029.aspx#EFAA"><img alt=架构组件 src="http://i.msdn.microsoft.com/Aa480029.arrow_px_down(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/aa480029.aspx#EFAA"><font color=#0033cc>架构组件</font></a> <br><a href="http://msdn.microsoft.com/zh-cn/library/aa480029.aspx#EEAA"><img alt=挑战 src="http://i.msdn.microsoft.com/Aa480029.arrow_px_down(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/aa480029.aspx#EEAA"><font color=#0033cc>挑战</font></a> <br><a href="http://msdn.microsoft.com/zh-cn/library/aa480029.aspx#EDAA"><img alt=结论 src="http://i.msdn.microsoft.com/Aa480029.arrow_px_down(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/aa480029.aspx#EDAA"><font color=#0033cc>结论</font></a> <br></p>
<h3 id=EGAA>介绍</h3>
<p>你可能考虑过在你的企业中实施SOA。在这个实施过程中，会遇到复杂的挑战—包括那些仅对你的公司和产业存在的挑战。然而，通过一种灵活的路线图去控制实施SOA，你可以在遇到这些挑战时很快的面对并解决它们。</p>
<p>SOA是一个重要的新的架构范例，它支持中间层解决方案的模块化实现。尤其适用于当多个根据不同技术开发的应用软件在不同的平台上运行时，平台间相互交互的情况。</p>
<p>然而，SOA并不是一夜醒来就可以顺利实施的。企业首先必须朝着构建先进的组件和服务的方向努力。一个路线图和企业特有标准是必备的先决条件—以保证这个架构在企业系统化的实施。</p>
<p>这篇文章针对企业实施SOA将遇到的各种挑战提供了不同的解决方法。基于EDS与客户的实践给出了一些例子。同时，还在构造一个工具时根据EDS的经验进行调节，这个工具为企业网络服务的配置、管理和部署提供了便利。</p>
<div><a href="http://msdn.microsoft.com/zh-cn/library/aa480029.aspx#mainSection"><img alt="" src="http://i.msdn.microsoft.com/Aa480029.arrow_px_up(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/aa480029.aspx#mainSection"><font color=#0033cc>返回页首</font></a> <br></div>
<h3 id=EFAA>架构组件</h3>
<p>图1显示了一个SOA架构的基本组件。这些组件包括：</p>
<ul>
    <li>
    <p><strong>服务提供者</strong>. 一个服务提供者是一个组件或组件集，在无状态方式下完成商业功能，接受预定义的输入和输出。</p>
    <li>
    <p><strong>服务消费者</strong>. 一个服务消费者是一个组件集，有兴趣去使用一项或多项由服务提供者提供的服务。</p>
    <li>
    <p><strong>服务仓库</strong>. 一个服务仓库包括对服务的描述。服务提供者在服务仓库中记录他们提供的服务，而服务消费者接受服务仓库来查找提供的服务有哪些。</p>
    </li>
</ul>
<p><img alt=SOA01 src="http://i.msdn.microsoft.com/Aa480029.SOA01(zh-cn,MSDN.10).gif"> </p>
<p><strong>图</strong> <strong>1. </strong><strong>SOA架构组件</strong> </p>
<div><a href="http://msdn.microsoft.com/zh-cn/library/aa480029.aspx#mainSection"><img alt="" src="http://i.msdn.microsoft.com/Aa480029.arrow_px_up(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/aa480029.aspx#mainSection"><font color=#0033cc>返回页首</font></a> <br></div>
<h3 id=EEAA>挑战</h3>
<p>在企业实施SOA时，需要面对的关键挑战有八个。这些挑战在一个典型的工程部署计划中排列如下：</p>
<ol>
    <li>
    <p><strong>服务识别</strong>.服务是什么？一个指定的服务能够提供什么样的商业功能？如何提供最佳粒度的服务？</p>
    <li>
    <p><strong>服务场所</strong>. 在企业内，哪里是服务场所的最佳位置？</p>
    <li>
    <p><strong>服务范围定义</strong>. 这些服务如何集合进逻辑领域？</p>
    <li>
    <p><strong>服务打包</strong>. 现存的原有主机系统提供的功能如何工程化包装为可再度使用的服务？</p>
    <li>
    <p><strong>服务协调</strong>. 这些复杂的服务如何协调？</p>
    <li>
    <p><strong>服务发送</strong>. 服务消费者提出的要求如何发送到适当的服务或服务领域？</p>
    <li>
    <p><strong>服务管理</strong>. 企业如何按步骤在管理和维持服务？</p>
    <li>
    <p><strong>服务通信标准采用</strong>. 企业如何长期采取一个特定的标准？</p>
    </li>
</ol>
<p><strong>服务识别</strong> </p>
<p><em>挑战</em> </p>
<p>构建SOA时，正确的识别服务和确定相应的服务提供者是一个关键的首要步骤。现今世界，在企业内部，类似的商业功能可以由多个系统提供。</p>
<p><em>方法</em> </p>
<p>解决这个挑战有两个方法；服务合理化与服务合并。服务合理化是对所有提供特定商业功能的系统和应用平台进行一个仔细的分析。通过服务合理化，由少量访问系统实现的商业功能可以在海量访问系统平台。在流线型系统上使用这种方法，我们可以传送更多可靠的服务。</p>
<p><img alt=SOA02 src="http://i.msdn.microsoft.com/Aa480029.SOA02(zh-cn,MSDN.10).gif"> </p>
<p><strong>图2</strong> <strong>. </strong><strong>服务合理化账号配置</strong> </p>
<p>图2提供了一个服务合理化的例子。大量的前台应用，比如在线银行、CRM与VRU，需要账号配置商业功能提供的相关信息。客户与账号库的信息记录系统应该支持账号配置商业功能。根据前台应用的特性调用这个功能，可以返回账号配置的各个子集。</p>
<p>在这个例子中，企业为客户增加在线与VRU访问的同时，减少了需要人类交互的CRM的使用。</p>
<p><em>服务合并</em> </p>
<p>服务合并主要将所有各种服务合并成一种服务方式，这种方式支持多个个体服务表现出的用户界面的扩展。这种合并后重定义的服务可以被各个独立的应用系统稳定的提供。</p>
<p><img alt=SOA03 src="http://i.msdn.microsoft.com/Aa480029.SOA03(zh-cn,MSDN.10).gif"> </p>
<p><strong>图3</strong> <strong>. </strong><strong>服务合并产品目录</strong> </p>
<p>图3说明了一个产品目录库如何被三个独立的服务访问。三个服务被用来寻找一个产品相关信息的预定义子集。在服务合并后，只有一个服务来提供全部产品目录。这个服务包括了各个独立服务在服务合并中提供的信息。服务消费者选择产品目录中感兴趣的部分。服务合并就这样成为一种高效率的方式帮助大量流线型服务支持同样的商业功能。</p>
<p><strong>服务场所</strong> </p>
<p><em>挑战</em>服务经常在一个特定记录系统上的特定商业实体集上执行。这个记录系统是服务执行的一个理想场所。然而，分布式架构解决方案将导致商业数据通过多个应用平台扩散，而同样商业实体将产生大量由记录系统产生的记录。两个系统间的数据同步就成为一个关键需求。这种情况下，哪里才是最佳服务场所呢？</p>
<p><em>方法</em> </p>
<p>这里有三种方法来解决这个挑战；基于内容方法，基于服务库方法，与后端复制方法。</p>
<p><em>基于内容方法</em> </p>
<p>这个方法将收到的服务请求发送到正确的记录系统。此解决方案支持服务消费者眼中服务场所透明化：算法确定服务消费者并不知道一个特定服务是在哪里提供的。给出一个请求时，记录系统支持服务被打包为一个逻辑体。</p>
<p><img alt=SOA04 src="http://i.msdn.microsoft.com/Aa480029.SOA04(zh-cn,MSDN.10).gif"> </p>
<p><strong>图4</strong> <strong>. </strong><strong>基于内容方法</strong> </p>
<p>图4说明了一个基于内容方法的例子。在这个例子中，消费者的相关信息被按区域隔离开来。属于特定区域的消费者信息被储存在数据中心特定区域的库中。然而，任意区域的服务消费者都可以访问这些信息。接收到服务请求后，消费者配置服务执行一个商业规则来确定对消费者有用的信息在哪个特定区域中存储。然后，消费者配置服务再将这个请求发送到找到的区域。</p>
<p><em>基于服务库方法</em> </p>
<p>上面描述了一个基于内容方法的变化，基于服务库方法显示在图5中。消费者配置服务执行与基于内容方法同样的商业规则时，协调服务配置的信息来指引服务请求到正确的区域。这种方法使得必要时改变服务请求发送逻辑更容易。通过改变服务配置信息，服务请求可以被很容易的发送到不同的区域，不用再改变消费者配置服务自身的商业规则。</p>
<p><img alt=SOA05 src="http://i.msdn.microsoft.com/Aa480029.SOA05(zh-cn,MSDN.10).gif"> </p>
<p><strong>图5</strong> <strong>. </strong><strong>基于服务库方法</strong> </p>
<p><em>后端复制方法</em> </p>
<p>这个方法协调内在的交互应用连接能力去访问包括指定信息的物理库。这样，各个记录系统可以像逻辑接口一样去访问分布在系统上的信息。这个服务可以在每个记录系统上执行。这些被操作的数据的物理位置对服务本身是透明的。图6说明了一个后段复制方法的情况。同样的消费者配置服务在靠近服务的记录系统被执行。当其他区域库的信息被请求时，存在于数据库的技术提供的的内在数据复制能力可以支持获取相关数据。</p>
<p><img alt=SOA06 src="http://i.msdn.microsoft.com/Aa480029.SOA06(zh-cn,MSDN.10).gif"> </p>
<p><strong>图6</strong> <strong>. </strong><strong>后端复制方法</strong> </p>
<p><strong>服务范围定义</strong> </p>
<p><em>挑战</em> </p>
<p>将服务按照逻辑区域分类减少了组件的数量，从而简化了结构。因为一些结构上的原因，这些组可以被协调：负载平衡，存取控制，代理模拟，与纵向或横向的商业逻辑区分。然而，对于企业内的商业部门与技术中心来说，要在定义一个正确的服务范围上取得一致，经常有一系列的挑战。什么才是一个好的服务范围逻辑组呢？</p>
<p><em>方法</em> </p>
<p>我们可以选择多个方法来定义服务范围。表格1显示了一个跨商业部门的应用和平台分类例子。这个例子将用来说明在这部分中讨论的每种方法的重要特征。</p>
<table>
    <tbody>
        <tr>
            <th>
            <p>商业部门</p>
            </th>
            <th>
            <p>主要中间层平台</p>
            </th>
            <th>
            <p>应用</p>
            </th>
        </tr>
        <tr>
            <td>
            <p>家庭贷款</p>
            </td>
            <td>
            <p>UNIX</p>
            </td>
            <td>
            <p>SAP</p>
            </td>
        </tr>
        <tr>
            <td>
            <p>在线银行</p>
            </td>
            <td>
            <p>Windows</p>
            </td>
            <td>
            <p>Siebel</p>
            </td>
        </tr>
        <tr>
            <td>
            <p>金融中心</p>
            </td>
            <td>
            <p>UNIX</p>
            </td>
            <td>
            <p>PeopleSoft</p>
            </td>
        </tr>
        <tr>
            <td>
            <p>保险服务</p>
            </td>
            <td>
            <p>Windows</p>
            </td>
            <td>
            <p>SAP</p>
            </td>
        </tr>
        <tr>
            <td>
            <p>个人贷款</p>
            </td>
            <td>
            <p>Linux</p>
            </td>
            <td>
            <p>Oracle</p>
            </td>
        </tr>
        <tr>
            <td>
            <p>企业贷款</p>
            </td>
            <td>
            <p>UNIX</p>
            </td>
            <td>
            <p>IBM DB2</p>
            </td>
        </tr>
    </tbody>
</table>
<table>
    <tbody>
    </tbody>
</table>
<p><em>功能范围</em> </p>
<p>功能范围以一个特定服务集合的商业功能为基础。最好安排企业内的商业过程所有者来定义和隔离商业功能以及对应的服务范围。通过这样分组，某一特定范围的商业过程所有者可以自己控制此范围内的服务。只要商业过程所有者确信在他们各自范围内的服务也被提供给了其他企业，他们就完全控制了对这个服务的架构和应用。</p>
<p>在上面的例子中，有三个功能服务范围：贷款，金融和保险。在这些领域内的服务必须跨越多个平台与后端应用来处理属于他们领域的请求。然而，这些商业处理在一个特定领域内使类似的，不管执行服务的是那些应用或平台。</p>
<ol>
    <li>
    <p><strong>贷款</strong>. 贷款范围覆盖服务显然包含在放出和管理贷款给消费者和企业用户中。这个服务领域包括住房抵押贷款与购买其他价值等同于住房的商品的贷款。服务可能包括借贷、分期还款与利息计算。</p>
    <li>
    <p><strong>金融</strong>. 金融范围覆盖服务显然通过大量媒介与金融有联系，这些媒介包括网络，ATM，VRU和金融中心。服务可能包括开设一个账户，查询帐户余额与账户间的转帐。</p>
    <li>
    <p><strong>保险.</strong> 保险范围覆盖服务仅包括对保险产业的服务。服务可能包括保险费计算、医疗历史查找与投诉处理。</p>
    </li>
</ol>
<p><strong><em>基于技术范围</em> </strong></p>
<p>跨越多个技术平台的功能服务范围使得保持与每一个技术平台同步的内在挑战时时存在。出售方努力用一种方式揭示工业标准来支持他们的解决方案并使得企业依赖于他们的架构，硬件和软件。基于技术的服务范围规范允许特定技术能力的高效使用。</p>
<p>在这个例子中，服务范围可以按照UNIX，Linux与Windows平台分类。基础服务比如错误记录，事务监控与时间处理是这类服务很好的处理对象。他们依赖于运行平台，但独立于驱动这个功能服务范围的商业过程。</p>
<p><em>基于应用范围</em> </p>
<p>作为一种帮助企业面对更新现有系统需求的方法，企业应用集成的概念应运而生。现在，企业拥有大量前台应用系统，因此需要集成这些类似的系统，通过不同的方式处理、打包并提出同样的数据。</p>
<p>基于应用服务范围允许一个特定系统提供分组服务。这种方法使得管理和维持服务更加容易，因为一个领域内的系统对所有的服务都是一样的。</p>
<p>在上面的例子中，SAP，Siebel，PeopleSoft，IBM DB2与Oracle都是很好的基于应用范围的客户。这些范围中的一些典型服务列在下面。</p>
<p><strong>SAP</strong> </p>
<ul>
    <li>
    <p>账户支付—帐目核对</p>
    <li>
    <p>财政计算</p>
    </li>
</ul>
<p><strong>PeopleSoft</strong> </p>
<ul>
    <li>
    <p>增加雇员</p>
    <li>
    <p>更新补偿</p>
    </li>
</ul>
<p><strong>Oracle</strong> </p>
<ul>
    <li>
    <p>数据复制</p>
    <li>
    <p>基于账户信息的角色说明</p>
    </li>
</ul>
<p><strong>服务打包</strong> </p>
<p><em>挑战</em> </p>
<p>在SOA中，一个企业系统必须功能以服务为主。方便集成的构造系统可以很容易的做到这一点。面向结构的系统有一些困难。是一些集成电路应用构造了这些系统，系统包括了所有的商业规则与过程逻辑。这些信息被分配给多个多个连接程序的集合。</p>
<p>一个SOA支持独立的服务—没有任何其它服务的相关知识。结构化程序与特定上下文紧密联系在一起。这些结构化程序如何被再次打包围独立的服务？</p>
<p><em>方法</em> </p>
<p>我们可以使用一个共有三个步骤地方法来应对这个挑战。这个方法包括在架构方案中定义逻辑商业领域、为这些商业领域分配程序集、并使两个程序集之间松耦合关联。这些步骤在下面详细说明：</p>
<ol>
    <li>
    <p><strong>商业领域定义</strong>. 在这个步骤中，我们建立商业功能的逻辑域。我们可以使用程序调用规划图和过程流图来定义这些商业领域。我们也可以通过调节架构系统的程序间的关系来定义商业领域。[架构系统的程序往往是与其它程序有关。在这种程序到程序的关系中，有一些通用的商业方法。]</p>
    <li>
    <p><strong>程序分配. </strong>在标准的商业领域，我们分配独立的程序到一个特定的领域。我们非常需要再分配那些没有讲自己关联到对应商业领域的程序，使在特定商业领域内排列更有规则。在前面讨论过的服务范围概念的引导下，这样一组程序集可以排列的更好。</p>
    <li>
    <p><strong>松耦合关联.</strong> 在这一点上，甚至即使这些程序已经在标准商业领域内排列过了，她们海狮需要彼此间相互关联。在最后这一步骤中，我们将原来的轻度耦合关联替换为一个更加松散的耦合。为了这样做，我们重新定义架构程序界面，这样其它的应用可以调节自己；程序提供同样输出并且接受同样输入，就像它们开始做的那样。这个重定义过程提供了一个非常好的机会来确保这些程序就像一个整体来服务企业，而不是单个程序独立服务企业—就像它们开始被创建时那样。这个方法同时也使现有架构系统朝着面向服务方向靠近，配置他们使它们在内部是结构系统，外部对服务用户来说是面向服务的。</p>
    </li>
</ol>
<p><strong>服务协调</strong> </p>
<p>一个特定服务的存在是因为至少有一个服务消费者发出了这项服务的请求。然而，在一些情况下，一个服务不得不调用许多其它服务来满足服务消费者的原始请求。简单的情况导致一个特定服务扩展原始请求到一个或者更多其它服务上。然而，复杂的情况可以导致大量服务的递归调用，在一些极端的例子里，一些服务的内部依赖调用—可以导致死循环。</p>
<p>这里有一个例子。当一张机票被出售时，下面的服务需要被执行：</p>
<p>取得消费者</p>
<p>取得日程表</p>
<p>检查可用信息</p>
<p>提供费用</p>
<p>接受支付</p>
<p>构建协调能力使得每一服务在如此复杂情况下都能顺利执行</p>
<p>如图7所示。</p>
<p><img alt=SOA07 src="http://i.msdn.microsoft.com/Aa480029.SOA07(zh-cn,MSDN.10).gif"> </p>
<p><strong>图7</strong> <strong>. </strong><strong>服务协调挑战</strong> </p>
<p>这些综合服务如何被协调？</p>
<p><em>商业过程管理方法</em> </p>
<p>这种方法使独立的服务更简单：这些服务没有能力来协调其它为了满足请求而调用的服务。</p>
<p>替代性的，这种能力被放置于商业过程层。商业过程负责调用每一个子服务，这样来提供组合服务来满足消费者的原始请求。商业过程就成为一个组成服务的专业例子。</p>
<p><img alt=SOA08 src="http://i.msdn.microsoft.com/Aa480029.SOA08(zh-cn,MSDN.10).gif"> </p>
<p><strong>图8</strong> <strong>. </strong><strong>商业过程管理方法</strong> </p>
<p>图8说明了这张机票购买的商业过程，包括每一个独立执行的逻辑步骤。这个商业过程通过一个简单的访问来查找服务，然后按顺序协调为适当步骤。</p>
<p><strong>服务发送</strong> </p>
<p><em>挑战</em> </p>
<p>SOA还要提供对消费者的地域透明性：服务消费者必须能够在任意服务范围内发送任意服务的请求。同时，在调用一个服务前访问服务库是一个减少时间的步骤。这些架构在保证可接受的系统性能水平的同时，如何提供地域透明性？</p>
<p><em>方法</em> </p>
<p>我们能够用两种方法解决服务发送挑战。</p>
<p><em>智能服务</em> </p>
<p>使用这种方法，我们为所有的服务建造各自的地域信息。这就消除了一些跳跃性的请求同时也导致了服务重载。考虑到服务以及服务所在场所的更新频率，这种方法有着持久性。另外，这种方法并不与松耦合的服务架构时刻一致。不过，他支持一个更高层次执行的解决方案。</p>
<p><em>发送</em> </p>
<p>另一种方法是智能的移动将发送从一个发送组件移动到独立的服务。这些发送组件可以在两种层次：服务域和服务。</p>
<ol>
    <li>
    <p><strong>服务域发送. </strong>一个服务域发送在所有服务域的场所上是智能的。当接收到一个请求，它确定是否它能够仅靠自己支持的服务来满足这个请求。如果能，它处理这个请求。如果不能，它将这个请求传送到正确的可以满足这个请求的服务范围。</p>
    <li>
    <p><strong>服务发送.</strong> 一个服务发送在一个服务范围内被调用，在域内将请求发送到正确的服务。只有哪些可以在域内满足服务的请求被传递给服务发送。服务发送减少了各个服务上场所信息的负载。</p>
    </li>
</ol>
<p><strong>服务域发送和服务发送对那些包括一定数量服务的服务域来说更加实用。对那些只有几个的服务，智能服务是一个可行的选择。</strong> </p>
<p><img alt=SOA09 src="http://i.msdn.microsoft.com/Aa480029.SOA09(zh-cn,MSDN.10).gif"> </p>
<p><strong>图9</strong> <strong>. </strong><strong>服务域发送和服务发送</strong> </p>
<p>图9说明了一个域内服务域发送与服务发送的概念。</p>
<p><strong>服务管理</strong> </p>
<p><em>挑战</em> </p>
<p>不管一个企业以何种方式定义服务域，这里有几种哲学与技术上的方法来创建新的服务或修改现存的服务。谁来监控，定义和管理企业内这些对现存服务的改变？</p>
<p><em>方法</em> </p>
<p>一个企业可以通过建立一个内部管理体来更有效的应对这个挑战。大量的管理模型都是可能的。下面讨论了一些。</p>
<p><em>中心管理</em> </p>
<p>通过中心管理，企业内的管理体从各个服务域和各个没有直接依赖于各个服务域的部分得到说明。也必须从不同的商业部门和一些事件处理专家那里得到说明，这些专家可能在解决方案的关键技术组件上发表看法。中心管理体就像一个服务的删减后的完整回顾，也包括对现存服务的改变，在允许他们执行前。</p>
<p><img alt=SOA10 src="http://i.msdn.microsoft.com/Aa480029.SOA10(zh-cn,MSDN.10).gif"> </p>
<p><strong>图10</strong> <strong>. </strong><strong>中心管理模型</strong> </p>
<p>如上面图10所示，中心管理体依赖于企业建立与执行SOA时的指导方针与标准。还依赖于通过这些标准与其他商业部门，架构小组和技术小组的交流。</p>
<p><em>分布式管理</em> </p>
<p>通过分布式管理，每一个商业部门可以独立控制如何在自身组织内部提供服务。分布式管理要求一种功能性服务领域方法。一个服务架构委员会可以依旧提供高水平的指导方针的标准给服务执行，但是委员会不必批准商业部门内部对现有服务架构的调整。委员会可以建议与指导方针保持一致但不能强迫这样做。</p>
<p><img alt=SOA11 src="http://i.msdn.microsoft.com/Aa480029.SOA11(zh-cn,MSDN.10).gif"> </p>
<p><strong>图11</strong> <strong>. </strong><strong>分布式管理模型</strong> </p>
<p>如图11 分布式管理模型所示，商业部门A与B都有权利来建立他们自己的独立的标准。然而适当的被动标准（架构和程序标准）都在适当的位置等候部门去遵守。</p>
<p><strong>服务通信标准采用</strong> </p>
<p><em>挑战</em> </p>
<p>对纵向工业来说，通信标准是不同的，这导致了基于一个数据元素集合和通信方式的标准。然而，在一个独立数据元素层次，这些标准是弹性的，企业能够适应它们来与企业特定商业限制保持一致。这样，同一企业内的不同的商业部门可以通过不同方式与统一标准保持一致。另外，这些标准还提供了客户数据元素的创建。</p>
<p>举例，IFX标准确定了多种方式对一个顾客：</p>
<ul>
    <li>
    <p>〈客户唯一标识符〉内部数据库用来唯一标识消费者</p>
    <li>
    <p>〈客户登录标识符〉消费者登录使用的账号</p>
    </li>
</ul>
<p>两个域都是唯一的。企业采用IFX标准就必须决定使用每个域的时间和地点。有时，消费者甚至决定忽略这两个标识符，自己重新创建一个客户域，这个客户域对企业记录系统更适合！</p>
<p>如何使企业选择一个单独的标准成为可能？</p>
<p><em>元数据管理方法</em> </p>
<p>企业内元数据库支持关键商业体的可靠描述。这些描述是分布在多个企业系统上信息的扩展集。数据字典也就是逻辑与物理数据模型是元数据库定义和维护的关键输入。</p>
<p>元数据管理组在前面讨论过的中心管理模型中应该是一个焦点集中的组。元数据管理必须在企业层次执行。换句话说，即使一个企业选择了分布式管理模型来维护服务，它也必须选择一个中心管理模型来支持元数据。</p>
<p>在上面的例子中，消费者的元数据包括一个简单形式的标识符，这个标识符是唯一的。另外，元数据要确保维持管理信息的描述，这些描述包括登录帐号和密码。因此，即使一个企业已经通过一个客户域为消费者选择了唯一标识符，这个客户域也必须在元数据库中被描述。</p>
<div><a href="http://msdn.microsoft.com/zh-cn/library/aa480029.aspx#mainSection"><img alt="" src="http://i.msdn.microsoft.com/Aa480029.arrow_px_up(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/aa480029.aspx#mainSection"><font color=#0033cc>返回页首</font></a> <br></div>
<h3 id=EDAA>结论</h3>
<p><strong>像一声春雷，</strong>SOA已经被IT界迅速接受，它帮助企业用模块化的方法搭建并部署服务。然而，架构的实际应用需要仔细的规划。感兴趣的企业必须首先确定他们适合长期应用和支持SOA。</p>
<p>通过开发并制定一个实现路线图，企业可以预先应对好一系列将遇到的挑战。每个企业都将面对一个独特的挑战集；相应的应对这些挑战的方法也就各自不同。这些挑战的影响—在执行中与执行后—还依赖于这些特定企业的环境。</p>
<p>关于作者</p>
<p>Easwaran G. Nadhan是EDS，Plano，Texas顾问中的关键人物。他有在软件产业设计并实现分布式解决方案的20年的经验。最近，他借助自己的经验帮助企业完成企业应用集成，同时实现了SOA。这篇文章中的例子以EDS提供服务的实践经验为基础。可以写信与Nadhan联系<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#101;&#97;&#115;&#119;&#97;&#114;&#97;&#110;&#46;&#110;&#97;&#100;&#104;&#97;&#110;&#64;&#101;&#100;&#115;&#46;&#99;&#111;&#109;"><font color=#0033cc>easwaran.nadhan@eds.com</font></a> </p>
<img src ="http://www.cnitblog.com/MartinYao/aggbug/52406.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/MartinYao/" target="_blank">玄铁剑</a> 2008-12-09 21:06 <a href="http://www.cnitblog.com/MartinYao/articles/52406.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>ASP.NET  服务器控件授权</title><link>http://www.cnitblog.com/MartinYao/articles/52403.html</link><dc:creator>玄铁剑</dc:creator><author>玄铁剑</author><pubDate>Tue, 09 Dec 2008 12:50:00 GMT</pubDate><guid>http://www.cnitblog.com/MartinYao/articles/52403.html</guid><wfw:comment>http://www.cnitblog.com/MartinYao/comments/52403.html</wfw:comment><comments>http://www.cnitblog.com/MartinYao/articles/52403.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/MartinYao/comments/commentRss/52403.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/MartinYao/services/trackbacks/52403.html</trackback:ping><description><![CDATA[<div class=title xmlns:msxsl="urn:schemas-microsoft-com:xslt">ASP.NET 服务器控件授权 </div>
<!--content type: DocStudio. Transform: psdk2mtps.xslt.-->
<div id=mainSection>
<div id=mainBody>
<div>
<div>发布日期 : 4/1/2004<span> | </span>更新日期 : 4/1/2004</div>
<p><strong>摘要：</strong>了解 ASP.NET 服务器控件的授权要求，了解可用于 .NET 框架版本 1.0 和 1.1 的 ASP.NET 控件授权实现。该实现可以进行扩展，以创建自定义的服务器端授权方案。（23 页打印页）</p>
<p>Nikhil Kothari Microsoft ASP.NET 小组</p>
<p>Vandana Datye 自由撰稿人</p>
<p>2003 年 7 月</p>
<p>适用于： Microsoft? ASP.NET</p>
<p>请下载 <a id=ctl00_rs1_mainContentContainer_ctl01 onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00|ctl00_rs1_mainContentContainer_ctl01',this);" href="http://download.microsoft.com/download/1/5/3/1531a189-a8bf-4866-baa1-5e49442289bf/ASPNETControlLicensing.msi"><font color=#0033cc>ASPNETControlLicensing.msi</font></a>。</p>
<p><strong>背景</strong> </p>
<p>本文假设读者熟悉 Microsoft? ASP.NET 编程和 ASP.NET 服务器控件创建。</p>
<p>Developing Microsoft ASP.NET Server Controls and Components (ISBN 0-7356-1582-9)（Microsoft Press，2002。保留所有权利。）一书中，关于授权部分内容更详细的阐述。该部分内容的使用已获得 Microsoft Press 的许可。有关该书的详细信息，请参阅 <a id=ctl00_rs1_mainContentContainer_ctl02 onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00|ctl00_rs1_mainContentContainer_ctl02',this);" href="http://www.microsoft.com/mspress/books/5728.asp"><font color=#0033cc>http://www.microsoft.com/mspress/books/5728.asp</font></a>。</p>
<div style="TEXT-ALIGN: center"><img alt=* src="http://i.msdn.microsoft.com/Aa479017.3squares(zh-cn,MSDN.10).gif"> </div>
<h5>本页内容</h5>
<p><a href="http://msdn.microsoft.com/zh-cn/library/aa479017.aspx#XSLTsection130121120120"><img alt=简介 src="http://i.msdn.microsoft.com/Aa479017.arrow_px_down(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/aa479017.aspx#XSLTsection130121120120"><font color=#0033cc>简介</font></a> <br><a href="http://msdn.microsoft.com/zh-cn/library/aa479017.aspx#XSLTsection131121120120"><img alt="ASP.NET 服务器控件授权要求" src="http://i.msdn.microsoft.com/Aa479017.arrow_px_down(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/aa479017.aspx#XSLTsection131121120120"><font color=#0033cc>ASP.NET 服务器控件授权要求</font></a> <br><a href="http://msdn.microsoft.com/zh-cn/library/aa479017.aspx#XSLTsection132121120120"><img alt=已授权控件演练 src="http://i.msdn.microsoft.com/Aa479017.arrow_px_down(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/aa479017.aspx#XSLTsection132121120120"><font color=#0033cc>已授权控件演练</font></a> <br><a href="http://msdn.microsoft.com/zh-cn/library/aa479017.aspx#XSLTsection133121120120"><img alt=".NET 框架授权结构" src="http://i.msdn.microsoft.com/Aa479017.arrow_px_down(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/aa479017.aspx#XSLTsection133121120120"><font color=#0033cc>.NET 框架授权结构</font></a> <br><a href="http://msdn.microsoft.com/zh-cn/library/aa479017.aspx#XSLTsection134121120120"><img alt="ASP.NET 服务器控件授权基础结构" src="http://i.msdn.microsoft.com/Aa479017.arrow_px_down(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/aa479017.aspx#XSLTsection134121120120"><font color=#0033cc>ASP.NET 服务器控件授权基础结构</font></a> <br><a href="http://msdn.microsoft.com/zh-cn/library/aa479017.aspx#XSLTsection135121120120"><img alt=扩展默认授权方案 src="http://i.msdn.microsoft.com/Aa479017.arrow_px_down(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/aa479017.aspx#XSLTsection135121120120"><font color=#0033cc>扩展默认授权方案</font></a> <br><a href="http://msdn.microsoft.com/zh-cn/library/aa479017.aspx#XSLTsection136121120120"><img alt=过期许可方案 src="http://i.msdn.microsoft.com/Aa479017.arrow_px_down(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/aa479017.aspx#XSLTsection136121120120"><font color=#0033cc>过期许可方案</font></a> <br><a href="http://msdn.microsoft.com/zh-cn/library/aa479017.aspx#XSLTsection137121120120"><img alt=加密的许可方案 src="http://i.msdn.microsoft.com/Aa479017.arrow_px_down(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/aa479017.aspx#XSLTsection137121120120"><font color=#0033cc>加密的许可方案</font></a> <br><a href="http://msdn.microsoft.com/zh-cn/library/aa479017.aspx#XSLTsection138121120120"><img alt=授权实现核对清单 src="http://i.msdn.microsoft.com/Aa479017.arrow_px_down(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/aa479017.aspx#XSLTsection138121120120"><font color=#0033cc>授权实现核对清单</font></a> <br><a href="http://msdn.microsoft.com/zh-cn/library/aa479017.aspx#XSLTsection139121120120"><img alt=小结 src="http://i.msdn.microsoft.com/Aa479017.arrow_px_down(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/aa479017.aspx#XSLTsection139121120120"><font color=#0033cc>小结</font></a> <br></p>
<h3 id=XSLTsection130121120120>简介</h3>
<p>Microsoft .NET 框架有一个内置的可扩展授权结构，支持所有托管组件（包括业务对象、Windows 窗体控件和 ASP.NET 服务器控件）的设计时授权和运行时授权。本文就建立在该结构的基础上，以提供专门针对 ASP.NET 控件进行优化的授权实现，并且，您可以扩展该授权实现，以创建自定义授权方案，例如： </p>
<ul>
    <li>
    <p>简单授权方案 － 只检查是否存在有效的许可数据，以决定是否启用控件。 </p>
    <li>
    <p>按每次使用授权方案 － 经过某个使用计数后，许可过期。此方案可用于控件的演示版。许可过期后，应用程序开发人员可以注册（和购买）您的控件，然后收到一个不过期许可。</p>
    <li>
    <p>只有当请求来自特定客户机（如本地计算机）时，才在某个页面中启用 ASP.NET 服务器控件的授权方案。此方案可用于实现控件的试用版。</p>
    <li>
    <p>依靠加密来防止应用程序开发人员进行许可数据欺骗的授权方案。</p>
    </li>
</ul>
<div><a href="http://msdn.microsoft.com/zh-cn/library/aa479017.aspx#mainSection"><img alt="" src="http://i.msdn.microsoft.com/Aa479017.arrow_px_up(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/aa479017.aspx#mainSection"><font color=#0033cc>返回页首</font></a> <br></div>
<h3 id=XSLTsection131121120120>ASP.NET 服务器控件授权要求</h3>
<p>ASP.NET 服务器控件授权方案必须满足以下要求： </p>
<ul>
    <li>
    <p>支持不编译方案。ASP.NET Web 应用程序常常使用动态编译模型，因此没有与应用程序相关联的预编译程序集。授权机制不应该依靠在应用程序的程序集中找到作为程序集资源嵌入的许可。 </p>
    <li>
    <p>支持运行时授权。页面开发人员使用可视设计时工具及简单文本编辑器，来开发自己的页面。授权机制不能依靠设计时检查，必须提供运行时验证。而且，运行时授权实现不应与任何（可选的）设计时授权实现有依赖关系。 </p>
    <li>
    <p>支持许可缓存机制。理想情况下，每个应用程序只应该检索一次许可数据，而不是针对每个页面请求都进行检索，因为检索逻辑会涉及开销较大的操作，例如，打开文件和对信息解密。应该在第一次需要许可时创建许可，并进行缓存，以便以后在服务器上重用。您仍然可以在每次使用许可来实现基于使用的授权方案时，验证缓存的许可。 </p>
    <li>
    <p>支持 XCOPY 部署。ASP.NET 使得页面开发人员能够只是通过在网络上的计算机之间复制文件，就可以部署其 Web 应用程序。授权方案不应该依靠注册表，或者其他禁止简单 XCOPY 部署的特定于计算机的资源。 </p>
    </li>
</ul>
<p>为简单起见，我们在前面的列表中使用了服务器控件这个术语。不过，授权要求适用于所有 ASP.NET 服务器组件。同样，本文中描述的 ASP.NET 控件授权方案也适用于其他 ASP.NET 服务器组件。</p>
<div><a href="http://msdn.microsoft.com/zh-cn/library/aa479017.aspx#mainSection"><img alt="" src="http://i.msdn.microsoft.com/Aa479017.arrow_px_up(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/aa479017.aspx#mainSection"><font color=#0033cc>返回页首</font></a> <br></div>
<h3 id=XSLTsection132121120120>已授权控件演练</h3>
<p>控件授权涉及三个关键元素： </p>
<ul>
    <li>
    <p>控件中支持授权的代码 </p>
    <li>
    <p>许可数据 </p>
    <li>
    <p>检查许可数据、发放许可以及在后来使用控件时验证许可的类</p>
    </li>
</ul>
<p><strong>已授权服务器控件</strong> </p>
<p>下面列出的 </p>
<pre>LicensedLabel</pre>
服务器控件是从 ASP.NET System.Web.UI.WebControls.Label 控件派生的，并为其添加了授权支持。以粗体显示的代码提供了授权功能。
<pre>// LicensedLabel.cs
//
using System;
using System.ComponentModel;
using System.Web.UI.WebControls;
namespace LicensedControls {
[
LicenseProvider(typeof(ServerLicenseProvider))
]
public class LicensedLabel : Label {
public LicensedLabel() {
LicenseManager.Validate(typeof(LicensedLabel));
}
}
}
</pre>
<p>该示例说明了为支持授权，您必须向任何服务器组件的代码添加下列内容： </p>
<ul>
    <li>
    <p>在控件的构造函数中，调用 <strong>System.ComponentModel.LicenseManager</strong> 类的静态方法 <strong>Validate</strong>，并将它作为参数传递到组件的类型中。如果该控件没有有效许可，<strong>LicenseManager</strong> 的 <strong>Validate</strong> 方法将引发 <strong>System.ComponentModel.LicenseException</strong>。另一种方法是，在构造函数中，您可以调用 <strong>LicenseManager</strong> 类的静态方法 <strong>IsValid</strong>，这样就不会引发异常。如果您希望在没有有效许可的情况下启用控件（在简装版本上就是如此），请调用 <strong>IsValid</strong> 方法。</p>
    <li>
    <p>将 <strong>System.ComponentModel.LicenseProviderAttribute</strong> 元数据属性应用于您的组件，并向它传递执行组件授权的许可提供程序（从 <strong>System.ComponentModel.LicenseProvider</strong> 派生的类）的类型。本文中 <a href="http://msdn.microsoft.com/zh-cn/library/aa479017.aspx#"><font color=#0033cc>ASP.NET 服务器控件授权基础结构</font></a>一节显示了 </p>
    <pre>LicensedLabel</pre>
    控件的许可提供程序
    <pre>ServerLicenseProvider</pre>
    的实现。 </li>
</ul>
<p>如图 1 所示，您为支持授权而必须对控件所做的更改是最小的。真正的授权功能在许可提供程序类中，稍后再说明这部分内容。</p>
<p>如果您已经在 Windows 窗体控件中实现了授权，您可能很惊奇地发现，</p>
<pre>LicensedLabel</pre>
不处置其许可。这是因为，
<pre>LicensedLabel</pre>
使用一个在服务器上缓存许可的许可提供程序。
<p><strong>许可数据</strong> </p>
<p>许可数据提供由授权结构进行验证并合并到许可中的信息。您可以用许多不同的方式提供许可数据（如过期日期、使用计数或唯一密钥）。许可数据的类型和位置由特定的授权方案来指定。通常在扩展名为 .lic 的文件中提供许可数据。图 1 中的 </p>
<pre>LicensedLabel</pre>
控件的许可数据位于一个名为 LicensedControls.LicensedLabel.lic 的文件中，该文件只包含文本 "LicensedControls.LicensedLabel is licensed"。
<p><strong>在页面上使用已授权控件</strong> </p>
<p>随本文的代码示例提供的 ReadMe 文档描述了如何构建这些示例。</p>
<p><strong>在页面中使用</strong> <strong>LicensedLabel </strong><strong>控件</strong> </p>
<p>1.将 LicensedControls 程序集（包含 </p>
<pre>LicensedLabel</pre>
控件）复制到应用程序的 \Bin 目录。如果您使用的是 Microsoft Visual Studio? .NET 并在您的 Web 应用程序项目中添加了对 LicensedControls 项目的引用，则不需要此步骤。
<p>2.将 LicensedControls.LicensedLabel.lic 文件复制到应用程序的 Licenses\LicensedControls\1.0.0.0 目录。</p>
<p>现在，您应该能从应用程序中的任何页面使用控件。</p>
<p>下面的代码显示了一个使用 </p>
<pre>LicensedLabel</pre>
控件的页面。
<pre>&lt;%@ Page language="c#" %&gt;
&lt;%@ Register TagPrefix="lc" Assembly="LicensedControls"
Namespace="LicensedControls" %&gt;
&lt;html&gt;
&lt;head&gt;
&lt;title&gt;LicensedLabel Sample&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;form method="post" runat="server" ID="Form1"&gt;
&lt;p&gt;
&lt;lc:LicensedLabel runat="server" id="LicensedLabel1" Text="Hello
World!" /&gt;
&lt;/p&gt;
&lt;/form&gt;
&lt;/body&gt;
&lt;/html&gt;
</pre>
<p>要查看授权是否正在生效，请删除 LicensedControls.LicensedLabel.lic 文件或将它移到另一个位置。重新生成应用程序或做出某个可导致应用程序重新启动的更改。此步骤的作用是清除由 </p>
<pre>ServerLicenseProvider</pre>
（
<pre>LicensedLabel</pre>
控件的元数据中指定的许可提供程序）管理的许可缓存。在浏览器中请求 LicensedLabelTest.aspx 页。该页将生成下图中显示的错误。
<p><img alt="" src="http://i.msdn.microsoft.com/Aa479017.aspnetcontrollicensing01(zh-cn,MSDN.10).gif"> </p>
<div><strong>图</strong> <strong>1. LicensedLabelTest.aspx </strong><strong>页尝试在没有有效许可的情况下使用</strong> <strong>LicensedLabel </strong><strong>时生成的错误</strong> </div>
<div><a href="http://msdn.microsoft.com/zh-cn/library/aa479017.aspx#mainSection"><img alt="" src="http://i.msdn.microsoft.com/Aa479017.arrow_px_up(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/aa479017.aspx#mainSection"><font color=#0033cc>返回页首</font></a> <br></div>
<h3 id=XSLTsection133121120120>.NET 框架授权结构</h3>
<p>下图（图 2）说明了 .NET 框架的授权结构。从中可以看出当一个页面尝试对前面一节描述的 </p>
<pre>LicensedLabel</pre>
控件进行实例化时发生的主要步骤。虽然实际步骤发生在服务器控件的上下文中，但该图显示了构成 .NET 框架授权结构的类，以及任何运行时授权方案所共有的关键步骤。许可提供程序执行的确切步骤是特定于提供程序实现的具体授权方案的。例如，正如本文中 <a href="http://msdn.microsoft.com/zh-cn/library/aa479017.aspx#"><font color=#0033cc>ASP.NET 服务器控件授权基础结构</font></a>一节所描述的，图中显示的许可缓存功能就是特定于
<pre>ServerLicenseProvider</pre>
的。以粗体显示的类是 .NET 框架类，以斜体显示的类是实现的派生类。
<p><img alt="" src="http://i.msdn.microsoft.com/Aa479017.aspnetcontrollicensing02(zh-cn,MSDN.10).gif"> </p>
<div><strong>图</strong> <strong>2. .NET </strong><strong>框架的授权结构</strong> </div>
<p>对控件实施授权的主要步骤包括： </p>
<p>1.已授权控件在其构造函数中调用静态方法 <strong>System.ComponentModel.LicenseManager.Validate</strong>。<strong>（该控件也可以在其构造函数中调用静态方法</strong> <strong>LicenseManager.IsValid</strong>。在这种情况下，返回类型与图中显示的会有所不同，并且不会引发异常。） </p>
<p>2.<strong>LicenseManager.Validate</strong> 方法检查组件的元数据，从应用于该组件的 <strong>LicenseProviderAttribute</strong> 属性获得许可提供程序的类型。许可提供程序类必须从 <strong>System.ComponentModel.LicenseProvider</strong> 类派生。 </p>
<p>3.<strong>LicenseManager</strong> 对许可提供程序类（<strong>System.ComponentModel.LicenseProviderAttribute</strong> 元数据属性中指定了它的类型）进行实例化，将该组件的类型传递到该许可提供程序，并指出该组件在设计时使用还是在运行时使用。 </p>
<p>4.许可提供程序在许可缓存中查找组件的许可。如果找到一个许可，许可提供程序就验证该许可。注意，许可缓存查找和许可存储不是一般的要求，而是特定于 </p>
<pre>ServerLicenseProvider</pre>
－ 我们已经实现的许可提供程序的。
<p>a.（仅限第一次）许可提供程序获取许可数据，并进行验证。如果该数据无效，许可提供程序将引发 <strong>System.ComponentModel.LicenseException</strong> 异常。 </p>
<p>b.（仅限第一次）如果许可数据有效，许可提供程序将创建一个许可（从 <strong>System.ComponentModel.License</strong> 派生的类）。此外，许可提供程序还会验证许可，如果许可有效，则将它存储在许可缓存中。 </p>
<p>5.许可提供程序将一个有效许可返回许可管理器，或引发许可异常。 </p>
<p>6.<strong>LicenseManager.Validate</strong> 方法返回一个有效许可，或将许可异常传递到调用代码中。 </p>
<p>7.如果 <strong>LicenseManager</strong> 返回有效许可，构造函数将对该类进行初始化，该控件将被实例化。否则，构造函数将 <strong>LicenseException</strong> 异常传递到试图实例化该控件的代码。本文<a href="http://msdn.microsoft.com/zh-cn/library/aa479017.aspx#"><font color=#0033cc>已授权控件演练</font></a>一节中的图所显示的错误消息是 ASP.NET 运行时产生的，ASP.NET 运行时处理当某页在没有有效许可的情况下使用已授权控件时，由该控件的构造函数传递的许可异常。 </p>
<p>初次创建指的是组件在 Web 应用程序中的第一次实例化。如果在同一页上或者在应用程序中的另一页上创建了该组件的另一个实例（在同一个请求中或者在后来的请求中），则不会发生步骤 4a 和 4b。出于性能方面的原因，</p>
<pre>ServerLicenseProvider</pre>
按每个应用程序对许可进行缓存（而不是按每页或每个会话）。
<p>.NET 框架中授权结构的设计使得非法使用组件非常困难（但并非不可能）。如果用户试图在没有许可的情况下使用一个已授权组件，授权机制就会使用户很明显地看出该组件正在被非法使用。授权不产生组件篡改证据。</p>
<p>.NET 框架中的授权结构是由 <strong>System.ComponentModel</strong> 命名空间中的以下四个类提供的： </p>
<ul>
    <li>
    <p><strong>LicenseManager</strong>：该类负责对组件的元数据中指定的许可提供程序进行实例化。许可管理器还向许可提供程序传递组件的类型和授权上下文，授权上下文指明该组件是在设计时使用还是在运行时使用。除了在组件的构造函数中调用 <strong>LicenseManager</strong> 类的 <strong>Validate</strong> 或 <strong>IsValid</strong> 方法之外，您无需知道有关 <strong>LicenseManager</strong> 的其他详细信息。</p>
    <li>
    <p><strong>LicenseProviderAttribute</strong>：此属性指定负责创建和验证组件许可的许可提供程序的类型。您必须将此属性应用于支持授权的组件。</p>
    <li>
    <p><strong>LicenseProvider</strong>：该类包含任何授权方案的核心功能 － 即发放和验证许可的任务。要实现授权支持，您必须通过从 <strong>LicenseProvider</strong> 派生来创建自定义许可提供程序，并实现基类的抽象方法 <strong>GetLicense</strong>，以提供授权逻辑。许可提供程序 </p>
    <pre>ServerLicenseProvider</pre>
    的实现将在本文下一节讨论。
    <li>
    <p><strong>License</strong>：该类是许可数据（如包含在 .lic 文件中的许可数据）的软件抽象。要实现许可类，您必须从 <strong>License</strong> 类派生，并实现基类的抽象属性 <strong>LicenseKey</strong>。在本文的下一节，我们将实现一个与 </p>
    <pre>ServerLicenseProvider</pre>
    一起使用的许可类。 </li>
</ul>
<p>.NET 框架在 <strong>System.ComponentModel.LicFileLicenseProvider</strong> 类中提供了许可提供程序的默认实现。该许可提供程序依靠可视设计器（如 Visual Studio .NET）在设计时和编译期间获取授权数据，将许可数据作为资源嵌入使用已授权组件的应用程序的程序集中。<strong>LicFileLicenseProvider</strong> 类可以由 Windows 窗体控件使用，但它不满足本文中 <a href="http://msdn.microsoft.com/zh-cn/library/aa479017.aspx#"><font color=#0033cc>ASP.NET 服务器控件授权要求</font></a>一节描述的 ASP.NET 服务器控件授权要求。</p>
<div><a href="http://msdn.microsoft.com/zh-cn/library/aa479017.aspx#mainSection"><img alt="" src="http://i.msdn.microsoft.com/Aa479017.arrow_px_up(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/aa479017.aspx#mainSection"><font color=#0033cc>返回页首</font></a> <br></div>
<h3 id=XSLTsection134121120120>ASP.NET 服务器控件授权基础结构</h3>
<p>本节将描述核心授权实现，它提供了 ASP.NET 服务器控件授权方案的具体部署。该实现包含在两个类中，</p>
<pre>ServerLicenseProvider</pre>
和
<pre>ServerLicense</pre>
，它们分别从 LicenseProvider 和 License 类中派生。我们希望在将来的 ASP.NET 版本中，类似的一组基类中有内置的授权支持。您可以使用和扩展
<pre>ServerLicenseProvider</pre>
和
<pre>ServerLicense</pre>
类，而不必检查其源代码，就像您使用 .NET 框架中的类一样。不过，为完整起见，本节包括了这些类的代码。
<p><strong>ServerLicenseProvider </strong><strong>类</strong> </p>
<p>&#160;</p>
<pre>ServerLicenseProvider</pre>
类是从 LicenseProvider 派生的，它覆盖了 GetLicense 方法，以实现核心服务器控件授权要求。
<pre>ServerLicenseProvider</pre>
满足了本文前面说明的服务器授权要求 － 不编译模型、运行时授权支持、授权缓存和 XCOPY 部署。
<pre>ServerLicenseProvider</pre>
将实现从 .lic 文本文件加载许可数据的默认授权方案，该文件存储在 Web 应用程序根目录中一个名为 Licenses 的目录中。此目录下的结构基于本文<a href="http://msdn.microsoft.com/zh-cn/library/aa479017.aspx#"><font color=#0033cc>已授权控件演练</font></a>一节中显示的程序集的名称和版本。该默认方案依靠在 .lic 文件中找到以下内容：&#8220;&lt;组件的完整类型名&gt; is licensed.&#8221;。
<pre>// ServerLicenseProvider.cs
//
using System;
using System.Collections;
using System.Collections.Specialized;
using System.ComponentModel;
using System.IO;
using System.Diagnostics;
using System.Globalization;
using System.Web;
namespace LicensedControls {
public class ServerLicenseProvider : LicenseProvider {
private static readonly ServerLicenseCollector LicenseCollector =
new ServerLicenseCollector();
protected virtual ServerLicense CreateLicense(Type type, string
key) {
return new ServerLicense(type, key);
}
protected virtual ServerLicense CreateEmptyLicense(Type type) {
return new ServerLicense(type, String.Empty);
}
public override License GetLicense(LicenseContext context, Type
type, object instance, bool allowExceptions) {
ServerLicense license = null;
string errorMessage = null;
if (context.UsageMode == LicenseUsageMode.Designtime) {
license = CreateEmptyLicense(type);
}
else {
license = LicenseCollector.GetLicense(type);
if (license == null) {
string licenseData = GetLicenseData(type);
if ((licenseData != null) &amp;&amp; (licenseData.Length !=
0)) {
if (ValidateLicenseData(type, licenseData)) {
ServerLicense newLicense = CreateLicense(type,
licenseData);
if (ValidateLicense(newLicense, out
errorMessage)) {
license = newLicense;
LicenseCollector.AddLicense(type,
license);
}
}
}
}
else {
if (ValidateLicense(license, out errorMessage) ==
false) {
license = null;
}
}
}
if (allowExceptions &amp;&amp; (license == null)) {
if (errorMessage == null) {
throw new LicenseException(type);
}
else {
throw new LicenseException(type, instance,
errorMessage);
}
}
return license;
}
protected virtual string GetLicenseData(Type type) {
string licenseData = null;
Stream licenseStream = null;
try {
licenseStream = GetLicenseDataStream(type);
if (licenseStream != null) {
StreamReader sr = new StreamReader(licenseStream);
licenseData = sr.ReadLine();
}
}
finally {
if (licenseStream != null) {
licenseStream.Close();
licenseStream = null;
}
}
return licenseData;
}
protected virtual Stream GetLicenseDataStream(Type type) {
string assemblyPart = type.Assembly.GetName().Name;
string versionPart =
type.Assembly.GetName().Version.ToString();
string relativePath = "~/licenses/" + assemblyPart + "/" +
versionPart + "/" + type.FullName + ".lic";
string licensesFile = null;
try {
licensesFile =
HttpContext.Current.Server.MapPath(relativePath);
if (File.Exists(licensesFile) == false) {
licensesFile = null;
}
}
catch {
}
if (licensesFile != null) {
return new FileStream(licensesFile, FileMode.Open,
FileAccess.Read, FileShare.Read);
}
return null;
}
protected virtual bool ValidateLicense(ServerLicense license, out
string errorMessage) {
errorMessage = null;
return true;
}
protected virtual bool ValidateLicenseData(Type type, string
licenseData) {
string licenseKey = type.FullName + " is licensed.";
return String.Compare(licenseKey, licenseData, true,
CultureInfo.InvariantCulture) == 0;
}
private sealed class ServerLicenseCollector {
private IDictionary _collectedLicenses;
public ServerLicenseCollector() {
_collectedLicenses = new HybridDictionary();
}
public void AddLicense(Type objectType, ServerLicense license)
{
if (objectType == null) {
throw new ArgumentNullException("objectType");
}
if (license == null) {
throw new ArgumentNullException("objectType");
}
_collectedLicenses[objectType] = license;
}
public ServerLicense GetLicense(Type objectType) {
if (objectType == null) {
throw new ArgumentNullException("objectType");
}
if (_collectedLicenses.Count == 0) {
return null;
}
return (ServerLicense)_collectedLicenses[objectType];
}
public void RemoveLicense(Type objectType) {
if (objectType == null) {
throw new ArgumentNullException("objectType");
}
_collectedLicenses.Remove(objectType);
}
}
}
}
</pre>
<p><strong>ServerLicense </strong><strong>类</strong> </p>
<p>&#160;</p>
<pre>ServerLicense</pre>
是与
<pre>ServerLicenseProvider</pre>
兼容的基许可类。
<pre>ServerLicense</pre>
是从 License 类派生的，并实现了该基类的抽象属性 LicenseKey。
<pre>// ServerLicense.cs
//
using System;
using System.ComponentModel;
using System.Diagnostics;
namespace LicensedControls {
public class ServerLicense : License {
private Type _type;
private string _key;
public ServerLicense(Type type, string key) {
_type = type;
_key = key;
}
public override string LicenseKey {
get {
return _key;
}
}
public Type LicensedType {
get {
return _type;
}
}
public override void Dispose() {
}
}
}
</pre>
<div><a href="http://msdn.microsoft.com/zh-cn/library/aa479017.aspx#mainSection"><img alt="" src="http://i.msdn.microsoft.com/Aa479017.arrow_px_up(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/aa479017.aspx#mainSection"><font color=#0033cc>返回页首</font></a> <br></div>
<h3 id=XSLTsection135121120120>扩展默认授权方案</h3>
<p>&#160;</p>
<pre>ServerLicenseProvider</pre>
和
<pre>ServerLicense</pre>
类实现了一个简单的默认授权方案。您可以对这些类进行扩展，以实现您自己的具有更复杂验证逻辑的自定义授权方案。要实现自定义授权方案，请从
<pre>ServerLicenseProvider</pre>
派生您自己的许可提供程序，并覆盖它的一个或多个虚拟方法（见下表）。要完成您的授权方案的实现，您可能还必须实现从
<pre>ServerLicense</pre>
派生的并与许可提供程序一起使用的许可类。
<p>下表描述了 </p>
<pre>ServerLicenseProvider</pre>
类中定义的可覆盖方法。
<table>
    <tbody>
        <tr>
            <th>
            <p>可覆盖的</p>
            </th>
            <th>
            <p>说明</p>
            </th>
        </tr>
        <tr>
            <td>
            <p>&#160;</p>
            <pre>protected virtual ServerLicense CreateLicense(Type type, string key)</pre>
            </td>
            <td>
            <p>创建并返回指定的已授权类型的 </p>
            <pre>ServerLicense</pre>
            实例。该字符串参数是与类型相关联的已验证许可数据。派生的许可提供程序可以覆盖此方法，以返回派生的许可。例如，请参见本文中<a href="http://msdn.microsoft.com/zh-cn/library/aa479017.aspx#"><font color=#0033cc>过期许可方案</font></a>一节中描述的
            <pre>ExpiringLicenseProvider</pre>
            。 </td>
        </tr>
        <tr>
            <td>
            <p>&#160;</p>
            <pre>protected virtual ServerLicense CreateEmptyLicense(Type type)</pre>
            </td>
            <td>
            <p>创建并返回不与真正的许可数据相关联的空的 </p>
            <pre>ServerLicense</pre>
            实例。
            <pre>ServerLicenseProvider</pre>
            使用空的许可来支持设计时使用。派生的许可提供程序可以覆盖此方法，以返回空的派生许可。 </td>
        </tr>
        <tr>
            <td>
            <p>&#160;</p>
            <pre>protected virtual string GetLicenseData(Type type)</pre>
            </td>
            <td>
            <p>通过读取许可流中的第一行数据，从许可流检索许可数据。派生的许可提供程序可以覆盖此方法，以便从不基于流的其他许可存储中进行读取。</p>
            </td>
        </tr>
        <tr>
            <td>
            <p>&#160;</p>
            <pre>protected virtual Stream GetLicenseDataStream(Type type)</pre>
            </td>
            <td>
            <p>打开用于读取许可数据的流。此方法还包含形成到适当的 .lic 文件的虚拟路径的逻辑。派生的许可提供程序可以覆盖此方法，以返回本文中<a href="http://msdn.microsoft.com/zh-cn/library/aa479017.aspx#"><font color=#0033cc>加密的许可方案</font></a>一节 </p>
            <pre>EncryptedLicenseProvider</pre>
            示例中显示的自定义流实现。 </td>
        </tr>
        <tr>
            <td>
            <p>&#160;</p>
            <pre>protected virtual bool ValidateLicense(ServerLicense license, out string errorMessage)</pre>
            </td>
            <td>
            <p>验证缓存的许可。该验证在每次请求许可时发生。派生的许可提供程序可以覆盖此方法，以实现自己的验证逻辑。本文<a href="http://msdn.microsoft.com/zh-cn/library/aa479017.aspx#"><font color=#0033cc>过期许可方案</font></a>一节中描述的 </p>
            <pre>ExpiringLicenseProvider</pre>
            示例实现了一个基于使用的授权方案。 </td>
        </tr>
        <tr>
            <td>
            <p>&#160;</p>
            <pre>protected virtual bool ValidateLicenseData(Type type, string licenseData)</pre>
            </td>
            <td>
            <p>验证许可数据，如果数据有效则创建许可并返回 True。派生的许可提供程序可通过覆盖此方法实现自定义验证规则。</p>
            </td>
        </tr>
    </tbody>
</table>
<table>
    <tbody>
    </tbody>
</table>
<div><a href="http://msdn.microsoft.com/zh-cn/library/aa479017.aspx#mainSection"><img alt="" src="http://i.msdn.microsoft.com/Aa479017.arrow_px_up(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/aa479017.aspx#mainSection"><font color=#0033cc>返回页首</font></a> <br></div>
<h3 id=XSLTsection136121120120>过期许可方案</h3>
<p>本节和下一节将说明如何扩展提供的默认授权实现，以创建自定义授权方案。本节方案中显示的过期许可方案将通过在控件已使用指定次数后禁用该控件来扩展默认方案。此方案可用于控件的演示版。</p>
<p>过期许可方案在 </p>
<pre>ExpiringLicenseProvider</pre>
类中实现。
<pre>// ExpiringLicenseProvider.cs
//
using System;
using System.Diagnostics;
using System.Globalization;
namespace LicensedControls {
public class ExpiringLicenseProvider : ServerLicenseProvider {
protected override ServerLicense CreateLicense(Type type, string
key) {
string[] parts = key.Split(';');
Debug.Assert(parts.Length == 2);
return new ExpiringLicense(type, key, Int32.Parse(parts[1],
CultureInfo.InvariantCulture));
}
protected override bool ValidateLicense(ServerLicense license, out
string errorMessage) {
errorMessage = null;
ExpiringLicense testLicense = (ExpiringLicense)license;
testLicense.IncrementUsageCounter();
if (testLicense.IsExpired) {
errorMessage = "The License for " +
testLicense.LicensedType.Name + " has expired.";
return false;
}
return true;
}
protected override bool ValidateLicenseData(Type type, string
licenseData) {
string[] parts = licenseData.Split(';');
if (parts.Length == 2) {
return base.ValidateLicenseData(type, parts[0]);
}
else {
return false;
}
}
}
}
</pre>
<p>&#160;</p>
<pre>ExpiringLicenseProvider</pre>
类是从
<pre>ServerLicenseProvider</pre>
派生的，它覆盖了该基类的下列方法：
<ul>
    <li>
    <p>&#160;</p>
    <pre>CreateEmptyLicense</pre>
    ，以创建不与真正的许可数据相关联的许可。空的许可适于在设计时使用。
    <li>
    <p>&#160;</p>
    <pre>CreateLicense</pre>
    ，以创建具有 .lic 文件指定的使用计数的许可。
    <li>
    <p>&#160;</p>
    <pre>ValidateLicense</pre>
    ，以检查许可是否已过期。
    <pre>ValidateLicense</pre>
    在进行检查之前将递增使用计数。
    <li>
    <p>&#160;</p>
    <pre>ValidateLicenseData</pre>
    ，以检查 .lic 文件中的文本字符串是否包含用分号分隔的两部分：&#8220;&lt;完整类型名&gt; is licensed.;&lt;使用计数&gt;&#8221;。 </li>
</ul>
<p>&#160;</p>
<pre>ExpiringLicenseProvider</pre>
类使用
<pre>ExpiringLicense</pre>
类作为其许可类型。
<pre>// ExpiringLicense.cs
//
using System;
namespace LicensedControls {
public class ExpiringLicense : ServerLicense {
private int _usageLimit;
private int _usageCount;
public ExpiringLicense(Type type, string key, int usageLimit) :
base(type, key) {
_usageLimit = usageLimit;
}
public bool IsExpired {
get {
return _usageCount &gt; _usageLimit;
}
}
public void IncrementUsageCounter() {
_usageCount++;
}
}
}
</pre>
<p>&#160;</p>
<pre>ExpiringLicense</pre>
类实现了以下逻辑：
<ul>
    <li>
    <p>定义两个私有字段，以存储使用限制和使用计数。</p>
    <li>
    <p>公开 </p>
    <pre>IncrementUsageCounter</pre>
    方法，该方法在每次访问
    <pre>ExpiringLicense</pre>
    对象时会递增使用计数。
    <li>
    <p>公开 </p>
    <pre>IsExpired</pre>
    属性，该属性通过将使用计数与使用限制进行比较，确定许可是否已经过期。 </li>
</ul>
<p>注意，使用计数可保存在 </p>
<pre>ExpiringLicense</pre>
本身中，因为服务器许可是进行缓存的。
<pre>ServerLicenseProvider</pre>
对于一个特定的组件类型只会创建许可的一个实例，并将许可保存在其内部许可缓存中。不过，在缓存的许可对象中存储数据并不十分安全。例如，如果应用程序重新启动，使用计数将重置为零，因此使得总使用限制大于许可数据中指定的许可限制。同样，如果应用程序是在服务器场上部署的，所有服务器上的总使用计数则可能会超过预期的使用限制。不过，
<pre>ExpiringLicense</pre>
中实现的逻辑还是可以接受的，因为授权的目的是为了在应用程序的性能不会受到重大影响的情况下，防止对组件进行未经授权的使用。
<p>下面的代码显示了将 </p>
<pre>ExpiringLicenseProvider</pre>
类用于其授权方案的控件。
<pre>// ExpiringLicensedLabel.cs
//
using System;
using System.ComponentModel;
using System.Web.UI.WebControls;
namespace LicensedControls {
[
LicenseProvider(typeof(ExpiringLicenseProvider))
]
public class ExpiringLicensedLabel : Label {
public ExpiringLicensedLabel() {
LicenseManager.Validate(typeof(ExpiringLicensedLabel));
}
}
}
</pre>
<p>&#160;</p>
<pre>ExpiringLicensedLabel</pre>
的.lic 文件中的内容是 "LicensedControls.ExpiringLicensedLabel is licensed.;5"。
<p>如上面的示例所示，如果许可是在一个明文文件中指定的，应用程序开发人员则可以很容易地欺骗该文件中的数据。您可以通过对许可数据加密（如下节所示）来提高授权方案的安全性。</p>
<div><a href="http://msdn.microsoft.com/zh-cn/library/aa479017.aspx#mainSection"><img alt="" src="http://i.msdn.microsoft.com/Aa479017.arrow_px_up(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/aa479017.aspx#mainSection"><font color=#0033cc>返回页首</font></a> <br></div>
<h3 id=XSLTsection137121120120>加密的许可方案</h3>
<p>本节将扩展默认方案，以实现一个加密的许可提供程序，它会在读取加密的许可数据时对这些加密许可数据进行解密。下面的代码显示了 </p>
<pre>EncryptedLicenseProvider</pre>
类。
<pre>// EncryptedLicenseProvider.cs
//
using System;
using System.Diagnostics;
using System.IO;
using System.Security.Cryptography;
namespace LicensedControls {
public class EncryptedLicenseProvider : ServerLicenseProvider {
// This is a 64-bit key generated from the string
// "5FB281F6".
//
private static readonly byte[] encryptionKeyBytes =
new byte[] { 0x35, 0x46, 0x42, 0x32, 0x38, 0x31, 0x46, 0x36 };
protected override Stream GetLicenseDataStream(Type type) {
Stream baseStream = base.GetLicenseDataStream(type);
if (baseStream == null) {
return null;
}
DESCryptoServiceProvider des = new DESCryptoServiceProvider();
des.Key = encryptionKeyBytes;
des.IV = encryptionKeyBytes;
ICryptoTransform desDecryptor = des.CreateDecryptor();
return new CryptoStream(baseStream, desDecryptor,
CryptoStreamMode.Read);
}
}
}
</pre>
<p>&#160;</p>
<pre>EncryptedLicenseProvider</pre>
类是从
<pre>ServerLicenseProvider</pre>
派生的，并覆盖了
<pre>GetLicenseDataStream</pre>
方法，该方法创建 System.IO.Stream 对象来读取许可数据。
<pre>EncryptedLicenseProvider</pre>
用 System.Security.Cryptography.CryptoStream 来包装此流，以便在许可数据被读入时对其进行解密。示例中使用的 CryptoStream 采用了带有 64 位加密密钥的数据加密标准 (DES) 密码算法，加密密钥作为私有字段
<pre>encryptionKeyBytes</pre>
嵌入到了
<pre>EncryptedLicenseProvider</pre>
类本身。之所以允许用这种方式嵌入密钥，是因为授权结构的设计使得破坏许可非常困难，甚至不可能。Win32 安全 API 提供了更多存储加密密钥的更加复杂的机制；不过，那些技术通常不适于 XCOPY 部署。
<p>&#160;</p>
<pre>ServerLicense</pre>
很适合作为
<pre>EncryptedLicenseProvider</pre>
的许可类，因此无需为此许可提供程序实现任何派生的许可类。
<p>下面代码中显示的 </p>
<pre>EncryptedLicensedLabel</pre>
控件使用了在
<pre>EncryptedLicenseProvider</pre>
类中实现的授权方案。
<pre>// EncryptedLicensedLabel.cs
//
using System;
using System.ComponentModel;
using System.Web.UI.WebControls;
namespace LicensedControls {
[
LicenseProvider(typeof(EncryptedLicenseProvider))
]
public class EncryptedLicensedLabel : Label {
public EncryptedLicensedLabel() {
LicenseManager.Validate(typeof(EncryptedLicensedLabel));
}
}
}
</pre>
<p>下图显示了与 </p>
<pre>EncryptedLicensedLabel</pre>
相关联的 .lic 文件。
<p><img alt="" src="http://i.msdn.microsoft.com/Aa479017.aspnetcontrollicensing03(zh-cn,MSDN.10).gif"> </p>
<div><strong>图</strong> <strong>3. LicensedControls.EncryptedLicensedLabel </strong><strong>文件的内容</strong> </div>
<p>此文件的内容是使用 </p>
<pre>EncryptedLicenseProvider</pre>
中嵌入的同一个密钥加密的。该文件中的数据是字符串 "LicensedControls.EncryptedLicensedLabel is licensed"。不过，由于已经加密，人们无法阅读。本文的示例文件中提供了加密工具 EncLicGen.exe 及其源代码 EncryptedLicenseGenerator.cs。
<p>加密使授权方案变得更加强大。例如，您可以在过期许可方案中使用加密来提高其安全性。您可以将自己的数据和用户的注册信息组合在一起进行加密，而不是像此示例中那样对固定字符串加密。</p>
<div><a href="http://msdn.microsoft.com/zh-cn/library/aa479017.aspx#mainSection"><img alt="" src="http://i.msdn.microsoft.com/Aa479017.arrow_px_up(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/aa479017.aspx#mainSection"><font color=#0033cc>返回页首</font></a> <br></div>
<h3 id=XSLTsection138121120120>授权实现核对清单</h3>
<p>下表描述了实现服务器控件的授权时需要执行的任务： </p>
<ul>
    <li>
    <p>实现一个从 </p>
    <pre>ServerLicenseProvider</pre>
    类派生的许可提供程序，并覆盖基类的一个或多个虚拟方法，以提供授权方案的逻辑。
    <li>
    <p>实现一个从 </p>
    <pre>ServerLicense</pre>
    类派生、与您实现的许可提供程序一起使用的许可类（请参见上一个项目符号后的内容）。如果
    <pre>ServerLicense</pre>
    适用于您的授权方案，则不需要此步骤（如本文上一节的
    <pre>EncryptedLicenseProvider</pre>
    示例所示）。
    <li>
    <p>通过应用 <strong>LicenseProviderAttribute</strong> 元数据属性并向它传递您实现的许可提供程序的类型（请参见第一个项目符号后的内容），向您的组件添加授权支持。还需要从组件的构造函数调用 <strong>LicenseManager.Validate</strong>。 </p>
    <li>
    <p>为组件创建授权数据。您可以将此数据保存在 .lic 文件中，或以授权方案所要求的任何其他形式保存。 </p>
    <li>
    <p>（可选）创建一个工具来生成许可数据。例如，随本文的示例文件提供的加密工具 EncLicGen.exe。如果您要创建用于商用分发的组件，您会发现拥有一个自动创建许可数据的工具非常有用。 </p>
    <li>
    <p>如果组件的许可数据包含在文件中（如 .lic 文件），请向应用程序开发人员提供有关说明，用于创建您的授权方案所需的目录结构，以及将许可文件复制到 Web 应用程序中的必要位置。</p>
    </li>
</ul>
<div><a href="http://msdn.microsoft.com/zh-cn/library/aa479017.aspx#mainSection"><img alt="" src="http://i.msdn.microsoft.com/Aa479017.arrow_px_up(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/aa479017.aspx#mainSection"><font color=#0033cc>返回页首</font></a> <br></div>
<h3 id=XSLTsection139121120120>小结</h3>
<p>本文讨论了 ASP.NET 服务器控件授权的要求和 .NET 框架授权结构。我们讲述了三个关键要素 － 控件中支持授权的代码、许可数据以及发放和验证许可的许可提供程序类 － 是如何提供授权功能的。我们提供了一个默认的 ASP.NET 服务器控件授权实现，该实现创建了服务器控件授权方案的具体部署，我们还说明了如何通过创建不同的自定义授权方案来扩展此默认授权实现。</p>
</div>
</div>
</div>
<img src ="http://www.cnitblog.com/MartinYao/aggbug/52403.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/MartinYao/" target="_blank">玄铁剑</a> 2008-12-09 20:50 <a href="http://www.cnitblog.com/MartinYao/articles/52403.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>ASP.NET 控件开发速成</title><link>http://www.cnitblog.com/MartinYao/articles/52402.html</link><dc:creator>玄铁剑</dc:creator><author>玄铁剑</author><pubDate>Tue, 09 Dec 2008 12:48:00 GMT</pubDate><guid>http://www.cnitblog.com/MartinYao/articles/52402.html</guid><wfw:comment>http://www.cnitblog.com/MartinYao/comments/52402.html</wfw:comment><comments>http://www.cnitblog.com/MartinYao/articles/52402.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/MartinYao/comments/commentRss/52402.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/MartinYao/services/trackbacks/52402.html</trackback:ping><description><![CDATA[Microsoft ASP.NET 2.0<br>Visual Basic 2005<br>Visual C# 2005<br>.NET Frameworks<br>Visual Web Developer 2005
<p><strong>摘要：</strong> Dino Esposito 一直在编写有关 ASP.NET 控件开发的系列教程，并在以下第四部分中介绍了如何使用和创建复合控件。</p>
<p><strong>随本文提供了</strong> <strong>Visual Basic </strong><strong>和</strong> <strong>C# </strong><strong>两种源代码。请从</strong> <a id=ctl00_rs1_mainContentContainer_ctl01 onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00|ctl00_rs1_mainContentContainer_ctl01',this);" href="http://download.microsoft.com/download/4/0/1/4019B622-A922-493F-9673-C3169C9891E1/Composite-Source.msi"><font color=#0033cc>此处</font></a> <strong>下载。</strong> </p>
<h5>本页内容</h5>
<p><a href="http://msdn.microsoft.com/zh-cn/library/aa479016.aspx#XSLTsection124121120120"><img alt=简介 src="http://i.msdn.microsoft.com/Aa479016.arrow_px_down(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/aa479016.aspx#XSLTsection124121120120"><font color=#0033cc>简介</font></a> <br><a href="http://msdn.microsoft.com/zh-cn/library/aa479016.aspx#XSLTsection127121120120"><img alt=复合控件的要点是什么？ src="http://i.msdn.microsoft.com/Aa479016.arrow_px_down(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/aa479016.aspx#XSLTsection127121120120"><font color=#0033cc>复合控件的要点是什么？</font></a> <br><a href="http://msdn.microsoft.com/zh-cn/library/aa479016.aspx#XSLTsection130121120120"><img alt=复合控件的常见方案 src="http://i.msdn.microsoft.com/Aa479016.arrow_px_down(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/aa479016.aspx#XSLTsection130121120120"><font color=#0033cc>复合控件的常见方案</font></a> <br><a href="http://msdn.microsoft.com/zh-cn/library/aa479016.aspx#XSLTsection133121120120"><img alt=复合控件的呈现引擎 src="http://i.msdn.microsoft.com/Aa479016.arrow_px_down(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/aa479016.aspx#XSLTsection133121120120"><font color=#0033cc>复合控件的呈现引擎</font></a> <br><a href="http://msdn.microsoft.com/zh-cn/library/aa479016.aspx#XSLTsection136121120120"><img alt="用于解决设计时问题的 CompositeControl" src="http://i.msdn.microsoft.com/Aa479016.arrow_px_down(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/aa479016.aspx#XSLTsection136121120120"><font color=#0033cc>用于解决设计时问题的 CompositeControl</font></a> <br><a href="http://msdn.microsoft.com/zh-cn/library/aa479016.aspx#XSLTsection139121120120"><img alt=生成数据绑定复合控件 src="http://i.msdn.microsoft.com/Aa479016.arrow_px_down(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/aa479016.aspx#XSLTsection139121120120"><font color=#0033cc>生成数据绑定复合控件</font></a> <br><a href="http://msdn.microsoft.com/zh-cn/library/aa479016.aspx#XSLTsection142121120120"><img alt=结论 src="http://i.msdn.microsoft.com/Aa479016.arrow_px_down(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/aa479016.aspx#XSLTsection142121120120"><font color=#0033cc>结论</font></a> <br></p>
<h3 id=XSLTsection124121120120>简介</h3>
<p>复合控件只不过是普通的 ASP.NET 控件，还不属于要论及的另一种类型的 ASP.NET 服务器控件。既然这样，为什么在各书籍和文档中总要留出专门的章节来论述复合控件呢？ASP.NET 复合控件有什么特别之处呢？</p>
<p>顾名思义，复合控件是将多个其他控件聚集在某单一顶部和单一 API 下的控件。如果某个自定义控件由一个标签和一个文本框组成，就可以说该控件是一个复合控件。&#8220;复合&#8221;一词表明该控件本质上是由其他构成组件在运行时组合而成。复合控件所暴露的方法集和属性集通常（但不是必须）由构成组件的方法和属性提供，并加入一些新成员。复合控件也可以引发自定义事件，还可以处理并激起子控件所引起的事件。</p>
<p>复合控件在 ASP.NET 中如此特别并不是因为其有可能成为服务器控件新类型的代表。更确切的说是因为它在呈现时获得了 ASP.NET 运行时的支持。</p>
<p>复合控件是一个功能强大的工具，可以生成丰富复杂的组件，这些组件产生自活动对象的相互作用而不是某些字符串生成器对象的标记输出。复合控件以构成控件树的形式呈现，每个构成控件都有其自己的生命周期和事件，并且所有构成控件都联合构成一个全新的 API，并按需要尽可能地抽象化。</p>
<p>在本文中，我将论述复合控件的内部体系结构，以阐明它在多种情况下为您带来的好处。接下来，我将生成一个复合列表控件，与我在以前文章中所述控件的功能集相比，此控件的功能集更为丰富。</p>
<div><a href="http://msdn.microsoft.com/zh-cn/library/aa479016.aspx#mainSection"><img alt="" src="http://i.msdn.microsoft.com/Aa479016.arrow_px_up(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/aa479016.aspx#mainSection"><font color=#0033cc>返回页首</font></a> <br></div>
<h3 id=XSLTsection127121120120>复合控件的要点是什么？</h3>
<p>前一段时间，我曾经自己尝试在 ASP.NET. 中研究复合控件。我从 MSDN 文档学习理论和实践知识，并也设计出一些不错的控件。但是，只有当我有一次在纯属偶然的情况下看到以下示例时，我才真正领悟到复合控件的要点（和优点）。设想一下由两个其他控件（<strong>Label</strong> 和 <strong>TextBox</strong>）的组合生成的迄今为止最简单（也是最常见）的控件。以下介绍了一种编写这种控件的可行方法。我们将其命名为 <strong>LabelTextBox</strong>。</p>
<pre>public class LabelTextBox :WebControl, INamingContainer
{
public string Text {
get {
object o = ViewState["Text"];
if (o == null)
return String.Empty;
return (string) o;
}
set { ViewState["Text"] = value; }
}
public string Title {
get {
object o = ViewState["Title"];
if (o == null)
return String.Empty;
return (string) o;
}
set { ViewState["Title"] = value; }
}
protected override void CreateChildControls()
{
Controls.Clear();
CreateControlHierarchy();
ClearChildViewState();
}
protected virtual void CreateControlHierarchy()
{
TextBox t = new TextBox();
Label l = new Label();
t.Text = Text;
l.Text = Title;
Controls.Add(l);
Controls.Add(t);
}
}
</pre>
<p>该控件具备两个公共属性（<strong>Text</strong> 和 <strong>Title</strong>）以及一个呈现引擎。这两个属性保存在视图状态中，并分别表示 <strong>TextBox</strong> 和 <strong>Label</strong> 的内容。该控件对于 <strong>Render</strong> 方法没有替换方法，并通过 <strong>CreateChildControls</strong> 替换方法来生成其自己的标记。我马上就会详述呈现阶段的例行过程。<strong>CreateChildControls</strong> 的代码首先清除子控件的集合，然后为当前控件输出的构成控件生成控件树。<strong>CreateControlHierarchy</strong> 是一种特定于控件的方法，不要求必须标记为受保护和虚拟。但请注意，大多数自带复合控件（例如 <strong>DataGrid</strong>）只是通过一个类似的虚拟方法来暴露用于生成控件树的逻辑。</p>
<p><strong>CreateControlHierarchy</strong> 方法会根据需要实例化多个构成组件，然后合成最终输出。完成之后，各控件将被添加到当前控件的 <strong>Controls</strong> 集合。如果希望控件的输出结果是一个 HTML 表，则可以创建一个 <strong>Table</strong> 控件，并相应添加含有各自内容的行和单元格。所有行、单元格和所含控件都是最外部表的子项。这时，您只需将 <strong>Table</strong> 控件添加到 <strong>Controls</strong> 集合中即可。在上述代码中，Label 和 TextBox 是 <strong>LabelTextBox</strong> 控件的直接子项并直接添加到集合中。控件的呈现状态和运行状态都很正常。</p>
<p>单纯从性能上看，创建控件的暂态实例不如呈现一些纯文本的效率高。让我们考虑一种无需子控件就能编写上述控件的替代方法。这次让我们将其命名为 <strong>TextBoxLabel</strong>。 </p>
<pre>public class LabelTextBox :WebControl, INamingContainer
{
:
protected override void Render(HtmlTextWriter writer)
{
string markup = String.Format(
"&lt;span&gt;{0}&lt;/span&gt;&lt;input type=text value='{1}'&gt;",
Title, Text);
writer.Write(markup);
}
}
</pre>
<p>该控件具备同样的两个属性（<strong>Text</strong> 和 <strong>Title</strong>）并替换了 <strong>Render</strong> 方法。正如您所看到的那样，其实现过程相当简单并且代码运行速度也略胜一筹。您可以通过在字符串生成器中合成文本并为浏览器输出最终标记来取代合成子控件的这种方法。同样，此时控件的呈现状态良好。但我们真的可以说它的运行状态也同样良好吗？图 1 显示了在示例页中运行的两个控件。</p>
<p><img alt="" src="http://i.msdn.microsoft.com/Aa479016.aspnetcontdev01(zh-cn,MSDN.10).gif"> </p>
<div><strong>图 1：使用不同呈现引擎的相似控件</strong> </div>
<p>在页面中启用跟踪功能并重新运行。当页面显示在浏览器中时，将其向下滚动并查看控件树。它将如下所示：</p>
<p><img alt="" src="http://i.msdn.microsoft.com/Aa479016.aspnetcontdev02(zh-cn,MSDN.10).gif"> </p>
<div><strong>图 2：由两个控件生成的控件树</strong> </div>
<p>复合控件由构成组件的活动实例组成。ASP.NET 运行时会发现这些子控件，并可以在处理已发布数据时同它们进行直接通信。其结果是，子控件可以自己处理视图状态并自动激起事件。</p>
<p>对于基于标记合成的控件，情况则不同。如图中所示，该控件是一个带有空 <strong>Controls</strong> 集合的代码基本单位。如果标记在页面中注入交互元素（文本框、按钮、下拉式菜单），则 ASP.NET 在不涉及控件本身的情况下无法处理回发数据及事件。 </p>
<p>尝试在两个文本框中输入一些文本并单击图 1 中的&#8220;刷新&#8221;按钮，这样就可以发生一个回发。第一个控件（即复合控件）在经过回发后会正确保留所分配的文本。使用 <strong>Render</strong> 方法的第二个控件在经过回发后会丢失新文本。为什么会这样呢？其中兼有两个原因。</p>
<p>第一个原因是，在上述标记中我没有为 <strong>&lt;input&gt;</strong> 标记命名。这样，它的内容就不会回发。请注意，必须使用 <strong>name</strong> 属性来为元素命名。让我们对 <strong>Render</strong> 方法做如下修改。</p>
<pre>protected override void Render(HtmlTextWriter writer)
{
string markup = String.Format(
"&lt;span&gt;{0}&lt;/span&gt;&lt;input type=text value='{1}' name='{2}'&gt;",
Title, Text, ClientID);
writer.Write(markup);
}
</pre>
<p>注入客户端页面的 <strong>&lt;input&gt;</strong> 元素现在与服务器控件使用相同的 ID。页面回发时，ASP.NET 运行时可发现一个与已发布字段的 ID 相匹配的服务器控件。但它并不知道如何处理该控件。要使 ASP.NET 将所有的客户端更改都应用于服务器控件，该控件必须实现 <strong>IPostBackDataHandler</strong> 接口。 </p>
<p>包含 <strong>TextBox</strong> 的复合控件无需担心回发问题，因为所嵌入的控件会使用 ASP.NET 自动解决该问题。呈现 <strong>TextBox</strong> 的控件需要与 ASP.NET 进行交互，以确保可以正确处理回发值并正常引发事件。以下代码表明了如何扩展 <strong>TextBoxLabel</strong> 控件以使其完全支持回发。</p>
<pre>bool LoadPostData(string postDataKey, NameValueCollection postCollection)
{
string currentText = Text;
string postedText = postCollection[postDataKey];
if (!currentText.Equals(postedText, StringComparison.Ordinal))
{
Text = postedText;
return true;
}
return false;
}
void IPostBackDataHandler.RaisePostDataChangedEvent()
{
return;
}
</pre>
<div><a href="http://msdn.microsoft.com/zh-cn/library/aa479016.aspx#mainSection"><img alt="" src="http://i.msdn.microsoft.com/Aa479016.arrow_px_up(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/aa479016.aspx#mainSection"><font color=#0033cc>返回页首</font></a> <br></div>
<h3 id=XSLTsection130121120120>复合控件的常见方案</h3>
<p>复合控件是适合用于构建复杂组件的工具，在复合控件中，多个子控件聚合到一起，并在彼此之间以及与外部之间进行交互。呈现控件则只用于只读式控件聚合，其输出不包括交互元素（例如下拉框或文本框）。 </p>
<p>如果您对事件处理和回发数据感兴趣，我强烈建议您选择复合控件。如果使用子控件，则生成复杂的控件树会更加轻松，而且最终结果也更清晰简洁。此外，只有需要提供附加功能时才需要处理回发接口。</p>
<p>呈现控件不但需要实现附加接口，还要将含有属性值的标记静态部分缝合到一起。</p>
<p>复合控件的优点还表现在可以呈现多个同类项，这与在 <strong>DataGrid</strong> 控件中的情况类似。将每个构成项作为活动对象启用使您可以引发创建事件并以编程方式访问它们的属性。在 ASP.NET 2.0 中，对于要完全实现实际的数据绑定复合控件（上述控件只是随便的举例）所需的样板代码，绝大部分都隐藏在新基类的折叠部分中：<strong>CompositeDataBoundControl</strong>。</p>
<div><a href="http://msdn.microsoft.com/zh-cn/library/aa479016.aspx#mainSection"><img alt="" src="http://i.msdn.microsoft.com/Aa479016.arrow_px_up(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/aa479016.aspx#mainSection"><font color=#0033cc>返回页首</font></a> <br></div>
<h3 id=XSLTsection133121120120>复合控件的呈现引擎</h3>
<p>在深入探讨 ASP.NET 2.0 编码技术之前，让我们回顾一下复合控件的内部例行过程。我们提到过，复合控件的呈现是集中围绕 <strong>CreateChildControls</strong> 方法进行的，该方法从 <strong>Control</strong> 基类继承而来。您可能会认为，要使服务器控件呈现其内容，替换 <strong>Render</strong> 方法是必不可少的一步。正如我们先前所看到的，如果 <strong>CreateChildControls</strong> 被替换，则并不总是需要执行这一步。但是，何时在控件调用栈中调用 <strong>CreateChildControls</strong> 呢？</p>
<p>如图中所示，在页面第一次显示时，会在预呈现阶段调用 <strong>CreateChildControls</strong>。</p>
<p><img alt=aspnetcontdev03 src="http://i.msdn.microsoft.com/Aa479016.aspnetcontdev03(zh-cn,MSDN.10).gif"> </p>
<div><strong>图 3：在预呈现阶段调用 CreateChildControls</strong> </div>
<p>特别是，请求处理代码（在 <strong>Page</strong> 类中）在将 <strong>PreRender</strong> 事件引发至页面和每个子控件之前会直接调用 <strong>EnsureChildControls</strong>。换言之，如果控件树还未完全生成，则不会呈现任何控件。</p>
<p>以下代码段例示了 <strong>EnsureChildControls</strong>（在 <strong>Control</strong> 基础上定义的另一种方法）的伪代码。</p>
<pre>protected virtual void EnsureChildControls()
{
if (!ChildControlsCreated)
{
try {
CreateChildControls();
}
finally {
ChildControlsCreated = true;
}
}
}
</pre>
<p>此方法可能会在页面和控件的生命周期内反复调用。为避免控件重复，<strong>ChildControlsCreated</strong> 属性被设为 <strong>true</strong>。如果此属性返回 <strong>true</strong>，则该方法会立即退出。</p>
<p>当页面回发时，<strong>ChildControlsCreated</strong> 会在周期前期调用。如图 4 所示，它在已发布数据处理阶段调用。</p>
<p><img alt="" src="http://i.msdn.microsoft.com/Aa479016.aspnetcontdev04(zh-cn,MSDN.10).gif"> </p>
<div><strong>图 4：发生回发时在已发布数据处理阶段调用</strong> </div>
<p>当 ASP.NET 页面开始处理从客户端发布的数据时，它会尝试查找一个其 ID 与已发布字段的名称相匹配的服务器控件。在执行此步骤期间，页面代码会调用 <strong>Control</strong> 类中的 <strong>FindControl</strong> 方法。反之，该方法需要确保在进行操作之前控件树已完全生成，因此它调用 <strong>EnsureChildControls</strong> 并按需要生成控件层次结构。</p>
<p>那么要在 <strong>CreateChildControls</strong> 方法内部执行的代码是怎样的呢？尽管没有正式的指南可供遵循，但通常认为 <strong>CreateChildControls</strong> 至少必须完成以下任务：清除 <strong>Controls</strong> 集合，生成控件树，并清除子控件的视图状态。并不严格要求必须从 <strong>CreateChildControls</strong> 方法内部设置 <strong>ChildControlsCreated</strong> 属性。实际上，ASP.NET 页面框架始终通过 <strong>EnsureChildControls</strong>（此方法可自动设置布尔标记）来调用 <strong>CreateChildControls</strong>。</p>
<div><a href="http://msdn.microsoft.com/zh-cn/library/aa479016.aspx#mainSection"><img alt="" src="http://i.msdn.microsoft.com/Aa479016.arrow_px_up(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/aa479016.aspx#mainSection"><font color=#0033cc>返回页首</font></a> <br></div>
<h3 id=XSLTsection136121120120>用于解决设计时问题的 CompositeControl</h3>
<p>随 ASP.NET 2.0 一同提供了一个名为 <strong>CompositeControl</strong> 的基类。因此，新的非数据绑定复合控件应该从该类派生而不是从 <strong>WebControl</strong> 派生。在开发控件方面，<strong>CompositeControl</strong> 的用法变动不大。您仍然需要替换 <strong>CreateChildControls</strong> 并按先前所述方式编码。那么 <strong>CompositeControl</strong> 的作用是什么？让我们先从其原型着手：</p>
<pre>public class CompositeControl :WebControl,
INamingContainer,
ICompositeControlDesignerAccessor
</pre>
<p>使用该类就无需再用 <strong>INamingContainer</strong> 装饰控件，但这实际上并不是很重要，因为接口只是一个标记并且不包含任何方法。更为重要的是，该类实现了一个名为 <strong>ICompositeControlDesignerAccessor</strong> 的全新接口。</p>
<pre>public interface ICompositeControlDesignerAccessor
{
void RecreateChildControls();
}
</pre>
<p>此接口由复合控件的标准设计器用于在设计时重建控件树。以下是 <strong>CompositeControl</strong> 中方法的默认实现过程。</p>
<pre>void ICompositeControlDesignerAccessor.RecreateChildControls()
{
base.ChildControlsCreated = false;
EnsureChildControls();
}
</pre>
<p>简言之，如果您从 <strong>CompositeControl</strong> 派生复合控件，就不会遇到设计时的故障，而且无需采用技巧和妙计就可以使控件在运行时和设计时都能正常运行。</p>
<p>要充分理解此接口的重要性，可试以寄存某 <strong>LabelTextBox</strong> 复合控件的示例页为例，并将其转换为设计模式。控件在运行时工作正常，但在设计时却不可见。</p>
<p><img alt="" src="http://i.msdn.microsoft.com/Aa479016.aspnetcontdev05(zh-cn,MSDN.10).gif"> </p>
<div><strong>图 5：只有复合控件从 CompositeControl 派生才对它们进行特殊的设计时处理</strong> </div>
<p>如果只是用 <strong>CompositeControl</strong> 替换 <strong>WebControl</strong>，则控件在运行时仍然保持正常工作，而在设计时也会运行良好。</p>
<p><img alt="" src="http://i.msdn.microsoft.com/Aa479016.aspnetcontdev06(zh-cn,MSDN.10).gif"> </p>
<div><strong>图 6：在设计时运行良好的复合控件</strong> </div>
<div><a href="http://msdn.microsoft.com/zh-cn/library/aa479016.aspx#mainSection"><img alt="" src="http://i.msdn.microsoft.com/Aa479016.arrow_px_up(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/aa479016.aspx#mainSection"><font color=#0033cc>返回页首</font></a> <br></div>
<h3 id=XSLTsection139121120120>生成数据绑定复合控件</h3>
<p>大多数复杂的服务器控件都已绑定数据（也可能已经模板化），并且由各种子控件构成。这些控件保留了一个构成项（通常为表的行或单元格）的列表。该列表在经过回发后会保存在视图状态中，并且从绑定数据生成或从视图状态重建。该控件还在视图状态中保存其构成项的数量，以便在页面中其他控件引起回发时可以正确重建表结构。我将用 <strong>DataGrid</strong> 控件举例说明。</p>
<p><strong>DataGrid</strong> 由一列行构成，每一行都代表绑定数据源中的一个记录。每个网格行都通过一个 <strong>DataGridRow</strong> 对象（从 <strong>TableRow</strong> 派生的一个类）表示。在各网格行创建完成并被添加到最终网格表时，诸如 <strong>ItemCreated</strong> 和 <strong>ItemDataBound</strong> 之类的相应事件将被引发至页面。当通过数据绑定创建 <strong>DataGrid</strong> 时，其行数由绑定项数和页面大小决定。如果带有 <strong>DataGrid</strong> 的页面回发会怎样？</p>
<p>这种情况下，如果是由 <strong>DataGrid</strong> 自身引起的回发（例如，用户单击以进行排序或标页），则新页面会再次通过数据绑定来呈现 <strong>DataGrid</strong>。这是显而易见的，因为 <strong>DataGrid</strong> 需要刷新数据进行显示。如果是主页回发，则情况就不同了，因为单击了页面上的另一个控件（例如某按钮）。这种情况下，<strong>DataGrid</strong> 不绑定到数据并且必须从视图状态进行重建。（如果禁用了视图状态，就是另外一种情况了，这时只能通过数据绑定显示网格。）</p>
<p>数据源不保存在视图状态中。作为复合控件，<strong>DataGrid</strong> 包含子控件，其中每个子控件都将自己的状态保存到视图状态并从视图状态恢复。<strong>DataGrid</strong> 只需跟踪在所有行和所包含控件从视图状态恢复之前它所必须重复执行的次数。此次数与所显示绑定项的数量一致，并且必须作为控件状态的一部分存储到视图状态中。在 ASP.NET 1.x 中，您必须自己学习并实现此模式。在 ASP.NET 2.0 中，从新类 <strong>CompositeDataBoundControl</strong> 派生您的复合控件就可以了。</p>
<p>让我们尝试使用一种显示可扩展数据绑定新闻标题行的网格类控件。在此过程中，我们将再度使用在前文中论及的 <strong>Headline</strong> 控件。</p>
<pre>public class HeadlineListEx :CompositeDataBoundControl
{
:
}
</pre>
<p><strong>HeadlineListEx</strong> 控件包含了一个收集了所有绑定数据项的 <strong>Items</strong> 集合属性。该集合为公共集合，并且可在与多数列表控件一起运行时通过编程方式填充。对典型数据绑定的支持是通过一对属性（<strong>DataTextField</strong> 和 <strong>DataTitleField</strong>）实现的。这两个属性表明了数据源中将用于填充新闻标题和文本的字段。<strong>Items</strong> 集合被保存到视图状态中。</p>
<p>要将 <strong>HeadlineListEx</strong> 控件转换为真正的复合控件，您首先需要从 <strong>CompositeDataBoundControl</strong> 将其派生出来，然后再替换 <strong>CreateChildControls</strong>。有意思的是，你会注意到 <strong>CreateChildControls</strong> 是重载方法。</p>
<pre>override int CreateChildControls()
override int CreateChildControls(IEnumerable data, bool dataBinding)
</pre>
<p>第一个重载方法替换了在 <strong>Control</strong> 类中定义的方法。第二个重载方法是每个复合控件都必须替换的一种抽象方法。实际上，复合控件的开发工作简化为两大主要任务：</p>
<ul>
    <li>
    <p>替换 <strong>CreateChildControls</strong>。</p>
    <li>
    <p>实现 <strong>Rows</strong> 集合属性以跟踪控件的所有构成项。</p>
    </li>
</ul>
<p><strong>Rows</strong> 属性不同于 <strong>Items</strong>，因为它不保存在视图状态中，且具有与请求相同的生存期，并引用帮助程序对象而不是绑定数据项。</p>
<pre>public virtual HeadlineRowCollection Rows
{
get
{
if (_rows == null)
_rows = new HeadlineRowCollection();
return _rows;
}
}
</pre>
<p><strong>Rows</strong> 集合在控件生成时填充。让我们看一下 <strong>CreateChildControls</strong> 的替换方法。该方法采用了两个参数：绑定项和一个布尔标记，其中布尔标记用于指明该控件是通过数据绑定创建还是通过视图状态创建。（请注意示例程序文件中的程序员注释使用的是英文，本文中将其译为中文是为了便于参考。）</p>
<pre>override int CreateChildControls(IEnumerable dataSource, bool dataBinding)
{
if (dataBinding)
{
string textField = DataTextField;
string titleField = DataTitleField;
if (dataSource != null)
{
foreach (object o in dataSource)
{
HeadlineItem elem = new HeadlineItem();
elem.Text = DataBinder.GetPropertyValue(o, textField, null);
elem.Title = DataBinder.GetPropertyValue(o, titleField, null);
Items.Add(elem);
}
}
}
// 开始生成控件层次结构
Table t = new Table();
Controls.Add(t);
Rows.Clear();
int itemCount = 0;
foreach(HeadlineItem item in Items)
{
HeadlineRowType type = HeadlineRowType.Simple;
HeadlineRow row = CreateHeadlineRow(t, type,
item, itemCount, dataBinding);
_rows.Add(row);
itemCount++;
}
return itemCount;
}
</pre>
<p>在数据绑定的情况下，首先要填充 Items 集合。遍历绑定集合，提取数据，然后填充 <strong>HeadlineItem</strong> 类的新建实例。接下来，遍历 Items 集合（该集合中可能包含以编程方式添加的附加项），并在控件中创建行。</p>
<pre>HeadlineRow CreateHeadlineRow(Table t, HeadlineRowType rowType,
HeadlineItem dataItem, int index, bool dataBinding)
{
// 为最外部表创建新行
HeadlineRow row = new HeadlineRow(rowType);
// 为子控件创建单元格
TableCell cell = new TableCell();
row.Cells.Add(cell);
Headline item = new Headline();
cell.Controls.Add(item);
// 此时引发 HeadlineRowCreated 事件
// 将此行添加到所创建的 HTML 表
t.Rows.Add(row);
// 处理数据对象绑定
if (dataBinding)
{
row.DataItem = dataItem;
Headline ctl = (Headline) cell.Controls[0];
ctl.Text = dataItem.Text;
ctl.Title = dataItem.Title;
// 此时引发 HeadlineRowDataBound 事件
}
return row;
}
</pre>
<p><strong>CreateHeadlineRow</strong> 方法会创建并返回 <strong>HeadlineRow</strong> 类（从 <strong>TableRow</strong> 派生而来）的一个实例。在这种情况下，此行会包含一个由 <strong>Headline</strong> 控件填充的单元格。在其他情况下，您可以更改此部分代码以根据需要添加多个单元格并相应填充内容。</p>
<p>重要的是，要将所需完成的任务分为两个不同的步骤：创建和数据绑定。首先，创建行的布局，引发行创建事件（如果有），并最后将其添加到父表中。接下来，如果要将控件绑定到数据，则设置对绑定数据敏感的子控件属性。完成操作后，则引发一个行数据绑定事件（如果有）。</p>
<p>请注意，该模式更准确描述了 ASP.NET 自带复合控件的内部体系结构。</p>
<p>可以使用以下代码来引发事件。</p>
<pre>HeadlineRowEventArgs e = new HeadlineRowEventArgs();
e.DataItem = dataItem;
e.RowIndex = index;
e.RowType = rowType;
e.Item = row;
OnHeadlineRowDataBound(e);
</pre>
<p>请注意，只在要引发数据绑定事件时才设置 <strong>DataItem</strong> 属性。事件数据结构被任意设置为以下形式。如果您认为有必要，尽可以对其进行更改。</p>
<pre>public class HeadlineRowEventArgs :EventArgs
{
public HeadlineItem DataItem;
public HeadlineRowType RowType;
public int RowIndex;
public HeadlineRow Item;
}
</pre>
<p>若要实际引发一个事件，通常的做法是使用一个如下定义的受保护方法。</p>
<pre>protected virtual void OnHeadlineRowDataBound(HeadlineRowEventArgs e)
{
if (HeadlineRowDataBound != null)
HeadlineRowDataBound(this, e);
}
</pre>
<p>若要声明此事件，可在 ASP.NET 2.0 中使用新的一般事件处理程序委托。</p>
<pre>public event EventHandler&lt;HeadlineRowEventArgs&gt; HeadlineRowDataBound;
</pre>
<p>在示例页中，一切均照常执行。您可在控件标记上定义处理程序并将某方法写入代码文件。示例如下。</p>
<pre>&lt;cc1:HeadlineListEx runat="server" ID="HeadlineListEx1"
DataTextField="notes" DataTitleField="lastname"
DataSourceID="MySource" OnHeadlineRowDataBound="HeadlineRowCreated" /&gt;
</pre>
<p><strong>HeadlineRowCreated</strong> 事件处理程序的代码显示如下。</p>
<pre>protected void HeadlineRowCreated(object sender, HeadlineRowEventArgs e)
{
if (e.DataItem.Title.Contains("Doe"))
e.Item.BackColor = Color.Red;
}
</pre>
<p><img alt="" src="http://i.msdn.microsoft.com/Aa479016.aspnetcontdev07(zh-cn,MSDN.10).gif"> </p>
<div><strong>图 7：运行中的 HeadlineListEx 控件</strong> </div>
<p>通过挂接数据绑定事件，所有含有 <strong>Doe</strong> 的项都将以红色背景呈现。</p>
<div><a href="http://msdn.microsoft.com/zh-cn/library/aa479016.aspx#mainSection"><img alt="" src="http://i.msdn.microsoft.com/Aa479016.arrow_px_up(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/aa479016.aspx#mainSection"><font color=#0033cc>返回页首</font></a> <br></div>
<h3 id=XSLTsection142121120120>结论</h3>
<p>复合控件是通过将其他控件聚合在某一公用 API 顶下创建而成的控件。复合控件将保留自己子控件的活动实例，并且不仅限于呈现这些实例。通过检查页面跟踪输出中的控件树部分，您就可以很容易看到这一点。使用复合控件可以带来几点好处，例如可以简化对事件和回发的处理。在 ASP.NET 1.x 中生成复杂的数据绑定控件有点棘手，需要您深入了解一些实现细节。在引进 <strong>CompositeDataBoundControl</strong> 基类的情况下，这种复杂性在 ASP.NET 中基本可以迎刃而解。最后，如果在 ASP.NET 2.0 中需要非数据绑定的复合控件，则可以使用 <strong>CompositeControl</strong> 基类。对于数据绑定复合控件，则可以改为考虑 <strong>CompositeDataBoundControl</strong>。无论是哪种情况，您都必须提供一个 <strong>CreateChildControls</strong> 的有效替换方法，这是所有复合控件的核心，用于创建子控件层次结构。</p>
<p>作者简介</p>
<p>Dino Esposito 是 <a id=ctl00_rs1_mainContentContainer_ctl02 onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00|ctl00_rs1_mainContentContainer_ctl02',this);" href="http://www.solidqualitylearning.com/"><font color=#0033cc>Solid Quality Learning</font></a> 顾问，并且是 "Programming Microsoft ASP.NET 2.0" (Microsoft Press, 2005)（英文）的作者。Dino 居住在意大利，经常就世界范围的行业事件发表评论。请通过 <a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#99;&#117;&#116;&#116;&#105;&#110;&#103;&#64;&#109;&#105;&#99;&#114;&#111;&#115;&#111;&#102;&#116;&#46;&#99;&#111;&#109;"><font color=#0033cc>cutting@microsoft.com</font></a> 或者加入博客 <a id=ctl00_rs1_mainContentContainer_ctl03 onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00|ctl00_rs1_mainContentContainer_ctl03',this);" href="http://weblogs.asp.net/despos"><font color=#0033cc>http://weblogs.asp.net/despos</font></a> 与其联系。</p>
<img src ="http://www.cnitblog.com/MartinYao/aggbug/52402.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/MartinYao/" target="_blank">玄铁剑</a> 2008-12-09 20:48 <a href="http://www.cnitblog.com/MartinYao/articles/52402.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>利用反射操作窗体</title><link>http://www.cnitblog.com/MartinYao/articles/52401.html</link><dc:creator>玄铁剑</dc:creator><author>玄铁剑</author><pubDate>Tue, 09 Dec 2008 12:31:00 GMT</pubDate><guid>http://www.cnitblog.com/MartinYao/articles/52401.html</guid><wfw:comment>http://www.cnitblog.com/MartinYao/comments/52401.html</wfw:comment><comments>http://www.cnitblog.com/MartinYao/articles/52401.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/MartinYao/comments/commentRss/52401.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/MartinYao/services/trackbacks/52401.html</trackback:ping><description><![CDATA[<div class=overview>
<div id="">
<p>John Dyer<br>Dallas Theological Seminary</p>
</div>
<div id="">
<p>适用于：<br>Microsoft Visual Studio 2005 及早期版本<br>ASP.NET 1.1<br>C# 编程语言<br>Visual Basic 编程语言</p>
</div>
<div id="">
<p><strong>摘要：</strong>使用反射以单行代码将业务对象绑定到 ASP.NET Web 窗体，从而降低复杂性并减少错误。（本文包含一些指向英文站点的链接。请注意，在示例文件中，程序员的注释使用的是英文，本文中将其译为中文是为了便于读者理解。）</p>
</div>
<div id="">
<p><a href="http://download.microsoft.com/download/0/6/7/067D02CE-35CE-4A57-9203-394A2A569C38/MSDNFormBinding.msi" target=_blank><u><font color=#0000ff>下载 MSDFormBinding.msi 示例文件</font></u></a>。</p>
</div>
</div>
<center><img height=6 src="http://www.chinaitpower.com/A-A-A/2005/07/17/20050717090746119867_1.gif" width=30 border=0></center>
<div style="HEIGHT: 18px"></div>
<h5 style="PADDING-TOP: 2px">本页内容</h5>
<table style="MARGIN-TOP: 7px; MARGIN-BOTTOM: 12px" cellSpacing=0 cellPadding=0 border=0>
    <tbody>
        <tr vAlign=top>
            <td><a href="http://www.microsoft.com/china/msdn/library/webservices/asp.net/aspformbinding.mspx#E1AA"><img height=9 hspace=4 src="http://www.chinaitpower.com/A-A-A/2005/07/17/20050717090746119865_2.gif" width=7 vspace=2 border=0></a></td>
            <td class=onThisPage><a href="http://www.microsoft.com/china/msdn/library/webservices/asp.net/aspformbinding.mspx#E1AA"><font color=#0000ff><u>引言</u></font></a></td>
        </tr>
        <tr vAlign=top>
            <td><a href="http://www.microsoft.com/china/msdn/library/webservices/asp.net/aspformbinding.mspx#EXAA"><font color=#0000ff><u><img height=9 hspace=4 src="http://www.chinaitpower.com/A-A-A/2005/07/17/20050717090746119865_2.gif" width=7 vspace=2 border=0></u></font></a></td>
            <td class=onThisPage><a href="http://www.microsoft.com/china/msdn/library/webservices/asp.net/aspformbinding.mspx#EXAA"><font color=#0000ff><u>简化和缩短窗体代码</u></font></a></td>
        </tr>
        <tr vAlign=top>
            <td><a href="http://www.microsoft.com/china/msdn/library/webservices/asp.net/aspformbinding.mspx#EUAA"><font color=#0000ff><u><img height=9 hspace=4 src="http://www.chinaitpower.com/A-A-A/2005/07/17/20050717090746119865_2.gif" width=7 vspace=2 border=0></u></font></a></td>
            <td class=onThisPage><a href="http://www.microsoft.com/china/msdn/library/webservices/asp.net/aspformbinding.mspx#EUAA"><font color=#0000ff><u>开始：从反射中检索属性列表</u></font></a></td>
        </tr>
        <tr vAlign=top>
            <td><a href="http://www.microsoft.com/china/msdn/library/webservices/asp.net/aspformbinding.mspx#ERAA"><font color=#0000ff><u><img height=9 hspace=4 src="http://www.chinaitpower.com/A-A-A/2005/07/17/20050717090746119865_2.gif" width=7 vspace=2 border=0></u></font></a></td>
            <td class=onThisPage><a href="http://www.microsoft.com/china/msdn/library/webservices/asp.net/aspformbinding.mspx#ERAA"><font color=#0000ff><u>将对象属性值绑定到控件</u></font></a></td>
        </tr>
        <tr vAlign=top>
            <td><a href="http://www.microsoft.com/china/msdn/library/webservices/asp.net/aspformbinding.mspx#EOAA"><font color=#0000ff><u><img height=9 hspace=4 src="http://www.chinaitpower.com/A-A-A/2005/07/17/20050717090746119865_2.gif" width=7 vspace=2 border=0></u></font></a></td>
            <td class=onThisPage><a href="http://www.microsoft.com/china/msdn/library/webservices/asp.net/aspformbinding.mspx#EOAA"><font color=#0000ff><u>用已知属性设置未知控件的值</u></font></a></td>
        </tr>
        <tr vAlign=top>
            <td><a href="http://www.microsoft.com/china/msdn/library/webservices/asp.net/aspformbinding.mspx#ELAA"><font color=#0000ff><u><img height=9 hspace=4 src="http://www.chinaitpower.com/A-A-A/2005/07/17/20050717090746119865_2.gif" width=7 vspace=2 border=0></u></font></a></td>
            <td class=onThisPage><a href="http://www.microsoft.com/china/msdn/library/webservices/asp.net/aspformbinding.mspx#ELAA"><font color=#0000ff><u>反转过程：BindControlsToObject</u></font></a></td>
        </tr>
        <tr vAlign=top>
            <td><a href="http://www.microsoft.com/china/msdn/library/webservices/asp.net/aspformbinding.mspx#EIAA"><font color=#0000ff><u><img height=9 hspace=4 src="http://www.chinaitpower.com/A-A-A/2005/07/17/20050717090746119865_2.gif" width=7 vspace=2 border=0></u></font></a></td>
            <td class=onThisPage><a href="http://www.microsoft.com/china/msdn/library/webservices/asp.net/aspformbinding.mspx#EIAA"><font color=#0000ff><u>性能和 FormBinding 方案的扩展</u></font></a></td>
        </tr>
        <tr vAlign=top>
            <td><a href="http://www.microsoft.com/china/msdn/library/webservices/asp.net/aspformbinding.mspx#EFAA"><font color=#0000ff><u><img height=9 hspace=4 src="http://www.chinaitpower.com/A-A-A/2005/07/17/20050717090746119865_2.gif" width=7 vspace=2 border=0></u></font></a></td>
            <td class=onThisPage><a href="http://www.microsoft.com/china/msdn/library/webservices/asp.net/aspformbinding.mspx#EFAA"><font color=#0000ff><u>结论</u></font></a></td>
        </tr>
        <tr vAlign=top>
            <td><a href="http://www.microsoft.com/china/msdn/library/webservices/asp.net/aspformbinding.mspx#EEAA"><font color=#0000ff><u><img height=9 hspace=4 src="http://www.chinaitpower.com/A-A-A/2005/07/17/20050717090746119865_2.gif" width=7 vspace=2 border=0></u></font></a></td>
            <td class=onThisPage><a href="http://www.microsoft.com/china/msdn/library/webservices/asp.net/aspformbinding.mspx#EEAA"><u><font color=#0000ff>参考资料</font></u></a></td>
        </tr>
    </tbody>
</table>
<div id=""><a name=E1AA></a>
<h2>引言</h2>
<div id="">
<p>在 Web 开发人员的最常见任务之中，有一项任务是他们要反复执行的：建立更新数据库表的简单窗体。我们将创建一个列表页面和一个窗体页面，列表页面中以表格形式显示记录，窗体页面中带有用于各个数据库字段的适当的窗体控件。许多开发人员还使用表示数据库表的业务对象将代码组织到分为多层的设计中。如果以业务对象 (<strong>Document</strong>) 来表示数据库表 (<strong>Documents</strong>)，许多窗体的代码看上去将如下所示：</p>
</div>
<pre class=codeSample>&lt;script runat="server"&gt;
protected void Page_Load(Object Src, EventArgs E) {
if (!IsPostBack) {
Document document =
Documents.GetDocument(Request.QueryString["DocumentID"]);
Title.Text = document.Title;
Active.Checked = document.Active;
CreatedDate.Text = document.CreatedDate.ToString();
AuthorID.FindByValue(document.AuthorID.ToString()).Selected =
true;
// ... 等等
HtmlBody.Text = document.HtmlBody;
}
}
protected void SaveButton_Click(Object Src, EventArgs E) {
Document document =
Documents.GetDocument(Request.QueryString["DocumentID"]);
document.Title = Title.Text;
document.Active = Active.Checked;
document.CreatedDate = Convert.ToDateTime(CreatedDate.Text);
document.AuthorID = Convert.ToInt32(AuthorID.SelectedItem.Value);
// ... 等等
document.HtmlBody = HtmlBody.Text;
Documents.Update(document);
}
&lt;/script&gt;
</pre>
</div>
<div id="">
<div style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 10px"><a href="http://www.microsoft.com/china/msdn/library/webservices/asp.net/aspformbinding.mspx#top"><img height=9 src="http://www.chinaitpower.com/A-A-A/2005/07/17/20050717090746119860_11.gif" width=7 border=0></a><a class=topOfPage href="http://www.microsoft.com/china/msdn/library/webservices/asp.net/aspformbinding.mspx#top"><u><font color=#0000ff>返回页首</font></u></a></div>
<a name=EXAA></a>
<h2>简化和缩短窗体代码</h2>
<div id="">
<p>在以上代码中，对每个控件进行显式转换，并将其设置为窗体控件的正确属性。根据属性和窗体控件的数量，这部分代码可能会变长并难以管理。代码还应包含类型转换的错误更正和 <strong>ListControl</strong>，这将进一步增加复杂性。即使窗体是由代码生成工具（例如 Eric J. Smith 的优秀的 <a href="http://www.ericjsmith.net/codesmith/" target=_blank><u><font color=#0000ff>CodeSmith</font></u></a>）生成的，当需要任何自定义逻辑关系时，很容易引入错误。</p>
</div>
<div id="">
<p>使用反射，可以仅使用单行代码便将业务对象的所有属性绑定到相应的窗体控件，从而减少代码的行数并增强可读性。完成反射系统的建立后，以上代码将简化为：</p>
</div>
<pre class=codeSample>protected void Page_Load(Object Src, EventArgs E) {
if (!IsPostBack) {
Document document =
Documents.GetDocument(Request.QueryString["DocumentID"]);
FormBinding.BindObjectToControls(document);
}
}
protected void Save_Click(Object Src, EventArgs E) {
Document document =
Documents.GetDocument(Request.QueryString["DocumentID"]);
FormBinding.BindControlsToObject(document);
Documents.Update(document);
}
</pre>
<div id="">
<p>此代码可用于所有标准的 ASP.NET 控件（<strong>TextBox</strong>、<strong>DropDownList</strong>、<strong>CheckBox</strong> 等）和许多第三方控件（例如 <a href="http://www.freetextbox.com/" target=_blank><u><font color=#0000ff>Free TextBox</font></u></a> 和 <a href="http://www.eworldui.net/" target=_blank><u><font color=#0000ff>Calendar Popup</font></u></a>）。无论有多少业务对象属性和窗体控件，这一行代码都能提供所需的全部功能，只要窗体控件的 ID 与业务对象属性名相匹配。</p>
</div>
</div>
<div id="">
<div style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 10px"><a href="http://www.microsoft.com/china/msdn/library/webservices/asp.net/aspformbinding.mspx#top"><img height=9 src="http://www.chinaitpower.com/A-A-A/2005/07/17/20050717090746119860_11.gif" width=7 border=0></a><a class=topOfPage href="http://www.microsoft.com/china/msdn/library/webservices/asp.net/aspformbinding.mspx#top"><u><font color=#0000ff>返回页首</font></u></a></div>
<a name=EUAA></a>
<h2>开始：从反射中检索属性列表</h2>
<div id="">
<p>首先，我们需要检查业务对象的属性，并查找与业务对象属性名具有相同 ID 的 ASP.NET 控件。以下代码构成了绑定查找的基础：</p>
</div>
<pre class=codeSample>public class FormBinding {
public static void BindObjectToControls(object obj,
Control container) {
if (obj == null) return;
Type objType = obj.GetType();
PropertyInfo[] objPropertiesArray =
objType.GetProperties();
foreach (PropertyInfo objProperty in objPropertiesArray) {
Control control =
container.FindControl(objProperty.Name);
if (control != null) {
// 处理控件 ...
}
}
}
}
</pre>
<div id="">
<p>在以上代码中，方法 <strong>BindObjectsToControls</strong> 接受了业务对象 obj 和一个容器控件。容器控件通常是当前 Web 窗体的 <strong>Page</strong> 对象。如果所用版本是会在运行时更改控件嵌套顺序的 ASP.NET 1.x MasterPages，您将需要指定窗体控件所在的 <strong>Content</strong> 控件。这是在 ASP.NET 1.x 中，<strong>FindControl</strong> 方法对嵌套控件和命名容器的处理方式导致的。</p>
</div>
<div id="">
<p>在以上代码中，我们获取了业务对象的 <strong>Type</strong>，然后使用该 <strong>Type</strong> 来获取 <strong>PropertyInfo</strong> 对象的数组。每个 <strong>PropertyInfo</strong> 对象都包含关于业务对象属性以及从业务对象获取和设置值的能力的信息。我们使用 foreach 循环检查具有与业务对象属性名 (<strong>PropertyInfo.Name</strong>) 对应的 ID 属性的 ASP.NET 控件的容器。如果找到控件，则尝试将属性值绑定到该控件。</p>
</div>
</div>
<div id="">
<div style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 10px"><a href="http://www.microsoft.com/china/msdn/library/webservices/asp.net/aspformbinding.mspx#top"><img height=9 src="http://www.chinaitpower.com/A-A-A/2005/07/17/20050717090746119860_11.gif" width=7 border=0></a><a class=topOfPage href="http://www.microsoft.com/china/msdn/library/webservices/asp.net/aspformbinding.mspx#top"><u><font color=#0000ff>返回页首</font></u></a></div>
<a name=ERAA></a>
<h2>将对象属性值绑定到控件</h2>
<div id="">
<p>过程中的大部分操作是在此阶段执行的。我们需要用对象的属性值来填充找到的控件。一种实现方法是为每种控件类型创建一个 if ... else 语句。派生自 <strong>ListControl</strong>（<strong>DropDownList</strong>、<strong>RadioButtonList</strong>、<strong>CheckBoxList</strong> 和 <strong>ListBox</strong>）的所有控件都具有可以统一访问的公用接口，所以可以将它们编组在一起。如果找到的控件是 <strong>ListControl</strong>，我们可以将其作为 <strong>ListControl</strong> 进行转换，然后设置选定项：</p>
</div>
<pre class=codeSample>Control control = container.FindControl(objProperty.Name);
if (control != null) {
if (control is ListControl) {
ListControl listControl = (ListControl) control;
string propertyValue = objProperty.GetValue(obj,
null).ToString();
ListItem listItem =
listControl.Items.FindByValue(propertyValue);
if (listItem != null) listItem.Selected = true;
} else {
// 处理其他控件类型
}
}
</pre>
<div id="">
<p>不幸的是，其他控件类型并不从父类中派生。以下几个公用控件都具有 <strong>.Text</strong> 字符串属性：<strong>TextBox</strong>、<strong>Literal</strong> 和 <strong>Label</strong>。但该属性不是从公用父类中派生出来的，所以需要分别转换每种控件类型。我们还需要转换其他控件类型，例如 <strong>Calendar</strong> 控件，以便使用适当的属性（在 <strong>Calendar</strong> 的例子中，是 <strong>SelectedDate</strong> 属性）。要包含所有标准的 ASP.NET 窗体控件，并访问窗体控件的正确属性并不需要太多的代码行。</p>
</div>
<pre class=codeSample>if (control is ListControl) {
ListControl listControl = (ListControl) control;
string propertyValue = objProperty.GetValue(obj,
null).ToString();
ListItem listItem = listControl.Items.FindByValue(propertyValue);
if (listItem != null) listItem.Selected = true;
} else if (control is CheckBox) {
if (objProperty.PropertyType == typeof(bool))
((CheckBox) control).Checked = (bool)
objProperty.GetValue(obj, null);
} else if (control is Calendar) {
if (objProperty.PropertyType == typeof(DateTime))
((Calendar) control).SelectedDate = (DateTime)
objProperty.GetValue(obj, null);
} else if (control is TextBox) {
((TextBox) control).Text = objProperty.GetValue(obj,
null).ToString();
} else if (control is Literal)(
//... 等等。还可用于标签等属性。
}
</pre>
<div id="">
<p>此方法完整地涵盖了标准的 ASP.NET 1.x 控件。从这个角度来看，我们拥有了功能齐全的 <strong>BindObjectToControls</strong> 方法。但在起作用的同时，此方法的应用范围会受到限制，因为它仅考虑内置的 ASP.NET 1.x 控件。如果要支持新的 ASP.NET 2.0 控件，或者要使用任何第三方控件，我们必须在 <strong>FormBinding</strong> 项目中引用控件的程序集，并将控件类型添加到 if ... else 列表。 </p>
</div>
<div id="">
<p>此问题的解决方案是第二次使用反射，以查看各个控件的属性，并找出控件是否具有与业务对象的属性类型对应的属性类型。</p>
</div>
</div>
<div id="">
<div style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 10px"><a href="http://www.microsoft.com/china/msdn/library/webservices/asp.net/aspformbinding.mspx#top"><img height=9 src="http://www.chinaitpower.com/A-A-A/2005/07/17/20050717090746119860_11.gif" width=7 border=0></a><a class=topOfPage href="http://www.microsoft.com/china/msdn/library/webservices/asp.net/aspformbinding.mspx#top"><u><font color=#0000ff>返回页首</font></u></a></div>
<a name=EOAA></a>
<h2>用已知属性设置未知控件的值</h2>
<div id="">
<p>如上所述，有些控件共享字符串属性 <strong>.Text</strong>，大多数窗体控件以实质相同的方式使用此属性。该属性用于获取和设置用户输入的数据。有大量控件还使用了其他一些公用属性和属性类型。以下是这些属性中的一些：称为 <strong>.SelectedDate</strong> 的 <strong>DateTime</strong> 属性，它在许多日历和日期选取器控件中使用；称为 <strong>.Checked</strong> 的布尔属性，它在布尔型控件中使用；称为 <strong>.Value</strong> 的字符串属性，它常见于隐藏控件。这四个属性（<strong>string Text</strong>、<strong>string Value</strong>、<strong>bool Checked</strong> 和 <strong>DateTime SelectedDate</strong>）是最常见的控件属性。如果可以将系统设计成无论何种控件类型，都绑定到这些属性，那么我们的绑定方法将适用于使用那四个属性的任何控件。 </p>
</div>
<div id="">
<p>在以下代码中，我们将第二次使用反射（这一次是对窗体控件使用，而不是对业务对象使用），以确定它是否具有任何常用属性。如果有，则尝试将业务对象的属性值设置为控件的属性。作为示例，我们将对整个 PropertyInfo 数组进行迭代，并查找称为 <strong>.Text</strong> 的字符串属性。如果控件具有该属性，则将数据从业务对象发送到该控件的属性。</p>
</div>
<pre class=codeSample>if (control is ListControl) {
// ...
} else {
// 获取控件的类型和属性
//
Type controlType = control.GetType();
PropertyInfo[] controlPropertiesArray =
controlType.GetProperties();
// 查找 .Text 属性
//
foreach (PropertyInfo controlProperty
in controlPropertiesArray) {
if (controlPropertiesArray.Name == "Text" &amp;&amp;
controlPropertiesArray.PropertyType == typeof(String)) {
// 设置控件的 .Text 属性
//
controlProperty.SetValue(control,
(String) objProperty.GetValue(obj, null), null);
}
}
}
</pre>
<div id="">
<p>如果找到 <strong>.Text</strong>，则使用 <strong>PropertyInfo</strong> 类的 <strong>GetValue</strong> 方法从业务对象的属性中检索值。然后，使用控件的 <strong>.Text</strong> 属性的 <strong>SetValue</strong> 方法。在此，我们还使用 Type 命令将控件的属性设置为 typeof(String)，并使用 (String) 符号显式转换来自属性的值。 </p>
</div>
<div id="">
<p>为了使 <strong>BindObjectToControls</strong> 方法完整，我们还需要处理其他公用属性，即 <strong>.Checked</strong>、<strong>.SelectedDate</strong> 和 <strong>.Value</strong>。在以下代码中，我们将控件属性搜索打包到称为 <strong>FindAndSetControlProperty</strong> 的辅助方法中，以简化代码。</p>
</div>
<pre class=codeSample>if (control is ListControl) {
// ...
} else {
// 获取控件的属性
//
Type controlType = control.GetType();
PropertyInfo[] controlPropertiesArray =
controlType.GetProperties();
bool success = false;
success = FindAndSetControlProperty(obj,
objProperty, control, controlPropertiesArray,
"Checked", typeof(bool) );
if (!success)
success = FindAndSetControlProperty(obj,
objProperty, control, controlPropertiesArray,
"SelectedDate", typeof(DateTime) );
if (!success)
success = FindAndSetControlProperty(obj,
objProperty, control, controlPropertiesArray,
"Value", typeof(String) );
if (!success)
success = FindAndSetControlProperty(obj,
objProperty, control, controlPropertiesArray,
"Text", typeof(String) );
}
private static void FindAndSetControlProperty(object obj,
PropertyInfo objProperty, Control control,
PropertyInfo[] controlPropertiesArray, string propertyName,
Type type) {
// 在整个控件属性中进行迭代
foreach (PropertyInfo controlProperty in
controlPropertiesArray) {
// 检查匹配的名称和类型
if (controlPropertiesArray.Name == "Text" &amp;&amp;
controlPropertiesArray.PropertyType == typeof(String)) {
// 将控件的属性设置为
// 业务对象属性值
controlProperty.SetValue(control,
Convert.ChangeType(
objProperty.GetValue(obj, null), type) , null);
return true;
}
}
return false;
}
</pre>
<div id="">
<p>以上属性检查的顺序很重要，因为有些控件具有以上属性中的多个，但我们只想设置一个。例如，<strong>CheckBox</strong> 控件既有 <strong>.Text</strong> 属性也有 <strong>.Checked</strong> 属性。在此示例中，我们希望使用 <strong>.Checked </strong>属性而不是 <strong>.Text</strong> 属性，所以将 <strong>.Checked</strong> 放在属性搜索顺序的首位。任何情况下，如果找到具有正确名称和类型的控件属性，则尝试将控件的属性设置为业务对象属性的值。</p>
</div>
<div id="">
<p>从这个角度来看，我们拥有了功能齐全的 <strong>BindObjectToControls</strong> 方法。利用该方法，我们可以在 ASPX 窗体上的任何地方，使用任何类和控件的任意组合进行调用，而这确实有效。现在，我们需要创建在提交窗体时进行反转的方法。我们需要从表示用户输入的控件中检索新值，而不是将控件属性的值设置为业务对象的值。 </p>
</div>
</div>
<div id="">
<div style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 10px"><a href="http://www.microsoft.com/china/msdn/library/webservices/asp.net/aspformbinding.mspx#top"><img height=9 src="http://www.chinaitpower.com/A-A-A/2005/07/17/20050717090746119860_11.gif" width=7 border=0></a><a class=topOfPage href="http://www.microsoft.com/china/msdn/library/webservices/asp.net/aspformbinding.mspx#top"><u><font color=#0000ff>返回页首</font></u></a></div>
<a name=ELAA></a>
<h2>反转过程：BindControlsToObject</h2>
<div id="">
<p>在 <strong>BindControlsToObject</strong> 方法中，我们将以同样的方式开始，即从业务对象中检索属性的列表，然后使用 <strong>FindControl</strong> 方法找到具有与对象属性相匹配的 ID 的控件。如果找到控件，则检索值并将该值返回给业务对象。此部分还将包含 <strong>ListControl</strong> 的单独代码，因为这些控件具有公用接口。我们将使用另一种辅助方法来搜索并检索控件中的值，然后将该值返回给业务对象。</p>
</div>
<pre class=codeSample>public static void BindControlsToObject(object obj,
Control container) {
Type objType = obj.GetType();
PropertyInfo[] objPropertiesArray = objType.GetProperties();
foreach (PropertyInfo objProperty in objPropertiesArray) {
if (control is ListControl) {
ListControl listControl = (ListControl) control;
if (listControl.SelectedItem != null)
objProperty.SetValue(obj,
Convert.ChangeType(list.SelectedItem.Value,
objProperty.PropertyType), null);
} else {
// 获取控件的属性
//
Type controlType = control.GetType();
PropertyInfo[] controlPropertiesArray =
controlType.GetProperties();
bool success = false;
success = FindAndGetControlProperty(obj,
objProperty, control, controlPropertiesArray,
"Checked", typeof(bool) );
if (!success)
success = FindAndGetControlProperty(obj,
objProperty, control, controlPropertiesArray,
"SelectedDate", typeof(DateTime) );
if (!success)
success = FindAndGetControlProperty(obj,
objProperty, control, controlPropertiesArray,
"Value", typeof(String) );
if (!success)
success = FindAndGetControlProperty(obj,
objProperty, control, controlPropertiesArray,
"Text", typeof(String) );
}
}
}
private static void FindAndGetControlProperty(object obj,
PropertyInfo objProperty, Control control, PropertyInfo[]
controlPropertiesArray, string propertyName, Type type) {
// 在整个控件属性中进行迭代
foreach (PropertyInfo controlProperty in
controlPropertiesArray) {
// 检查匹配的名称和类型
if (controlPropertiesArray.Name == "Text" &amp;&amp;
controlPropertiesArray.PropertyType == typeof(String)) {
// 将控件的属性设置为
// 业务对象属性值
try {
objProperty.SetValue(obj,
Convert.ChangeType(
controlProperty.GetValue(control, null),
objProperty.PropertyType) , null);
return true;
} catch {
// 无法将来自窗体控件
// 的数据转换为
// objProperty.PropertyType
return false;
}
}
}
return true;
}
</pre>
<div id="">
<p>完成这两种方法后，我们的窗体语法将得到简化，如以上<a href="http://msdn.microsoft.com/library/en-us/dnaspp/html/aspformbinding.asp?frame=true#aspformbinding_topic2" target=_blank><u><font color=#0000ff>简化和缩短窗体代码</font></u></a>中所述。每个属性和控件的类型转换与错误更正都是自动进行的。这两种方法（<strong>BindObjectToControls</strong> 和 <strong>BindControlsToObject</strong>）为开发人员创建窗体提供了很大的灵活性。它们还可以用于处理以下这些常见方案： </p>
</div>
<table cellSpacing=0 cellPadding=0 border=0>
    <tbody>
        <tr>
            <td class=listBullet vAlign=top>&#8226;</td>
            <td class=listItem>
            <div id="">
            <p>如果将新属性添加到业务对象，并且需要在窗体上访问该新属性，那么开发人员只需将控件添加到页面，并将控件的 ID 设置为新属性的名称，<strong>FormBinding</strong> 方法将处理剩下的一切。 </p>
            </div>
            </td>
        </tr>
        <tr>
            <td class=listBullet vAlign=top>&#8226;</td>
            <td class=listItem>
            <div id="">
            <p>如果开发人员需要更改用于特定属性的控件的类型，例如从 <strong>TextBox</strong> 更改为第三方的 HTML 编辑器控件，他/她仅需要确保新控件具有以上属性之一（例如 <strong>.Text</strong> ），窗体将以与之前完全一致的方式进行工作。 </p>
            </div>
            </td>
        </tr>
        <tr>
            <td class=listBullet vAlign=top>&#8226;</td>
            <td class=listItem>
            <div id="">
            <p>全部使用 <strong>TextBox</strong> 控件也可以快速生成窗体，但输入仍将转换为适用于业务对象属性的正确类型。例如，可以用 <strong>TextBox</strong> 控件来代替 <strong>Calendar</strong> 控件或第三方的日期选取器控件。只要用户输入 DateTime 字符串作为值，便会将 <strong>TextBox</strong> 的 <strong>.Text</strong> 属性中的值转换为 DateTime，就如同它是日历控件上的 <strong>SelectedDate</strong> 属性一样。如果以后将 <strong>TextBox</strong> 更改为日期选取器控件，逻辑关系将保持不变。 </p>
            </div>
            </td>
        </tr>
        <tr>
            <td class=listBullet vAlign=top>&#8226;</td>
            <td class=listItem>
            <div id="">
            <p>通过将所有控件更改为 <strong>Literal</strong> 控件，开发人员还可以快速创建&#8220;视图&#8221;页面。<strong>Literal</strong> 的 <strong>.Text</strong> 属性将被设置为业务对象属性的值，就如同它是 <strong>TextBox</strong> 一样。 </p>
            </div>
            </td>
        </tr>
        <tr>
            <td class=listBullet vAlign=top>&#8226;</td>
            <td class=listItem>
            <div id="">
            <p>在实际方案中，窗体还包含其他数据类型和自定义配置。用于处理这些特定操作的代码可以放置在对 <strong>BindObjectToControls </strong>和 <strong>BindControlsToObject</strong> 的调用之后。 </p>
            </div>
            </td>
        </tr>
    </tbody>
</table>
</div>
<div id="">
<div style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 10px"><a href="http://www.microsoft.com/china/msdn/library/webservices/asp.net/aspformbinding.mspx#top"><img height=9 src="http://www.chinaitpower.com/A-A-A/2005/07/17/20050717090746119860_11.gif" width=7 border=0></a><a class=topOfPage href="http://www.microsoft.com/china/msdn/library/webservices/asp.net/aspformbinding.mspx#top"><u><font color=#0000ff>返回页首</font></u></a></div>
<a name=EIAA></a>
<h2>性能和 FormBinding 方案的扩展</h2>
<div id="">
<p>有些开发人员可能想知道，使用反射引起的性能下降是否值得。在我的测试中，使用了具有七种属性（int DocumentID、bool Active、DateTime Created、int CategoryID、String Title、string Author 和 String htmlText）的对象，<strong>BindObjectToControls</strong> 用时约 1/3 毫秒，<strong>BindControlsToObject</strong> 用时大约 1 毫秒。这些值是通过循环运行 1000 次 <strong>BindObjectToControls</strong> 和 <strong>BindControlsToObject</strong> 方法得到的。对于常见的&#8220;添加&#8221;和&#8220;编辑&#8221;窗体方案，这样的性能应不会引起任何重大的问题，而且确实能够提高开发速度和灵活性。 </p>
</div>
<div id="">
<p>尽管此方法几乎适用于每种窗体，但有时可能需要修改以上代码。在某些方案中，开发人员要使用的控件可能并不使用以上属性之一作为其主要接口。在此情形中，需要更新 <strong>FormBinding</strong> 方法，以包括该属性和类型。 </p>
</div>
</div>
<div id="">
<div style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 10px"><a href="http://www.microsoft.com/china/msdn/library/webservices/asp.net/aspformbinding.mspx#top"><img height=9 src="http://www.chinaitpower.com/A-A-A/2005/07/17/20050717090746119860_11.gif" width=7 border=0></a><a class=topOfPage href="http://www.microsoft.com/china/msdn/library/webservices/asp.net/aspformbinding.mspx#top"><u><font color=#0000ff>返回页首</font></u></a></div>
<a name=EFAA></a>
<h2>结论</h2>
<div id="">
<p>这两种 <strong>FormBinding</strong> 方法（<strong>BindObjectToControls</strong> 和 <strong>BindControlsToObject</strong>）可用于极大地简化窗体代码，并为 ASP.NET 窗体的开发提供了最大的灵活性。对它们的使用使我获益良多，希望您的团队同样能够从中受益。</p>
</div>
</div>
<div id="">
<div style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 10px"><a href="http://www.microsoft.com/china/msdn/library/webservices/asp.net/aspformbinding.mspx#top"><img height=9 src="http://www.chinaitpower.com/A-A-A/2005/07/17/20050717090746119860_11.gif" width=7 border=0></a><a class=topOfPage href="http://www.microsoft.com/china/msdn/library/webservices/asp.net/aspformbinding.mspx#top"><u><font color=#0000ff>返回页首</font></u></a></div>
<a name=EEAA></a>
<h2>参考资料</h2>
<table cellSpacing=0 cellPadding=0 border=0>
    <tbody>
        <tr>
            <td class=listBullet vAlign=top>&#8226;</td>
            <td class=listItem>
            <div id="">
            <p><a href="http://shopping.msn.com/search/detail.aspx?pcId=12237&amp;prodId=734690&amp;ptnrid=141&amp;ptnrdata=0" target=_blank><u><font color=#0000ff>ASP.NET Unleashed</font></u></a></p>
            </div>
            </td>
        </tr>
        <tr>
            <td class=listBullet vAlign=top>&#8226;</td>
            <td class=listItem>
            <div id="">
            <p><a href="http://shopping.msn.com/search/detail.aspx?pcId=4650&amp;prodId=1627167&amp;ptnrid=141&amp;ptnrdata=0" target=_blank><u><font color=#0000ff>Programming Microsoft ASP.NET</font></u></a></p>
            </div>
            </td>
        </tr>
    </tbody>
</table>
</div>
<div id="">
<p><strong>作者简介</strong></p>
</div>
<div id="">
<p>John Dyer 是 <a href="http://www.dts.edu/" target=_blank><u><font color=#0000ff>Dallas Theological Seminary</font></u></a> 的顶级 Web 开发人员，负责指导他们富有盛誉的联机培训计划，该计划建立在 <a href="http://www.telligentsystems.com/" target=_blank><u><font color=#0000ff>Telligent System</font></u></a> 的 Community Server 之上。他还是广泛使用的 ASP.NET HTML 编辑器 <a href="http://www.freetextbox.com/" target=_blank><u><font color=#0000ff>FreeTextBox</font></u></a> 的作者，该控件为除他之外的很多人带来了经济收益。在业余时间，他在 Dallas 神学院攻读神学硕士，并在准备 2005 年 1 月 1 日的婚礼。</p>
</div>
<img src ="http://www.cnitblog.com/MartinYao/aggbug/52401.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/MartinYao/" target="_blank">玄铁剑</a> 2008-12-09 20:31 <a href="http://www.cnitblog.com/MartinYao/articles/52401.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>EnhancedGridView.Pager[0 1 2 3..]</title><link>http://www.cnitblog.com/MartinYao/articles/51911.html</link><dc:creator>玄铁剑</dc:creator><author>玄铁剑</author><pubDate>Wed, 26 Nov 2008 13:56:00 GMT</pubDate><guid>http://www.cnitblog.com/MartinYao/articles/51911.html</guid><wfw:comment>http://www.cnitblog.com/MartinYao/comments/51911.html</wfw:comment><comments>http://www.cnitblog.com/MartinYao/articles/51911.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/MartinYao/comments/commentRss/51911.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/MartinYao/services/trackbacks/51911.html</trackback:ping><description><![CDATA[<p>using System;<br>using System.Collections.Generic;<br>using System.ComponentModel;<br>using System.Text;<br>using System.Web;<br>using System.Web.UI;<br>using System.Web.UI.WebControls;</p>
<p>namespace JustinsWebControls<br>{<br>&nbsp;&nbsp;&nbsp; /// &lt;summary&gt;<br>&nbsp;&nbsp;&nbsp; /// Enhanced version of the GridView control. <br>&nbsp;&nbsp;&nbsp; /// Supports: Column sort direction images, row selecting/highlighting, prev/numeric/next paging.<br>&nbsp;&nbsp;&nbsp; /// &lt;/summary&gt;<br>&nbsp;&nbsp;&nbsp; [DefaultProperty("SelectedValue")]<br>&nbsp;&nbsp;&nbsp; [ToolboxData("&lt;{0}:GridView runat=server&gt;&lt;/{0}:GridView&gt;")]<br>&nbsp;&nbsp;&nbsp; public class GridView : System.Web.UI.WebControls.GridView<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// &lt;summary&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// Color of the row being hovered over.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// &lt;/summary&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public string HighlightColor<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; get<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (ViewState["highlightColor"] == null)<br>&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; ViewState["highlightColor"] = false;<br>&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; return (string)ViewState["highlightColor"];<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; set<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ViewState["highlightColor"] = value;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// &lt;summary&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// Turns the selection highlighting on or off.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// &lt;/summary&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public bool EnableSelection<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; get<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (ViewState["enableSelection"] == null)<br>&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; ViewState["enableSelection"] = false;<br>&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; return (bool)ViewState["enableSelection"];<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; set<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ViewState["enableSelection"] = value;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// &lt;summary&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// Turns on JustinsWeb sanctioned pager.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// &lt;/summary&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public bool EnableNextPrevNumericPager<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; get<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (ViewState["enableJustinsWebPager"] == null)<br>&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; ViewState["enableJustinsWebPager"] = false;<br>&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; return (bool)ViewState["enableJustinsWebPager"];<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; set<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.AllowPaging = value;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ViewState["enableJustinsWebPager"] = value;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// &lt;summary&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// Get/Set alternative to the inherited SortDirection field that is always accurate.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// &lt;/summary&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public SortDirection SortDirectionAlt<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; get<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (ViewState["sortDirection"] == null)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ViewState["sortDirection"] = SortDirection.Ascending;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return (SortDirection)ViewState["sortDirection"];<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; set<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ViewState["sortDirection"] = value;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// &lt;summary&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// Get/Set alternative to the inherited SortExpression field that is always accurate.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// &lt;/summary&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public string SortExpressionAlt<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; get<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (ViewState["sortExpressionAlt"] == null)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ViewState["sortExpressionAlt"] = "";<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return (string)ViewState["sortExpressionAlt"];<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; set<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ViewState["sortExpressionAlt"] = value;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// &lt;summary&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// When using GridView in certain ways the SortDirection and SortExpression<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// properties are sometimes left blank or never changed. When using this control,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// the Alt properties remedy this situation.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// &lt;/summary&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// &lt;param name="e"&gt;&lt;/param&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; protected override void OnSorting(GridViewSortEventArgs e)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //Handle setting up of sorting info <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(!String.IsNullOrEmpty(this.SortExpression))<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SortExpressionAlt = e.SortExpression;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SortDirectionAlt = e.SortDirection;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (SortExpressionAlt == e.SortExpression)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (SortDirectionAlt == SortDirection.Ascending)<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; SortDirectionAlt = SortDirection.Descending;<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; else<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; SortDirectionAlt = SortDirection.Ascending;<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; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SortDirectionAlt = SortDirection.Ascending;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.SortExpressionAlt = e.SortExpression;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; base.OnSorting(e);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; protected override void InitializePager(GridViewRow row, int columnSpan, PagedDataSource pagedDataSource)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (this.EnableNextPrevNumericPager)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; base.PagerTemplate = new NextPrevNumericPagerTemplate(this.PageIndex, this.PageCount);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; base.InitializePager(row, columnSpan, pagedDataSource);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// &lt;summary&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// Adds custom effects to the GridView at runtime.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// &lt;/summary&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// &lt;param name="e"&gt;&lt;/param&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; protected override void OnRowCreated(GridViewRowEventArgs e)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; base.OnRowCreated(e);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (e.Row.RowType == DataControlRowType.DataRow)<br>&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; //If row selection is enabled then add mouse over scripts to enable on client.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (EnableSelection)<br>&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; EnableEnhancedSelectForRow(e);<br>&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; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else if (e.Row.RowType == DataControlRowType.Header &amp;&amp; ShowSortDirection)<br>&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; //this.SortDirection<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; foreach (TableCell headerCell in e.Row.Cells)<br>&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; if (headerCell.HasControls())<br>&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; AddSortImageToHeaderCell(headerCell);<br>&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; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private void AddSortImageToHeaderCell(TableCell headerCell)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // search for the header link<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; LinkButton lnk = (LinkButton)headerCell.Controls[0];<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (lnk != null)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // inizialize a new image<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.Web.UI.WebControls.Image img = new System.Web.UI.WebControls.Image();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // setting the dynamically URL of the image<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; img.ImageUrl = (this.SortDirectionAlt == SortDirection.Ascending ? this.SortAscImageUrl : this.SortDescImageUrl);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // checking if the header link is the user's choice<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (this.SortExpressionAlt == lnk.CommandArgument)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // adding a space and the image to the header link<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; headerCell.Controls.Add(new LiteralControl(" "));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; headerCell.Controls.Add(img);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private void EnableEnhancedSelectForRow(GridViewRowEventArgs e)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; e.Row.Attributes.Add("onmouseover", RowOnMouseOverScript());<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; e.Row.Attributes.Add("onmouseout", RowOnMouseOutScript());</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; foreach (TableCell cell in e.Row.Cells)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // if no link are presen the cell,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // we add the functionnality to select the row on the cell with a click<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (!Recurser.ContainsLink(cell))<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; AddPostBackEventToCell(e, cell);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private void AddPostBackEventToCell(GridViewRowEventArgs e, TableCell cell)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // here we add the command to postback when the user click somewhere in the cell<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cell.Attributes.Add("onclick",<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Page.ClientScript.GetPostBackEventReference(this,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "Select$" + e.Row.RowIndex.ToString()));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cell.Style.Add(HtmlTextWriterStyle.Cursor, "pointer");<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cell.Attributes.Add("title", "Select");<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// &lt;summary&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// Get or Set Image location to be used to display Ascending Sort order.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// &lt;/summary&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Description("Image to display for Ascending Sort"),<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Category("Misc"),<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Editor("System.Web.UI.Design.UrlEditor", typeof(System.Drawing.Design.UITypeEditor)),<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DefaultValue(""),<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ]<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public string SortAscImageUrl<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; get<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; object o = ViewState["SortImageAsc"];<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return (o != null ? o.ToString() : "");<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; set<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ViewState["SortImageAsc"] = value;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// &lt;summary&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// Get or Set Image location to be used to display Ascending Sort order.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// &lt;/summary&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Description("Sets whether or not we show the sort arrows in the GridView."),<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Category("Misc"),<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Editor("System.Web.UI.Design.UrlEditor", typeof(System.Drawing.Design.UITypeEditor)),<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DefaultValue("false"),<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ]<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public bool ShowSortDirection<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; get<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; object o = ViewState["ShowSortDirection"];<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return (o != null ? Convert.ToBoolean(o) : false);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; set<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ViewState["ShowSortDirection"] = value;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// &lt;summary&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// Gets or Sets whether we show the sort arrow in the GridView after the header text or before.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// &lt;/summary&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Description("Sets whether we show the sort arrow in the GridView after the header text or before."),<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Category("Misc"),<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Editor("System.Web.UI.Design.UrlEditor", typeof(System.Drawing.Design.UITypeEditor)),<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DefaultValue("false"),<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ]<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public bool ShowSortImageBeforeHeaderText<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; get<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; object o = ViewState["ShowSortImageBeforeHeaderText"];<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return (o != null ? Convert.ToBoolean(o) : false);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; set<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ViewState["ShowSortImageBeforeHeaderText"] = value; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// &lt;summary&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// Get or Set Image location to be used to display Ascending Sort order.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// &lt;/summary&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Description("Image to display for Descending Sort"),<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Category("Misc"),<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Editor("System.Web.UI.Design.UrlEditor", typeof(System.Drawing.Design.UITypeEditor)),<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DefaultValue(""),<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ]<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public string SortDescImageUrl<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; get<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; object o = ViewState["SortImageDesc"];<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return (o != null ? o.ToString() : "");<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; set<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ViewState["SortImageDesc"] = value;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// &lt;summary&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// Highlights the background of the row that the mouse is currently hovering over.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// &lt;/summary&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// &lt;returns&gt;&lt;/returns&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; protected string RowOnMouseOverScript()<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return "this.style.backgroundColor = '" + HighlightColor + "';";<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// &lt;summary&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// Removes highlighting created by RowOnMouseOverScript.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// &lt;/summary&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// &lt;returns&gt;&lt;/returns&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; protected string RowOnMouseOutScript()<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return "this.style.backgroundColor = '';";<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp; /// &lt;summary&gt;<br>&nbsp;&nbsp;&nbsp; /// <br>&nbsp;&nbsp;&nbsp; /// &lt;/summary&gt;<br>&nbsp;&nbsp;&nbsp; public static class Recurser<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// &lt;summary&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// this method check if a control or one of its children<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// has the type of the types given by recursivity<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// &lt;/summary&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// &lt;param name="control"&gt;control to check&lt;/param&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// &lt;param name="types"&gt;type to find&lt;/param&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// &lt;returns&gt;the first occurence of control which has one of the given types&lt;/returns&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public static Control ContainsControlType(Control control, params Type[] types)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // may be we could loop throug the controls first and then the types<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // to gain somme speed of process<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // but I wanted to ensure the same process for any control,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // for the first one like its children (cause the first one could have a given type)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; foreach (Type type in types)<br>&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; if (control.GetType().Equals(type))<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return control;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // begin recursivity<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; foreach (Control ctrl in control.Controls)<br>&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; Control tmpCtrl = ContainsControlType(ctrl, type);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (tmpCtrl != null)<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; return tmpCtrl;<br>&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; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // if no controls had the given type in the current control we return false<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return null;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// &lt;summary&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// Check if there is more thant 0 links controls in a control or if this control is a link<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// &lt;/summary&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// &lt;param name="control"&gt;control to check in&lt;/param&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// &lt;returns&gt;true if there is any links&lt;/returns&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public static bool ContainsLink(Control control)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; bool ret = false;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // search a link in the cell<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Control ctrl = ContainsControlType(control, typeof(HyperLink), typeof(LinkButton), typeof(DataBoundLiteralControl), typeof(TextBox), typeof(DropDownList));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // if a control is returned, we have to check the case of the literal which could contain no links<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (ctrl != null)<br>&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; if (ctrl.GetType().Equals(typeof(DataBoundLiteralControl)))<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; DataBoundLiteralControl dblc = (DataBoundLiteralControl)ctrl;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // here I check if the text contains a href or onclick attribute<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // I assume that there this text should not be used to be displayed<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (dblc.Text.Contains("href") || dblc.Text.Contains("onclick"))<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ret = true;<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; else ret = true;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return ret;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp; /// &lt;summary&gt;<br>&nbsp;&nbsp; /// Pager Template that conforms to JustinsWeb's user experience standards for functionality.<br>&nbsp;&nbsp; /// &lt;/summary&gt;<br>&nbsp;&nbsp; public class NextPrevNumericPagerTemplate : ITemplate<br>&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int _pageIndex;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int _pageNumber<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; get<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return _pageIndex + 1;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; set<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _pageIndex = value - 1;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int _pageCount;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// &lt;summary&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// Constructor for template. <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// &lt;/summary&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// &lt;param name="pageIndex"&gt;Current page index.&lt;/param&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// &lt;param name="pageCount"&gt;Total count of all pages.&lt;/param&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public NextPrevNumericPagerTemplate(int pageIndex, int pageCount)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _pageIndex = pageIndex;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _pageCount = pageCount;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// &lt;summary&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// Called when pager is instantiated in the provided control.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// &lt;/summary&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// &lt;param name="container"&gt;&lt;/param&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public void InstantiateIn(Control container)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int pagerStartIndex = startPageIndex(_pageIndex, _pageCount);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int pagerEndIndex = endPageIndex(_pageIndex, _pageCount);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; createResultsSummary(container);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; createPrevButton(container);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; createSpacer(container);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; createPrevEllipsisIfNeeded(container, pagerStartIndex);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; createCorrectPageButtons(container, pagerStartIndex, pagerEndIndex);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; createNextEllipsisIfNeeded(container, pagerEndIndex);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; createNextButton(container);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// &lt;summary&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// Creates the ... that allows you to jump forward to more pages in the pager.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// &lt;/summary&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// &lt;param name="container"&gt;&lt;/param&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// &lt;param name="pagerEndIndex"&gt;&lt;/param&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private void createNextEllipsisIfNeeded(Control container, int pagerEndIndex)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (pageNumber(pagerEndIndex) &lt; _pageCount)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; createEllipsisButton(container, pagerEndIndex + 1);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private void createPrevEllipsisIfNeeded(Control container, int pagerStartIndex)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (pageNumber(pagerStartIndex) &gt; 1)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; createEllipsisButton(container, pagerStartIndex - 1);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private void createCorrectPageButtons(Control container, int pagerStartIndex, int pagerEndIndex)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for (int i = pagerStartIndex; i &lt;= pagerEndIndex; i++)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; createCorrectPageButton(container, i);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private static void createNextButton(Control container)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; LinkButton nextButton = new LinkButton();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; nextButton.CommandName = "Page";<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; nextButton.CommandArgument = "Next";<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; nextButton.Text = "Next &gt;";<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; container.Controls.Add(nextButton);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private static void createPrevButton(Control container)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; LinkButton prevButton = new LinkButton();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; prevButton.CommandName = "Page";<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; prevButton.CommandArgument = "Prev";<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; prevButton.Text = "&lt; Prev";<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; container.Controls.Add(prevButton);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private void createResultsSummary(Control container)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Label resultsSummary = new Label();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; resultsSummary.CssClass = "PagerResultsSummary";<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; resultsSummary.Text = "Page " + _pageNumber + " of " + _pageCount;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; container.Controls.Add(resultsSummary);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private void createCorrectPageButton(Control container, int pageIndexOnButton)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (_pageIndex == pageIndexOnButton)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; createNumericPageLabel(container, pageIndexOnButton);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; createNumericPageButton(container, pageIndexOnButton);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; createSpacer(container);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private void createNumericPageButton(Control container, int pageIndex)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; LinkButton pageButton;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pageButton = new LinkButton();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pageButton.Text = pageNumber(pageIndex).ToString();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pageButton.CommandName = "Page";<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pageButton.CommandArgument = pageNumber(pageIndex).ToString();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; container.Controls.Add(pageButton);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private void createNumericPageLabel(Control container, int pageIndex)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Label currentPageLabel;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; currentPageLabel = new Label();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; currentPageLabel.CssClass = "SelectedPageButton";<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; currentPageLabel.Text = pageNumber(pageIndex).ToString();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; container.Controls.Add(currentPageLabel);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private void createEllipsisButton(Control container, int goToIndex)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; LinkButton pageButton;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pageButton = new LinkButton();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pageButton.Text = "...";<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pageButton.CommandName = "Page";<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pageButton.CommandArgument = pageNumber(goToIndex).ToString();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; container.Controls.Add(pageButton);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; createSpacer(container);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private static void createSpacer(Control container)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Literal spacer = new Literal();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; spacer = new Literal();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; spacer.Text = "&amp;nbsp;";</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; container.Controls.Add(spacer);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// &lt;summary&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// Finds the starting page index to display on the pager. (Will need conversion to page number)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// &lt;/summary&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// &lt;param name="currentPageIndex"&gt;&lt;/param&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// &lt;param name="totalPageCount"&gt;&lt;/param&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// &lt;returns&gt;&lt;/returns&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private int startPageIndex(int currentPageIndex, int totalPageCount)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int startingPageToDisplay = 0;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; startingPageToDisplay = currentPageIndex - 4;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if ((pageIndex(totalPageCount) - currentPageIndex) &lt; 5)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; startingPageToDisplay = pageIndex(totalPageCount) - 9;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (startingPageToDisplay &lt; 0)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; startingPageToDisplay = 0;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return startingPageToDisplay;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// &lt;summary&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// Finds the ending page index to display on the pager. (Will need conversion to page number)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// &lt;/summary&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// &lt;param name="currentPageIndex"&gt;&lt;/param&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// &lt;param name="totalPageCount"&gt;&lt;/param&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// &lt;returns&gt;&lt;/returns&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private int endPageIndex(int currentPageIndex, int totalPageCount)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int endingPageToDisplay = currentPageIndex + 5;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int maxEndingPageIndex = (9 &gt; pageIndex(totalPageCount)) ? pageIndex(totalPageCount) : 9;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (endingPageToDisplay &gt; totalPageCount - 1)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; endingPageToDisplay = totalPageCount - 1;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else if (currentPageIndex &lt; 5)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; endingPageToDisplay = maxEndingPageIndex;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return endingPageToDisplay;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// &lt;summary&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// Converts a given page index to a page number.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// &lt;/summary&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// &lt;param name="pageIndex"&gt;&lt;/param&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// &lt;returns&gt;&lt;/returns&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private int pageNumber(int pageIndex)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return pageIndex + 1;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// &lt;summary&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// Converts a given page number to a page index.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// &lt;/summary&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// &lt;param name="pageNumber"&gt;&lt;/param&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// &lt;returns&gt;&lt;/returns&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private int pageIndex(int pageNumber)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return pageNumber - 1;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp; }</p>
<p><br>}<br></p>
<img src ="http://www.cnitblog.com/MartinYao/aggbug/51911.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/MartinYao/" target="_blank">玄铁剑</a> 2008-11-26 21:56 <a href="http://www.cnitblog.com/MartinYao/articles/51911.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Master-Detail with the GridView, DetailsView and ModalPopup Controls</title><link>http://www.cnitblog.com/MartinYao/articles/44434.html</link><dc:creator>玄铁剑</dc:creator><author>玄铁剑</author><pubDate>Tue, 27 May 2008 14:13:00 GMT</pubDate><guid>http://www.cnitblog.com/MartinYao/articles/44434.html</guid><wfw:comment>http://www.cnitblog.com/MartinYao/comments/44434.html</wfw:comment><comments>http://www.cnitblog.com/MartinYao/articles/44434.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/MartinYao/comments/commentRss/44434.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/MartinYao/services/trackbacks/44434.html</trackback:ping><description><![CDATA[<p>A while back I wrote a <a href="http://mattberseth.com/blog/2007/07/modalpopupextender_example_for.html"><u><font color=#0066cc>post</font></u></a> describing how the <a href="http://msdn2.microsoft.com/en-us/library/system.web.ui.webcontrols.detailsview.aspx"><u><font color=#0066cc>DetailsView</font></u></a>, <a href="http://msdn2.microsoft.com/en-us/library/system.web.ui.webcontrols.gridview.aspx"><u><font color=#0066cc>GridView</font></u></a>, <a href="http://msdn2.microsoft.com/en-us/library/system.web.ui.updatepanel.aspx"><u><font color=#0066cc>UpdatePanel</font></u></a> and the <a href="http://www.asp.net/ajax/ajaxcontroltoolkit/samples/"><u><font color=#0066cc>AjaxControlToolkit's</font></u></a> <a href="http://www.asp.net/AJAX/AjaxControlToolkit/Samples/ModalPopup/ModalPopup.aspx"><u><font color=#0066cc>ModalPopup</font></u></a> controls could be used to implement the common Master/Details UI pattern.&nbsp; I cheated a bit when creating my original example in that I didn't really complete the implementation - the Save button on the popup didn't actually do anything.&nbsp; Since writing that post I have received a lot of email and a number of people left comments asking me to complete the example - so here it is.&nbsp; If you plan on reading through this article, I recommend playing around with the demo site to get a feel for how the page works.&nbsp; All data changes are only persisted to memory, so don't worry about messing up the data set.</p>
<p><a href="http://mattberseth2.com/master_details_II/" target=_blank><u><font color=#800080>Live Demo</font></u></a> | <a href="http://mattberseth2.com/downloads/master_details_II.zip" target=_blank><u><font color=#0066cc>Download</font></u></a></p>
<p><u><font color=#0066cc><img height=280 alt=image src="http://mattberseth.com/WindowsLiveWriter/958e288a8eb2_11AC8/image_32277640-7fe2-49d2-98f5-036ded667041.png" width=604></font></u></p>
<p><strong><u>Scenario</u></strong></p>
<p>I am sure everyone is pretty familiar with Master/Details style of editing data, but just in case - here is how my page works.&nbsp; The grid shows 12 rows of customer data.&nbsp; The far right column in the grid contains a hyperlink that when clicked brings the detail view of the row into focus so the corresponding row can be edited.&nbsp; The detail view is a popup control and contains a Save and Close buttons.&nbsp; When close is clicked, the detail popup is dismissed and the user goes back to viewing the main grid.&nbsp; When they click Save, some simple validation checks are run (all are <a href="http://msdn2.microsoft.com/en-us/library/system.web.ui.webcontrols.requiredfieldvalidator.aspx"><u><font color=#0066cc>RequiredFieldValidators</font></u></a> for this sample) and the new data values are persisted, and finally the detail popup is dismissed and the main grid is refreshed so that it displays the changes.</p>
<p><strong><u>Controls</u></strong></p>
<p>There are quite a few controls that work together for this sample.&nbsp; Here is a listing of the controls and the role they play.</p>
<table cellSpacing=0 cellPadding=2 width=684 border=1>
    <tbody>
        <tr>
            <td vAlign=top width=199><strong>Control</strong></td>
            <td vAlign=top width=483><strong>Role</strong></td>
        </tr>
        <tr>
            <td vAlign=top width=199><a href="http://msdn2.microsoft.com/en-us/library/system.web.ui.webcontrols.gridview.aspx"><u><font color=#0066cc>GridView</font></u></a></td>
            <td vAlign=top width=483>Display the 12 customer rows of data</td>
        </tr>
        <tr>
            <td vAlign=top width=199><a href="http://msdn2.microsoft.com/en-us/library/system.web.ui.webcontrols.detailsview.aspx"><u><font color=#0066cc>DetailsView</font></u></a></td>
            <td vAlign=top width=483>Display the single customer row that is currently being edited</td>
        </tr>
        <tr>
            <td vAlign=top width=199><a href="http://www.asp.net/AJAX/AjaxControlToolkit/Samples/ModalPopup/ModalPopup.aspx"><u><font color=#0066cc>ModalPopupExtender</font></u></a></td>
            <td vAlign=top width=483>Display the DetailView modally</td>
        </tr>
        <tr>
            <td vAlign=top width=199><a href="http://msdn2.microsoft.com/en-us/library/system.web.ui.updatepanel.aspx"><u><font color=#0066cc>UpdatePanel</font></u></a></td>
            <td vAlign=top width=483>Allow the DetailView to be loaded without refreshing the entire page</td>
        </tr>
        <tr>
            <td vAlign=top width=199><a href="http://msdn2.microsoft.com/en-us/library/system.web.ui.webcontrols.objectdatasource.aspx"><u><font color=#0066cc>ObjectDataSource</font></u></a></td>
            <td vAlign=top width=483>Manage how our UI interacts with the customer data</td>
        </tr>
    </tbody>
</table>
<p><strong><u>Customer GridView</u></strong></p>
<p>The Customer Grid is just a regular GridView.&nbsp; I have used BoundFields to bind to the data and I am using a TemplateField for rendering the 'Edit' anchor.&nbsp; </p>
<p><img height=403 alt=image src="http://mattberseth.com/WindowsLiveWriter/958e288a8eb2_11AC8/image_dfbf5c3c-c7bb-4eeb-b7ec-f888a04eddce.png" width=794> </p>
<p>When 'Edit' is clicked, I want to show the detail view popup.&nbsp; To accomplish this I set the CommandName of the 'Edit' button to Select - this will cause the SelectedIndexChanged event to fire on the server.&nbsp; Inside this event handler, I force the DetailView to databind, passing the datasource it is bound to the ID that corresponds to the row the user clicked.&nbsp; Finally, I let the UpdatePanel the DetailView is contained in that it needs to update its contents and invoke the Show server side method of my ModalPopupExtender which will cause the popup to be displayed when the partial page is reloaded on the client.</p>
<p>Here are the 2 events handlers that I am using to accomplish this bit of work.&nbsp; The top event handler fires when 'Edit' is clicked and the explicit call to DataBind forces the DetailView's corresponding ObjectDataSource's Selecting event to fire.&nbsp; In this handler I use the SelectedIndex of the GridView to fetch the ID the data source needs.&nbsp; </p>
<p><img height=305 alt=image src="http://mattberseth.com/WindowsLiveWriter/958e288a8eb2_11AC8/image_8c976986-e4bf-415a-bf0c-06238aab78a1.png" width=634> </p>
<p><img height=219 alt=image src="http://mattberseth.com/WindowsLiveWriter/958e288a8eb2_11AC8/image_3589db53-32d1-4564-a735-ed50739785fd.png" width=797>&nbsp; </p>
<p><strong><u>Customer DetailsView</u></strong></p>
<p>The DetailsView for the individual customer records is also pretty simple - except because I have included RequiredFieldValidators I wasn't able to use the BoundFields and instead I had to explicitly define the EditItemTemplate for each of the fields. </p>
<p><img height=402 alt=image src="http://mattberseth.com/WindowsLiveWriter/958e288a8eb2_11AC8/image_71591cea-672c-4881-bf7d-619526fa3bfe.png" width=726> </p>
<p>Just below the DetailsView, I have 2 buttons that interact with the ModalPopupExtender.&nbsp; The Close button dismissed the popup without posting back or committing any changes.&nbsp; The Save button does post back, moves the data out of the DetailsView and back to the ObjectDataSource, hides the ModalPopupExtender and finally causes the main GridView to refresh so it displays the changes.</p>
<p><img height=367 alt=image src="http://mattberseth.com/WindowsLiveWriter/958e288a8eb2_11AC8/image_50060c66-6398-4171-9772-c80699506635.png" width=789> </p>
<p>Here is the logic I have wired to the Save button's click handler.</p>
<p><img height=391 alt=image src="http://mattberseth.com/WindowsLiveWriter/958e288a8eb2_11AC8/image_52e21c59-ffbd-4158-a465-fcf9e9f76e4b.png" width=513> </p>
<p><strong><u>Apply a Yellow Fade to the Updated Row in the GridView</u></strong></p>
<p>And while this has been pretty much nuts and bolds functionality thus far, I couldn't resist trying to spice it up by applying a yellow fade to the row in the main grid that was updated.&nbsp; To implement this feature, I needed a way to send a bit of information back from the server that let me know what row in the grid was updated.&nbsp; So my approach was to use the <a href="http://msdn2.microsoft.com/en-us/library/bb398863.aspx"><u><font color=#0066cc>ScriptManager's</font></u></a> <a href="http://msdn.microsoft.com/en-us/library/bb359752.aspx"><u><font color=#0066cc>RegisterDataItem</font></u></a> method to accomplish this.&nbsp; Here is the doc from MSDN:</p>
<blockquote>
<p><strong><em>Sends custom data to a control during partial-page rendering, and indicates whether the data is in JavaScript Object Notation (JSON) format.</em></strong></p>
</blockquote>
<p>Just what I need - so after the Save button is clicked, back in the server side event handler, I call RegisterDataItem and pass the index of the updated row to the GridView.&nbsp; Then after the partial refresh has occurred back on the client, I extract the row index from the DataItem's collection, use it to find the row that changed and give it a custom CSS class.</p>
<p>So I added the following call to my Save button handler to register the index of the row that was changed.</p>
<p><img height=153 alt=image src="http://mattberseth.com/WindowsLiveWriter/958e288a8eb2_11AC8/image_d86d84d2-471f-48b5-93cc-2d2e5dbebc55.png" width=515></p>
<p>And then added the following JavaScript to my page to track down the corresponding TR element in the rendered table and apply the <em>updated</em> CSS class to the row.</p>
<p><img height=471 alt=image src="http://mattberseth.com/WindowsLiveWriter/958e288a8eb2_11AC8/image_6e3b2e07-0d48-4574-a1d9-d4211b9419c3.png" width=617>&nbsp; </p>
<p>And like magic I get a yellow background applied to the row that was edited.&nbsp; The background color stays in place for 1.5 seconds then my timeout handler fires and removes it.&nbsp; A poor man's animation, but the plumbing is in place for me to improve upon.&nbsp; Below are the screen shots of how it looks.</p>
<p><img height=265 alt=image src="http://mattberseth.com/WindowsLiveWriter/958e288a8eb2_11AC8/image_d076f72d-8773-4919-858c-6b53dee04afc.png" width=569> </p>
<p><img height=147 alt=image src="http://mattberseth.com/WindowsLiveWriter/958e288a8eb2_11AC8/image_1f1736fc-7169-4871-819a-cfef0ea248f2.png" width=559> </p>
<img src ="http://www.cnitblog.com/MartinYao/aggbug/44434.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/MartinYao/" target="_blank">玄铁剑</a> 2008-05-27 22:13 <a href="http://www.cnitblog.com/MartinYao/articles/44434.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Fast ASP.NET web page loading by downloading multiple javascripts in batch</title><link>http://www.cnitblog.com/MartinYao/articles/44433.html</link><dc:creator>玄铁剑</dc:creator><author>玄铁剑</author><pubDate>Tue, 27 May 2008 14:12:00 GMT</pubDate><guid>http://www.cnitblog.com/MartinYao/articles/44433.html</guid><wfw:comment>http://www.cnitblog.com/MartinYao/comments/44433.html</wfw:comment><comments>http://www.cnitblog.com/MartinYao/articles/44433.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/MartinYao/comments/commentRss/44433.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/MartinYao/services/trackbacks/44433.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: Download FastLoad_src - 7.91 KB IntroductionA web page can load a lot faster and feel faster if the Javascripts files referenced on the page can be loaded after the visible content has been load...&nbsp;&nbsp;<a href='http://www.cnitblog.com/MartinYao/articles/44433.html'>阅读全文</a><img src ="http://www.cnitblog.com/MartinYao/aggbug/44433.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/MartinYao/" target="_blank">玄铁剑</a> 2008-05-27 22:12 <a href="http://www.cnitblog.com/MartinYao/articles/44433.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>custom GridView pager</title><link>http://www.cnitblog.com/MartinYao/articles/41587.html</link><dc:creator>玄铁剑</dc:creator><author>玄铁剑</author><pubDate>Thu, 27 Mar 2008 14:44:00 GMT</pubDate><guid>http://www.cnitblog.com/MartinYao/articles/41587.html</guid><wfw:comment>http://www.cnitblog.com/MartinYao/comments/41587.html</wfw:comment><comments>http://www.cnitblog.com/MartinYao/articles/41587.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/MartinYao/comments/commentRss/41587.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/MartinYao/services/trackbacks/41587.html</trackback:ping><description><![CDATA[<p>&lt;PagerTemplate&gt; <br>&nbsp;&nbsp;&nbsp; &lt;asp:Label ID="lblPage" runat="server" Text="Page:" ForeColor="blue"&gt;&lt;/asp:Label&gt;<br>&nbsp;&nbsp;&nbsp; &lt;asp:Label ID="lblCurrentPage" runat="server" Font-Bold="true" Text="&lt;%# ((GridView)Container.NamingContainer).PageIndex + 1 %&gt;"<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ForeColor="red"&gt;&lt;/asp:Label&gt;/<br>&nbsp;&nbsp;&nbsp; &lt;asp:Label ID="lblPageCount" runat="server" Font-Bold="true" Text="&lt;%# ((GridView)Container.NamingContainer).PageCount %&gt;"<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ForeColor="black"&gt;&lt;/asp:Label&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; &lt;asp:LinkButton ID="lbtnFirstPage" runat="server" CommandName="Page" CommandArgument="First" Text="First"<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Enabled="&lt;%# ((GridView)Container.NamingContainer).PageIndex != 0 %&gt;"&gt;&lt;/asp:LinkButton&gt;<br>&nbsp;&nbsp;&nbsp; &lt;asp:LinkButton ID="lbtnPreviousPage" runat="server" Enabled="&lt;%# ((GridView)Container.NamingContainer).PageIndex != 0 %&gt;" Text="Prev"<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CommandName="Page" CommandArgument="Prev"&gt;&lt;/asp:LinkButton&gt;<br>&nbsp;&nbsp;&nbsp; &lt;asp:LinkButton ID="lbtnNextPage" runat="server" CommandName="Page" CommandArgument="Next" Text="Next"<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Enabled="&lt;%# ((GridView)Container.NamingContainer).PageIndex != ((GridView)Container.NamingContainer).PageCount - 1 %&gt;"&gt;&lt;/asp:LinkButton&gt;<br>&nbsp;&nbsp;&nbsp; &lt;asp:LinkButton ID="lbtnLastPage" runat="server" CommandName="Page" CommandArgument="Last" Text="Last"<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Enabled="&lt;%# ((GridView)Container.NamingContainer).PageIndex != ((GridView)Container.NamingContainer).PageCount - 1 %&gt;"&gt;&lt;/asp:LinkButton&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&lt;/PagerTemplate&gt;<br>&lt;PagerStyle CssClass="GridPager"&nbsp; HorizontalAlign="Center"/&gt;</p>
<p>&nbsp;</p>
<img src ="http://www.cnitblog.com/MartinYao/aggbug/41587.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/MartinYao/" target="_blank">玄铁剑</a> 2008-03-27 22:44 <a href="http://www.cnitblog.com/MartinYao/articles/41587.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>10 ASP.NET Performance and Scalability Secrets</title><link>http://www.cnitblog.com/MartinYao/articles/40978.html</link><dc:creator>玄铁剑</dc:creator><author>玄铁剑</author><pubDate>Sat, 15 Mar 2008 14:28:00 GMT</pubDate><guid>http://www.cnitblog.com/MartinYao/articles/40978.html</guid><wfw:comment>http://www.cnitblog.com/MartinYao/comments/40978.html</wfw:comment><comments>http://www.cnitblog.com/MartinYao/articles/40978.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/MartinYao/comments/commentRss/40978.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/MartinYao/services/trackbacks/40978.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: ASP.NET pipeline optimizationASP.NET process configuration optimizationThings you must do for ASP.NET before going liveContent Delivery NetworkCaching AJAX calls on browserMaking best use of...&nbsp;&nbsp;<a href='http://www.cnitblog.com/MartinYao/articles/40978.html'>阅读全文</a><img src ="http://www.cnitblog.com/MartinYao/aggbug/40978.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/MartinYao/" target="_blank">玄铁剑</a> 2008-03-15 22:28 <a href="http://www.cnitblog.com/MartinYao/articles/40978.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>性能的设计与开发</title><link>http://www.cnitblog.com/MartinYao/articles/40314.html</link><dc:creator>玄铁剑</dc:creator><author>玄铁剑</author><pubDate>Fri, 29 Feb 2008 14:11:00 GMT</pubDate><guid>http://www.cnitblog.com/MartinYao/articles/40314.html</guid><wfw:comment>http://www.cnitblog.com/MartinYao/comments/40314.html</wfw:comment><comments>http://www.cnitblog.com/MartinYao/articles/40314.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/MartinYao/comments/commentRss/40314.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/MartinYao/services/trackbacks/40314.html</trackback:ping><description><![CDATA[<div class=postbody>
<p style="TEXT-INDENT: 24pt"><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">性能问题应该从系统设计时期开始考虑，并延续到系统的生命期终止之时。</span></span></span></span></span></span><span style="FONT-SIZE: 12pt"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt"> </span></span></span></span></span>
<p><span><span style="FONT-SIZE: 8pt"></span></span></span></p>
<p style="TEXT-INDENT: 24pt"><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">具有可伸缩性的系统是指当系统的负载增加一倍，系统需要的资源也同样增加一倍。说起来简单，但在现实环境中确难以做到。由于管理并发用户的开销的增长、锁事务的增长、一致性读负载的增加、操作系统负载的增加、</span></span></span></span></span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">低效的</span></span></span></span></span><span><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">SQL或索引设计导致的过高的I/O等等因素，会导致系统资源的消耗的增长远大于一倍。 </span></span></span></span></span>
<p><span><span style="FONT-SIZE: 8pt"></span></span></span></span></p>
<p style="TEXT-INDENT: 24.1pt"><strong><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">破坏可伸缩性的因素：</span></span></span></span></span><span><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt"> </span></span></span></span></span>
<p><span><span style="FONT-SIZE: 8pt"></span></span></span></span></strong></p>
<p style="TEXT-INDENT: 24pt"><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">1.低效的应用程序设计、实施和配置 </span></span></span></span></span>
<p><span><span style="FONT-SIZE: 8pt"></span></span></span></p>
<p style="TEXT-INDENT: 24pt"><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">2.硬件部分的规模不合适 </span></span></span></span></span>
<p><span><span style="FONT-SIZE: 8pt"></span></span></span></p>
<p style="TEXT-INDENT: 24pt"><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">3.软件部分的限制 </span></span></span></span></span>
<p><span><span style="FONT-SIZE: 8pt"></span></span></span></p>
<p style="TEXT-INDENT: 24pt"><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">4.硬件部分的限制&nbsp;</span></span></span></span></span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">&nbsp; </span></span></span></span></span>
<p><span><span style="FONT-SIZE: 8pt"></span></span></span></p>
<p style="TEXT-INDENT: 24.1pt"><strong><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">系统的结构可分为硬件和软件两部分：</span></span></span></span></span><span><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt"> </span></span></span></span></span>
<p><span><span style="FONT-SIZE: 8pt"></span></span></span></span></strong></p>
<p style="TEXT-INDENT: 24pt"><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">硬件部分包括：</span></span></span></span></span><span><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">CPU、内存、I/O子系统和网络模块。 </span></span></span></span></span>
<p><span><span style="FONT-SIZE: 8pt"></span></span></span></span></p>
<p style="TEXT-INDENT: 24pt"><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">软件部分包括：管理用户接口、实现商业逻辑、管理用户请求和资源分配、管理数据和事务。</span></span></span></span></span><span><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">&nbsp;</span></span></span></span></span></span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">&nbsp; </span></span></span></span></span>
<p><span><span style="FONT-SIZE: 8pt"></span></span></span></p>
<p style="TEXT-INDENT: 24.1pt"><strong><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">在设计系统时，应该考虑以下几个问题：</span></span></span></span></span><span><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt"> </span></span></span></span></span>
<p><span><span style="FONT-SIZE: 8pt"></span></span></span></span></strong></p>
<p style="TEXT-INDENT: 24pt"><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">系统将支持多少用户？</span></span></span></span></span><span><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt"> </span></span></span></span></span>
<p><span><span style="FONT-SIZE: 8pt"></span></span></span></span></p>
<p style="TEXT-INDENT: 24pt"><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">用户的交互方式是什么？</span></span></span></span></span><span><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt"> </span></span></span></span></span>
<p><span><span style="FONT-SIZE: 8pt"></span></span></span></span></p>
<p style="TEXT-INDENT: 24pt"><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">用户所处的位置？</span></span></span></span></span><span><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt"> </span></span></span></span></span>
<p><span><span style="FONT-SIZE: 8pt"></span></span></span></span></p>
<p style="TEXT-INDENT: 24pt"><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">网络的速度怎样？</span></span></span></span></span><span><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt"> </span></span></span></span></span>
<p><span><span style="FONT-SIZE: 8pt"></span></span></span></span></p>
<p style="TEXT-INDENT: 24pt"><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">用户将访问多少数据？有多少数据是只读访问？</span></span></span></span></span><span><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt"> </span></span></span></span></span>
<p><span><span style="FONT-SIZE: 8pt"></span></span></span></span></p>
<p style="TEXT-INDENT: 24pt"><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">用户对响应时间的要求？</span></span></span></span></span><span><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt"> </span></span></span></span></span>
<p><span><span style="FONT-SIZE: 8pt"></span></span></span></span></p>
<p style="TEXT-INDENT: 24pt"><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">用户是否需要</span></span></span></span></span><span><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">24小时服务？ </span></span></span></span></span>
<p><span><span style="FONT-SIZE: 8pt"></span></span></span></span></p>
<p style="TEXT-INDENT: 24pt"><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">是否所有的修改需要实时完成？</span></span></span></span></span><span><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">&nbsp;</span></span></span></span></span></span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">&nbsp; </span></span></span></span></span>
<p><span><span style="FONT-SIZE: 8pt"></span></span></span></p>
<p style="TEXT-INDENT: 24.1pt"><strong><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">应用程序设计原则：</span></span></span></span></span><span><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt"> </span></span></span></span></span>
<p><span><span style="FONT-SIZE: 8pt"></span></span></span></span></strong></p>
<p style="TEXT-INDENT: 24pt"><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">设计简单性原则：</span></span></span></span></span><span><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt"> </span></span></span></span></span>
<p><span><span style="FONT-SIZE: 8pt"></span></span></span></span></p>
<p style="TEXT-INDENT: 24pt"><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">1.如果表的设计复杂到没有人能够完全的理解，那么表的设计可能是比较差的。 </span></span></span></span></span>
<p><span><span style="FONT-SIZE: 8pt"></span></span></span></p>
<p style="TEXT-INDENT: 24pt"><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">2.如果SQL语句过长以致于优化程序无法优化该语句，那么SQL语句的设计、事务和表的设计一定存在问题。 </span></span></span></span></span>
<p><span><span style="FONT-SIZE: 8pt"></span></span></span></p>
<p style="TEXT-INDENT: 24pt"><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">3.如果表的相同列上被重复索引，那么索引的设计可能是有问题的。 </span></span></span></span></span>
<p><span><span style="FONT-SIZE: 8pt"></span></span></span></p>
<p style="TEXT-INDENT: 24pt"><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">4.如果提交的查询没有限定，以致无法迅速的将结果返回给在线用户，那么用户接口或事务的设计是有问题的。 </span></span></span></span></span>
<p><span><span style="FONT-SIZE: 8pt"></span></span></span></p>
<p style="TEXT-INDENT: 24pt"><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">5.如果数据库的调用被许多层软件从应用逻辑中抽象出来，那么，软件开发的方法可能存在问题。 </span></span></span></span></span>
<p><span><span style="FONT-SIZE: 8pt"></span></span></span></p>
<p style="TEXT-INDENT: 24pt"><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">数据建模：应当注意，不要在非核心数据单元上花费过多的时间。</span></span></span></span></span><span><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt"> </span></span></span></span></span>
<p><span><span style="FONT-SIZE: 8pt"></span></span></span></span></p>
<p style="TEXT-INDENT: 24pt"><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">表和索引的设计：选择合适的列进行索引、选择索引类型、注意索引的代价、关注索引中列的顺序。</span></span></span></span></span><span><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt"> </span></span></span></span></span>
<p><span><span style="FONT-SIZE: 8pt"></span></span></span></span></p>
<p style="TEXT-INDENT: 24pt"><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">一个表上如果有</span></span></span></span></span><span><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">3个索引，那么当进行INSERT/UPDATE/DELETE操作时，会比不带索引的表慢大约10倍。 </span></span></span></span></span>
<p><span><span style="FONT-SIZE: 8pt"></span></span></span></span></p>
<p style="TEXT-INDENT: 24pt"><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">组合索引中，选择性高的列在前查询时需要的</span></span></span></span></span><span><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">I/O更少。选择性低的列在前，有助于代排序操作的查询。 </span></span></span></span></span>
<p><span><span style="FONT-SIZE: 8pt"></span></span></span></span></p>
<p style="TEXT-INDENT: 24pt"><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">SQL执行效率： </span></span></span></span></span>
<p><span><span style="FONT-SIZE: 8pt"></span></span></span></p>
<p style="TEXT-INDENT: 24pt"><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">数据库连接管理：应避免没有必要的过多连接。</span></span></span></span></span><span><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt"> </span></span></span></span></span>
<p><span><span style="FONT-SIZE: 8pt"></span></span></span></span></p>
<p style="TEXT-INDENT: 24pt"><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">数据库游标管理：使用</span></span></span></span></span><span><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">cursor和绑定变量，尽量避免硬分析，较少软分析。 </span></span></span></span></span>
<p><span><span style="FONT-SIZE: 8pt"></span></span></span></span></p>
<p style="TEXT-INDENT: 24pt"><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">硬分析：</span></span></span></span></span><span><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">sql语句第一次提交，并在共享池中无法找到。 </span></span></span></span></span>
<p><span><span style="FONT-SIZE: 8pt"></span></span></span></span></p>
<p style="TEXT-INDENT: 24pt"><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">软分析：</span></span></span></span></span><span><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">sql语句第一次提交，但是可以在共享池中找到相同的语句。&nbsp;</span></span></span></span></span></span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">&nbsp; </span></span></span></span></span>
<p><span><span style="FONT-SIZE: 8pt"></span></span></span></p>
<p style="TEXT-INDENT: 24.1pt"><strong><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">实施新的应用程序：</span></span></span></span></span><span><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt"> </span></span></span></span></span>
<p><span><span style="FONT-SIZE: 8pt"></span></span></span></span></strong></p>
<p style="TEXT-INDENT: 24pt; TEXT-ALIGN: left" align=left><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">切换方式包括两种：</span></span></span></span></span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">Big Bang Approach（所有用户一次性转移到新的系统上）和Trickle Approach（用户分多次转移到新的系统上）。 </span></span></span></span></span>
<p><span><span style="FONT-SIZE: 8pt"></span></span></span></p>
<p style="TEXT-INDENT: 24pt; TEXT-ALIGN: left" align=left><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">性能清单列表：</span></span></span></span></span><span><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt"> </span></span></span></span></span>
<p><span><span style="FONT-SIZE: 8pt"></span></span></span></span></p>
<p style="TEXT-INDENT: 24pt; TEXT-ALIGN: left" align=left><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">1.设置MAXINSTANCES, MAXDATAFILES，MAXLOGFILES，MAXLOGMEMBERS和 MAXLOGHISTORY的值高于预期值。避免系统的增长导致必须重建控制文件。 </span></span></span></span></span>
<p><span><span style="FONT-SIZE: 8pt"></span></span></span></p>
<p style="TEXT-INDENT: 24pt; TEXT-ALIGN: left" align=left><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">2.设置BLOCK SIZE和优化模式与开发环境中相同。如果测试环境中的所有SQL语句的执行计划都是正确的，可以测试环境中的统计信息导入到正式库中。 </span></span></span></span></span>
<p><span><span style="FONT-SIZE: 8pt"></span></span></span></p>
<p style="TEXT-INDENT: 24pt; TEXT-ALIGN: left" align=left><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">3.尽量少修改初始化参数。除了SGA的组成部分和归档目录的设置，其他初始化参数尽量保持默认值，可以为以后性能优化留下一定的余地。 </span></span></span></span></span>
<p><span><span style="FONT-SIZE: 8pt"></span></span></span></p>
<p style="TEXT-INDENT: 24pt; TEXT-ALIGN: left" align=left><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">4.通过设置数据库对象的存储参数来管理BLOCK的争用。 </span></span></span></span></span>
<p><span><span style="FONT-SIZE: 8pt"></span></span></span></p>
<p style="TEXT-INDENT: 24pt; TEXT-ALIGN: left" align=left><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">5.所有的sql语句应该被优化。 </span></span></span></span></span>
<p><span><span style="FONT-SIZE: 8pt"></span></span></span></p>
<p style="TEXT-INDENT: 24pt; TEXT-ALIGN: left" align=left><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">6.验证中间层软件和程序采用高效的方式连接数据库。 </span></span></span></span></span>
<p><span><span style="FONT-SIZE: 8pt"></span></span></span></p>
<p style="TEXT-INDENT: 24pt; TEXT-ALIGN: left" align=left><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">7.验证sql语句有效的利用游标。 </span></span></span></span></span>
<p><span><span style="FONT-SIZE: 8pt"></span></span></span></p>
<p style="TEXT-INDENT: 24pt; TEXT-ALIGN: left" align=left><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">8.确认所有方案的对象从开发环境移植到了产品数据库中。 </span></span></span></span></span>
<p><span><span style="FONT-SIZE: 8pt"></span></span></span></p>
<p style="TEXT-INDENT: 24pt; TEXT-ALIGN: left" align=left><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">9.一旦完成系统的切换，建立数据库和操作系统统计信息的基线。 </span></span></span></span></span>
<p><span><span style="FONT-SIZE: 8pt"></span></span></span></p>
<p style="TEXT-INDENT: 24pt; TEXT-ALIGN: left" align=left><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt">10.发现最先出现的瓶颈。</span></span></span></span></span></span><span style="FONT-SIZE: 10pt"><span><span style="FONT-SIZE: 8pt"><span style="FONT-SIZE: 10pt"><span style="FONT-SIZE: 12pt"><span style="FONT-SIZE: 10pt"> </span></span></span></span></span></span></p>
</div>
<img src ="http://www.cnitblog.com/MartinYao/aggbug/40314.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/MartinYao/" target="_blank">玄铁剑</a> 2008-02-29 22:11 <a href="http://www.cnitblog.com/MartinYao/articles/40314.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>防止重复登陆控制方案</title><link>http://www.cnitblog.com/MartinYao/articles/39104.html</link><dc:creator>玄铁剑</dc:creator><author>玄铁剑</author><pubDate>Sun, 20 Jan 2008 10:34:00 GMT</pubDate><guid>http://www.cnitblog.com/MartinYao/articles/39104.html</guid><wfw:comment>http://www.cnitblog.com/MartinYao/comments/39104.html</wfw:comment><comments>http://www.cnitblog.com/MartinYao/articles/39104.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.cnitblog.com/MartinYao/comments/commentRss/39104.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/MartinYao/services/trackbacks/39104.html</trackback:ping><description><![CDATA[<font face="Courier New" color=#0000ff size=2>
<p><br>方案一：</p>
<p>In web.config (StateServer mode, with a one minute timeout to make testing easier):</p>
<p>
<table cellSpacing=0 cellPadding=0 width=650 bgColor=#cccccc 0?>
    <tbody>
        <tr>
            <td><font face="Courier New, Courier, mono" color=#0000ff size=2>&lt;sessionState <br>mode="StateServer"<br>stateConnectionString="tcpip=127.0.0.1:42424"<br>sqlConnectionString="data source=127.0.0.1;user id=sa;password=letmein"<br>cookieless="false" <br>timeout="1" <br>/&gt;</font></td>
        </tr>
    </tbody>
</table>
</p>
<p>&nbsp;</p>
<p>In Global.asax.cs:</p>
<p>
<table cellSpacing=0 cellPadding=0 width=650 bgColor=#cccccc border=0>
    <tbody>
        <tr>
            <td><font face="Courier New, Courier, mono" color=#0000ff size=2>protected void Application_PreRequestHandlerExecute(Object sender, EventArgs e)<br>{<br><font color=#ff0000>// Let's write a message to show this got fired---</font><br>Response.Write("SessionID: " +Session.SessionID.ToString() + "User key: " +(string)Session["user"]); <br>if(Session["user"]!=null) <font color=#ff0000>// e.g. this is after an initial logon</font><br>{ <br>string sKey=(string)Session["user"];<br><font color=#ff0000>// Accessing the Cache Item extends the Sliding Expiration automatically</font><br>string sUser=(string) HttpContext.Current.Cache[sKey];<br>}<br>}</font></td>
        </tr>
    </tbody>
</table>
</p>
<p>&nbsp;</p>
<p>In your Login Page "Login" button handler:</p>
<p>
<table cellSpacing=0 cellPadding=0 width=650 bgColor=#cccccc border=0>
    <tbody>
        <tr>
            <td width=650><font face="Courier New, Courier, mono" color=#0000ff size=2>private void Button1_Click(object sender, System.EventArgs e)<br>{ <br><font color=#ff0000>//validate your user here (Forms Auth or Database, for example)<br>// this could be a new "illegal" logon, so we need to check<br>// if these credentials are already in the Cache </font><br>string sKey=TextBox1.Text+TextBox2.Text;<br>string sUser=Convert.ToString(Cache[sKey]);<br>if (sUser==null || sUser==String.Empty){<br><font color=#ff0000>// No Cache item, so sesion is either expired or user is new sign-on<br>// Set the cache item and Session hit-test for this user---</font><br>TimeSpan SessTimeOut=new TimeSpan(0,0,HttpContext.Current.Session.Timeout,0,0);<br>HttpContext.Current.Cache.Insert(sKey,sKey,null,DateTime.MaxValue,SessTimeOut,<br>&nbsp;&nbsp; System.Web.Caching.CacheItemPriority.NotRemovable,null);<br>Session["user"]=TextBox1.Text+TextBox2.Text;<br><font color=#ff0000>// Let them in - redirect to main page, etc.</font><br>Label1.Text="&lt;Marquee&gt;&lt;h1&gt;Welcome!&lt;/h1&gt;&lt;/marquee&gt;";<br><br>}<br>else<br>{<br><font color=#ff0000>// cache item exists, so too bad... </font><br>Label1.Text="&lt;Marquee&gt;&lt;h1&gt;&lt;font color=red&gt;ILLEGAL LOGIN ATTEMPT!!!&lt;/font&gt;&lt;/h1&gt;&lt;/marquee&gt;";<br>return;<br>} </font><font face="Courier New, Courier, mono">
            <p><font color=#0000ff size=2><br>}</font></p>
            <p></font>&nbsp;</p>
            </td>
        </tr>
    </tbody>
</table>
</p>
<p><br>方案二：</p>
<p>public&nbsp;virtual&nbsp;void&nbsp;Application_Start(object&nbsp;sender,&nbsp;EventArgs&nbsp;e)&nbsp;<br>{&nbsp;<br>//&nbsp;reset&nbsp;the&nbsp;mailer&nbsp;indicator&nbsp;<br>Application["MailerStatus"]&nbsp;=&nbsp;"All&nbsp;Mailings&nbsp;Complete";&nbsp;</p>
<p>//&nbsp;initialize&nbsp;a&nbsp;datatable&nbsp;for&nbsp;users&nbsp;online&nbsp;<br>DataTable&nbsp;objUserTable&nbsp;=&nbsp;new&nbsp;DataTable();&nbsp;<br>objUserTable.Columns.Add("SessionID",System.Type.GetType("System.Guid"));&nbsp;<br>objUserTable.Columns.Add("PeopleID",System.Type.GetType("System.Int32"));&nbsp;<br>objUserTable.Columns.Add("ShowDetail",System.Type.GetType("System.Boolean"));&nbsp;<br>DataColumn[]&nbsp;pk&nbsp;=&nbsp;new&nbsp;DataColumn[1];&nbsp;<br>pk[0]&nbsp;=&nbsp;objUserTable.Columns[0];&nbsp;<br>objUserTable.PrimaryKey&nbsp;=&nbsp;pk;&nbsp;<br>Application["UserTable"]&nbsp;=&nbsp;objUserTable;&nbsp;<br>}&nbsp;</p>
<p>/**////&nbsp;<br>///&nbsp;The&nbsp;Session_Start&nbsp;event&nbsp;adds&nbsp;user&nbsp;session&nbsp;information&nbsp;to&nbsp;<br>///&nbsp;Application["UserTable"].&nbsp;<br>///&nbsp;<br>public&nbsp;virtual&nbsp;void&nbsp;Session_Start(object&nbsp;sender,&nbsp;EventArgs&nbsp;e)&nbsp;<br>{&nbsp;<br>Application.Lock();&nbsp;<br>//Application.Lock&nbsp;();&nbsp;<br>DataTable&nbsp;objUserTable&nbsp;=&nbsp;(DataTable)Application["UserTable"];&nbsp;<br>DataRow&nbsp;objRow&nbsp;=&nbsp;objUserTable.NewRow();&nbsp;<br>Guid&nbsp;objGuid&nbsp;=&nbsp;Guid.NewGuid();&nbsp;<br>objRow[0]&nbsp;=&nbsp;objGuid;&nbsp;<br>Session["PfSessionID"]&nbsp;=&nbsp;objRow[0];&nbsp;<br>objRow[1]&nbsp;=&nbsp;0;&nbsp;<br>objRow[2]&nbsp;=&nbsp;false;&nbsp;<br>objUserTable.Rows.Add(objRow);&nbsp;<br>Application["UserTable"]&nbsp;=&nbsp;objUserTable;&nbsp;<br>Application.UnLock();&nbsp;<br>}&nbsp;</p>
<p><br>/**////&nbsp;<br>///&nbsp;The&nbsp;Session_End&nbsp;event&nbsp;deletes&nbsp;user&nbsp;session&nbsp;information&nbsp;from&nbsp;<br>///&nbsp;Application["UserTable"].&nbsp;<br>///&nbsp;<br>public&nbsp;virtual&nbsp;void&nbsp;Session_End(object&nbsp;sender,&nbsp;EventArgs&nbsp;e)&nbsp;<br>{&nbsp;<br>Application.Lock();&nbsp;<br>DataTable&nbsp;objUserTable&nbsp;=&nbsp;(DataTable)Application["UserTable"];&nbsp;<br>objUserTable.Rows.Find((Guid)Session["PfSessionID"]).Delete();&nbsp;<br>Application["UserTable"]&nbsp;=&nbsp;objUserTable;&nbsp;<br>Application.UnLock();&nbsp;<br>}</p>
<p><br>方案三：<br>&nbsp;&nbsp;&nbsp; protected void Login_Click(object sender, EventArgs e)<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //用户名<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; string sName = TextBox1.Text;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //生成Key&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; string sKey = sName + "_Login";<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //得到Cache中的给定Key的值&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; string sUser = Convert.ToString(Cache[sKey]);</p>
<p>&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; if (sUser == null || sUser == String.Empty)<br>&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; Session["username"] = sName;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //Cache中没有该Key的项目，表明用户没有登录，或者已经登录超时&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //TimeSpan 表示一个时间间隔，获取系统对session超时作的设置值<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //（如果考虑到允许用户再次登陆的时间小于session超时时间，可将此值设小）&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //TimeSpan SessTimeOut = new TimeSpan(0, 0, System.Web.HttpContext.Current.Session.Timeout, 0, 0);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //这里为了演示，把Cache保存时间间隔设置为了20秒<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TimeSpan SessTimeOut = new TimeSpan(0, 0, 0, 20, 0);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; HttpContext.Current.Cache.Insert(<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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sKey, <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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sKey, <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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; null, <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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DateTime.MaxValue, <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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SessTimeOut,<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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.Web.Caching.CacheItemPriority.NotRemovable, <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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; null<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;&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; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //启动Timer<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.Timer1.Enabled = true;</p>
<p>Label1.Text = "你好！" + sName + "欢迎光临";<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else<br>&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; //在Cache中发现该用户的记录，表示已经登录过，禁止再次登录&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Label1.Text = "对不起，你的用户身份已登陆";<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; catch (System.Exception ex)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Label1.Text = ex.Message;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; protected void Logout_Click(object sender, EventArgs e)<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //用户名<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; string sName = TextBox1.Text;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //生成Key&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; string sKey = sName + "_Login";</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //为了测试方便，设置了这个从Cache中移出登陆信息的方法<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; HttpContext.Current.Cache.Remove(sKey);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Label1.Text = Session["username"] + " 的用户登陆信息已从Cache清除!";<br>&nbsp;&nbsp;&nbsp; }<br><br>&nbsp;&nbsp;&nbsp; protected void TimerRunning_Tick(object sender, EventArgs e)<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (Session["username"] != null)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //用户名<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; string sName = TextBox1.Text;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //生成Key&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; string sKey = sName + "_Login";</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //得到Cache中的给定Key的值&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; string sUser = Convert.ToString(Cache[sKey]);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TimeSpan SessTimeOut = new TimeSpan(0, 0, 0, 20, 0);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (sUser != null)<br>&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; HttpContext.Current.Cache.Remove(sKey);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; HttpContext.Current.Cache.Insert(<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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sKey, <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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sKey, <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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; null, <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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DateTime.MaxValue, <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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SessTimeOut,<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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.Web.Caching.CacheItemPriority.NotRemovable, <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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; null<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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; );<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.Timer1.Enabled = false;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; }<br><br><br></p>
</font>
<img src ="http://www.cnitblog.com/MartinYao/aggbug/39104.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/MartinYao/" target="_blank">玄铁剑</a> 2008-01-20 18:34 <a href="http://www.cnitblog.com/MartinYao/articles/39104.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Design Templates</title><link>http://www.cnitblog.com/MartinYao/articles/32427.html</link><dc:creator>玄铁剑</dc:creator><author>玄铁剑</author><pubDate>Sat, 25 Aug 2007 14:40:00 GMT</pubDate><guid>http://www.cnitblog.com/MartinYao/articles/32427.html</guid><wfw:comment>http://www.cnitblog.com/MartinYao/comments/32427.html</wfw:comment><comments>http://www.cnitblog.com/MartinYao/articles/32427.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/MartinYao/comments/commentRss/32427.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/MartinYao/services/trackbacks/32427.html</trackback:ping><description><![CDATA[<table class=multicol>
    <tbody>
        <tr>
            <td class=innercol style="WIDTH: 100%" vAlign=top>
            <h1>Design Templates</h1>
            <p>Download these professionally designed Starter Kits and XHTML templates to get you started creating compelling, standards-compliant Web pages with ASP.NET 2.0. There are four Starter Kits below. These Starter Kits are complete web site solutions using ASP.NET&#8217;s latest controls (tree view control, grid view control, detail view control, login control, site map path controls and more). There are another 5 XHTML templates below. These are individual XHTML strict Web page templates (available each in two different colors) that are accompanied by design philosophy whitepapers that describe each template&#8217;s Section 508 conformance, page layout with CSS (page layouts using CSS floats or tables), Web page accessibility best practices and Web standard resources. All nine templates below are easily customizable and extendable to fit your own content.</p>
            <p>For introduction to the ASP.NET Master Pages template set, <a href="http://msdn2.microsoft.com/zh-cn/asp.net/aa336603"><u><font color=#0000ff>click here</font></u></a>.</p>
            <h3>ASP.NET Design Starter Kit Templates</h3>
            <table class=headlines_table>
                <tbody>
                    <tr>
                        <td class=headlines_td_image vAlign=top><a onclick="javascript:Track('ctl00_ctl01|ctl00_ctl02',this);" href="http://download.microsoft.com/download/3/1/3/3134bdea-cae1-4795-a685-d48140264a08/templatecommercecs0106.vsi"><img alt=Commerce src="http://msdn2.microsoft.com/zh-cn/asp.net/aa336613.commerce.jpg" align=left border=0></a></td>
                        <td class=headlines_td_text vAlign=top><a onclick="javascript:Track('ctl00_ctl01|ctl00_ctl03',this);" href="http://download.microsoft.com/download/3/1/3/3134bdea-cae1-4795-a685-d48140264a08/templatecommercecs0106.vsi"><strong><font color=#0000ff><u>Visual C# Commerce Design</u></font></strong></a><br>
                        <p><strong><a href="http://download.microsoft.com/download/0/e/2/0e201d64-1aee-4ffb-abfc-1fa24cecf757/templatecommerceVB0106.vsi"><u><font color=#0000ff>Visual Basic Commerce Design</font></u></a></strong></p>
                        <p>The "Commerce" template features three columns of content in a centered fixed/fluid layout. No layout tables, just CSS. The template showcases the new ASP.NET Treeview control for displaying hierarchical lists of items and shows how to skin it as part of a theme. The use of "Commerce" is not limited to online shop scenarios. Its three themes are well suited for other purposes as well, such as fan sites or even a personal web site. Section 508 accessibility, a dedicated print style sheet and several themed tabular controls, such as GridView and DetailsView complement its feature set. Included themes: Magnolia, Jazz, Snow.</p>
                        <p><em>Visual C# File Download Size: 1.0 MBVisual Basic File Download Size: 1.0 MB</em></p>
                        <p>For more information, view <a href="http://download.microsoft.com/download/d/2/4/d2484c54-101e-420c-b55b-d2ba0be68599/commerce.doc"><u><font color=#0000ff>"Commerce" template documentation</font></u></a>.</p>
                        </td>
                    </tr>
                </tbody>
            </table>
            <hr>
            <h3></h3>
            <table class=headlines_table>
                <tbody>
                    <tr>
                        <td class=headlines_td_image vAlign=top><a onclick="javascript:Track('ctl00_ctl01|ctl00_ctl04',this);" href="http://download.microsoft.com/download/2/3/2/2328890d-8ca1-4b7f-a138-08fca8a244bd/templatepersonalcs0106.vsi"><img alt=Personal src="http://msdn2.microsoft.com/zh-cn/asp.net/aa336613.personal.jpg" align=left border=0></a></td>
                        <td class=headlines_td_text vAlign=top><a onclick="javascript:Track('ctl00_ctl01|ctl00_ctl05',this);" href="http://download.microsoft.com/download/2/3/2/2328890d-8ca1-4b7f-a138-08fca8a244bd/templatepersonalcs0106.vsi"><strong><font color=#0000ff><u>Visual C# Personal Design</u></font></strong></a><br>
                        <p><strong><a href="http://download.microsoft.com/download/9/3/3/933adec5-f5d4-4501-b552-0e825a0cf0bf/templatepersonalVB0106.vsi"><u><font color=#0000ff>Visual Basic Personal Design</font></u></a></strong></p>
                        <p>Best suited for a personal or blog-style web site, this template has a hybrid fixed/fluid layout that organizes the content in two columns, with a horizontal menu at the top. It is a pure CSS layout that doesn&#8217;t rely on tables for arranging page content. Themes can be selected by the user and their preference will be restored upon the next visit by taking advantage of the new ASP.NET profile feature. The template contains a Section 508-compliant sample form, a print style sheet and a themed GridView control sample. Included themes: Green, Brown, Red.</p>
                        <p><em>Visual C# File Download Size: 1.0 MB Visual Basic File Download Size: 1.0 MB </em></p>
                        <p>For more information, view <a href="http://download.microsoft.com/download/d/2/4/d2484c54-101e-420c-b55b-d2ba0be68599/personal.doc"><u><font color=#0000ff>"Personal" template documentation</font></u></a>. </p>
                        </td>
                    </tr>
                </tbody>
            </table>
            <hr>
            <h3></h3>
            <table class=headlines_table>
                <tbody>
                    <tr>
                        <td class=headlines_td_image vAlign=top><a onclick="javascript:Track('ctl00_ctl01|ctl00_ctl06',this);" href="http://download.microsoft.com/download/9/8/0/980dc514-5e91-4608-b40c-c3aea45b00cb/templatecorporatecs0106.vsi"><img alt=Corporate src="http://msdn2.microsoft.com/zh-cn/asp.net/aa336613.corporate.jpg" align=left border=0></a></td>
                        <td class=headlines_td_text vAlign=top><a onclick="javascript:Track('ctl00_ctl01|ctl00_ctl07',this);" href="http://download.microsoft.com/download/9/8/0/980dc514-5e91-4608-b40c-c3aea45b00cb/templatecorporatecs0106.vsi"><strong><font color=#0000ff><u>Visual C# Corporate Design</u></font></strong></a><br>
                        <p><strong><a href="http://download.microsoft.com/download/e/f/2/ef2f6d60-4cc0-4e9d-a22d-5930999db677/templatecorporateVB0106.vsi"><u><font color=#0000ff>Visual Basic Corporate Design</font></u></a></strong></p>
                        <p>This template has been specifically designed to accommodate large amounts of content. It features a three-column, centered layout with a fixed with. Navigating the site is done though two correlated ASP.NET Menu controls and the SiteMapPath control. Using CSS, the page content is placed before the navigation in the XHTML source to improve accessibility and search-engine ranking. Placeholders and themes for including personalization features, such as the new Login control, are provided. The template is Section 508-compliant, contains a dedicated print style sheet and a themed GridView sample control. Included themes: Granite, Sand, Paper. </p>
                        <p><em>Visual C# File Download Size: 1.7 MBVisual Basic File Download Size: 1.0 MB</em></p>
                        <p>For more information, view <a href="http://download.microsoft.com/download/d/2/4/d2484c54-101e-420c-b55b-d2ba0be68599/corporate.doc"><u><font color=#0000ff>"Corporate Design" template documentation</font></u></a>.</p>
                        </td>
                    </tr>
                </tbody>
            </table>
            <hr>
            <h3></h3>
            <table class=headlines_table>
                <tbody>
                    <tr>
                        <td class=headlines_td_image vAlign=top><a onclick="javascript:Track('ctl00_ctl01|ctl00_ctl08',this);" href="http://download.microsoft.com/download/7/0/6/7061c0be-477a-44a3-ac02-a75ffd172f2d/templatesmallbusinesscs0106.vsi"><img alt="Small Business" src="http://msdn2.microsoft.com/zh-cn/asp.net/aa336613.smallbiz.jpg" align=left border=0></a></td>
                        <td class=headlines_td_text vAlign=top><a onclick="javascript:Track('ctl00_ctl01|ctl00_ctl09',this);" href="http://download.microsoft.com/download/7/0/6/7061c0be-477a-44a3-ac02-a75ffd172f2d/templatesmallbusinesscs0106.vsi"><strong><font color=#0000ff><u>Visual C# Small Business Design</u></font></strong></a><br>
                        <p><strong><a href="http://download.microsoft.com/download/e/3/6/e36ac37c-92fd-462b-b55e-7d5bcfdaf69f/templatesmallbusinessVB0106.vsi"><u><font color=#0000ff>Visual Basic Small Business Design</font></u></a></strong></p>
                        <p>This template features two columns that are arranged in a centered, fixed-width layout. It is best suited for a small to medium-sized site. Its themes are widely different from each other, demonstrating the visual potential of ASP.NET themes and skins. Additional highlights include: table-less layout; source-ordered XHTML, placing the page content before the navigation elements; Section 508-compliant sample form; ASP.NET Menu and SiteMapPath controls; varying header images; dedicated print style sheet that formats the pages for output in a printer; themed GridView control sample. Included themes: Fruits, Forest, Literature. </p>
                        <p><em>Visual C# File Download Size: 1.3 MBVisual Basic File Download Size: 1.3 MB</em></p>
                        <p>For more information, view <a href="http://download.microsoft.com/download/d/2/4/d2484c54-101e-420c-b55b-d2ba0be68599/small_business.doc"><u><font color=#0000ff>"Small Business" template documentation</font></u></a>.</p>
                        </td>
                    </tr>
                </tbody>
            </table>
            <hr>
            <p>These templates illustrate high quality design principles combined with standards driven code. Each template is XHTML 1.1 Strict, Section 508/WCAG conforming and works across many browsers. The accompanying documentation describes how the template was built, the thought processes, tradeoffs, and workarounds necessary to build it. It also contains instructions on how to easily customize and change the templates for use in your site. You will find these HTML templates to be an excellent starting point when beginning to build out dynamic, data-driven Web sites with Microsoft Visual Studio and Microsoft Visual Web Developer. We hope they'll give both designers and developers deep insight into their creation process while providing you with a workable starting point.</p>
            <h3></h3>
            <table class=headlines_table>
                <tbody>
                    <tr>
                        <td class=headlines_td_image vAlign=top><a onclick="javascript:Track('ctl00_ctl01|ctl00_ctl10',this);" href="http://download.microsoft.com/download/0/b/1/0b155bb5-6890-4af0-94a7-7b5b87c16796/basic06.msi"><img alt=Basic src="http://msdn2.microsoft.com/zh-cn/asp.net/aa336613.thumb-vs-design-template-basic.jpg" align=left border=0></a></td>
                        <td class=headlines_td_text vAlign=top><a onclick="javascript:Track('ctl00_ctl01|ctl00_ctl11',this);" href="http://download.microsoft.com/download/0/b/1/0b155bb5-6890-4af0-94a7-7b5b87c16796/basic06.msi"><strong><u><font color=#0000ff>Basic</font></u></strong></a><br>
                        <p><strong>The Basic template</strong> <em>File Download Size: 603 KB</em></p>
                        <ul>
                            <li>2 column layout
                            <li>Both columns are fixed width</li>
                        </ul>
                        <p><em>File Download Size: 603 KB</em></p>
                        <p>For more information, view <a href="http://download.microsoft.com/download/d/2/4/d2484c54-101e-420c-b55b-d2ba0be68599/basic.doc"><u><font color=#0000ff>"Basic" template documentation</font></u></a>. </p>
                        </td>
                    </tr>
                    <tr>
                        <td class=headlines_td_image vAlign=top><a onclick="javascript:Track('ctl00_ctl01|ctl00_ctl12',this);" href="http://download.microsoft.com/download/d/8/2/d8291134-ac4d-45cc-b7eb-446564bb095e/fun06.msi"><img alt=Fun src="http://msdn2.microsoft.com/zh-cn/asp.net/aa336613.thumb-vs-design-template-fun.jpg" align=left border=0></a></td>
                        <td class=headlines_td_text vAlign=top><a onclick="javascript:Track('ctl00_ctl01|ctl00_ctl13',this);" href="http://download.microsoft.com/download/d/8/2/d8291134-ac4d-45cc-b7eb-446564bb095e/fun06.msi"><strong><u><font color=#0000ff>Fun</font></u></strong></a><br>
                        <p><strong>The Fun template</strong> </p>
                        <ul>
                            <li>2 column layout
                            <li>Both columns are stretchy
                            <li>This layout uses absolute positioning</li>
                        </ul>
                        <p><em>File Download Size: 393 KB</em></p>
                        <p>For more information, view <a href="http://download.microsoft.com/download/d/2/4/d2484c54-101e-420c-b55b-d2ba0be68599/fun.doc"><u><font color=#0000ff>"Fun" template documentation</font></u></a>. </p>
                        </td>
                    </tr>
                    <tr>
                        <td class=headlines_td_image vAlign=top><a onclick="javascript:Track('ctl00_ctl01|ctl00_ctl14',this);" href="http://download.microsoft.com/download/1/f/d/1fd59802-643c-49d2-9586-26216d6840e8/grid06.msi"><img alt=Grid src="http://msdn2.microsoft.com/zh-cn/asp.net/aa336613.thumb-vs-design-template-grid.jpg" align=left border=0></a></td>
                        <td class=headlines_td_text vAlign=top><a onclick="javascript:Track('ctl00_ctl01|ctl00_ctl15',this);" href="http://download.microsoft.com/download/1/f/d/1fd59802-643c-49d2-9586-26216d6840e8/grid06.msi"><strong><u><font color=#0000ff>Grid</font></u></strong></a><br>
                        <p><strong>The Grid template</strong> </p>
                        <ul>
                            <li>3 column layout is used in the header section
                            <li>2 column layout is used for the body section
                            <li>All the columns are "stretchy"</li>
                        </ul>
                        <p><em>File Download Size: 570 KB</em></p>
                        <p>For more information, view <a href="http://download.microsoft.com/download/d/2/4/d2484c54-101e-420c-b55b-d2ba0be68599/grid.doc"><u><font color=#0000ff>"Grid" template documentation</font></u></a>. </p>
                        </td>
                    </tr>
                    <tr>
                        <td class=headlines_td_image vAlign=top><a onclick="javascript:Track('ctl00_ctl01|ctl00_ctl16',this);" href="http://download.microsoft.com/download/c/7/a/c7a07f0e-4778-4d73-a163-f873a06b18ed/rounded06.msi"><img alt=Rounded src="http://msdn2.microsoft.com/zh-cn/asp.net/aa336613.thumb-vs-design-template-rounded.jpg" align=left border=0></a></td>
                        <td class=headlines_td_text vAlign=top><a onclick="javascript:Track('ctl00_ctl01|ctl00_ctl17',this);" href="http://download.microsoft.com/download/c/7/a/c7a07f0e-4778-4d73-a163-f873a06b18ed/rounded06.msi"><strong><u><font color=#0000ff>Rounded</font></u></strong></a><br>
                        <p><strong>The Rounded template</strong> </p>
                        <ul>
                            <li>2 column layout
                            <li>Both columns are fixed width</li>
                        </ul>
                        <p><em>File Download Size: 495 KB</em></p>
                        <p>For more information, view <a href="http://download.microsoft.com/download/d/2/4/d2484c54-101e-420c-b55b-d2ba0be68599/rounded.doc"><u><font color=#0000ff>"Rounded" template documentation</font></u></a>. </p>
                        </td>
                    </tr>
                    <tr>
                        <td class=headlines_td_image vAlign=top><a onclick="javascript:Track('ctl00_ctl01|ctl00_ctl18',this);" href="http://download.microsoft.com/download/f/d/e/fdea6532-984c-43ec-b41f-ea6b92d33edb/simple06.msi"><img alt=Simple src="http://msdn2.microsoft.com/zh-cn/asp.net/aa336613.thumb-vs-design-template-simple.jpg" align=left border=0></a></td>
                        <td class=headlines_td_text vAlign=top><a onclick="javascript:Track('ctl00_ctl01|ctl00_ctl19',this);" href="http://download.microsoft.com/download/f/d/e/fdea6532-984c-43ec-b41f-ea6b92d33edb/simple06.msi"><strong><u><font color=#0000ff>Simple</font></u></strong></a><br>
                        <p><strong>The Simple template</strong> </p>
                        <ul>
                            <li>3 column layout
                            <li>The side columns are fixed width. The middle column is "stretchy"</li>
                        </ul>
                        <p><em>File Download Size: 420 KB</em></p>
                        <p>For more information</p>
                        </td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<img src ="http://www.cnitblog.com/MartinYao/aggbug/32427.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/MartinYao/" target="_blank">玄铁剑</a> 2007-08-25 22:40 <a href="http://www.cnitblog.com/MartinYao/articles/32427.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>SSIS  Interface not registered</title><link>http://www.cnitblog.com/MartinYao/articles/29894.html</link><dc:creator>玄铁剑</dc:creator><author>玄铁剑</author><pubDate>Mon, 16 Jul 2007 00:28:00 GMT</pubDate><guid>http://www.cnitblog.com/MartinYao/articles/29894.html</guid><wfw:comment>http://www.cnitblog.com/MartinYao/comments/29894.html</wfw:comment><comments>http://www.cnitblog.com/MartinYao/articles/29894.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/MartinYao/comments/commentRss/29894.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/MartinYao/services/trackbacks/29894.html</trackback:ping><description><![CDATA[<div class=postsub>
<h2><a id=_ctl0__ctl0__ctl0__ctl0_Month__ctl0_postlist__ctl0_EntryItems__ctl0_PostTitle href="http://www.vsteamsystemcentral.com/cs/blogs/applied_team_system/archive/2006/03/30/53.aspx"><u><font color=#0000ff>Interface Not Registered / Class Not Registered Error in SSIS</font></u></a></h2>
<p><font face=Verdana color=#000080>I ran into a couple errors recently when trying to create new SQL Server Integration Services (SSIS) projects. One error stated:</font></p>
<p><font face="Times New Roman" color=#000000 size=4>Failed to save package file "C:\Documents and Settings\Administrator\Local Settings\Temp\1\tmp2B.tmp" with error 0x80040155 "Interface not registered".<br></font><font face=Verdana color=#000080></font></p>
<p><font face=Verdana color=#000080>The other stated:</font></p>
<font face=Verdana color=#000080>
<p><font size=4><font face="Times New Roman" color=#000000>Failed to save package file "C:\Documents and Settings\Administrator\Local Settings\Temp\1\tmp2B.tmp" with error 0x80040154 "Class not registered".</font><br></font><font face=Verdana color=#000080></font></p>
<p><font face=Verdana color=#000080>These errors were encountered on a new laptop and a new virtual PC. Both had Visual Studio 2005 installed, so I suspected some sort of conflict. On the VPC, I loaded VS 2005 first, followed by SQL Server 2005 Developer. I noticed the client tools didn't install, although I thought I'd checked that box. To correct, I executed the setup for client tools. When completed, my client tools were available and creating an SSIS project succeeded.</font></p>
<p>But the laptop wasn't so simple. I thought "I have a solution!" and went about re-installing the client tools, but it didn't correct the issue! </p>
<p>So I popped on Google and searched for similar issues. I found a helpful post at MSDN Forums: <a href="http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=53694&amp;SiteID=1"><u><font color=#0000ff>http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=53694&amp;SiteID=1</font></u></a>. Executing two RegSvr32 commands did the trick:</p>
<p><font face="Times New Roman" color=#000000 size=4>regsvr32 msxml3.dll<br>regsvr32 msxml6.dll&nbsp;</font></p>
<p>When all else fails, read the instructions!</p>
<p>:{&gt; Andy</p>
</font></div>
<img src ="http://www.cnitblog.com/MartinYao/aggbug/29894.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/MartinYao/" target="_blank">玄铁剑</a> 2007-07-16 08:28 <a href="http://www.cnitblog.com/MartinYao/articles/29894.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>區域打印</title><link>http://www.cnitblog.com/MartinYao/articles/28622.html</link><dc:creator>玄铁剑</dc:creator><author>玄铁剑</author><pubDate>Sun, 17 Jun 2007 05:20:00 GMT</pubDate><guid>http://www.cnitblog.com/MartinYao/articles/28622.html</guid><wfw:comment>http://www.cnitblog.com/MartinYao/comments/28622.html</wfw:comment><comments>http://www.cnitblog.com/MartinYao/articles/28622.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/MartinYao/comments/commentRss/28622.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/MartinYao/services/trackbacks/28622.html</trackback:ping><description><![CDATA[<p>&lt;html&gt;<br>&lt;head&gt;打印</p>
<p>&lt;scrīpt language="javascrīpt"&gt;<br>function print()<br>{<br>&nbsp;&nbsp;&nbsp; document.body.innerHTML=document.getElementById('print').innerHTML;</p>
<p>&nbsp;&nbsp;&nbsp; window.print();<br>}<br>&lt;/scrīpt&gt;</p>
<p>&lt;/head&gt;<br>&lt;body&gt;</p>
<p>&lt;form id="form1" runat="server"&gt;<br>&lt;div id="print"&gt;<br>&lt;table&gt;&lt;tr&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;<br>&lt;/div&gt;</p>
<p>&lt;asp:Button ID="butPrint" runat="server" Text="打印" OnClientClick="Print();" CssClass="cmd_print" /&gt;<br>&lt;/form&gt;<br>&lt;/body&gt;<br>&lt;/html&gt;</p>
<p>&nbsp;</p>
<p>使用CSS区域打印：</p>
<p>定义一个.noprint的class，将不打印的内容放入这个class内。</p>
<p>&lt;style media=print type="text/css"&gt;&nbsp;&nbsp; <br>.noprint{visibility:hidden}&nbsp;&nbsp; <br>&lt;/style&gt;&nbsp;&nbsp; </p>
<p>&lt;p class="noprint"&gt;将不打印的代码放在这里。&lt;/p&gt;<br>&lt;a href="javascrīpt:window.print()"打印&lt;/a&gt;</p>
<p>&nbsp;</p>
<img src ="http://www.cnitblog.com/MartinYao/aggbug/28622.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/MartinYao/" target="_blank">玄铁剑</a> 2007-06-17 13:20 <a href="http://www.cnitblog.com/MartinYao/articles/28622.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Building Tree View on demand using AJAX</title><link>http://www.cnitblog.com/MartinYao/articles/28364.html</link><dc:creator>玄铁剑</dc:creator><author>玄铁剑</author><pubDate>Tue, 12 Jun 2007 13:38:00 GMT</pubDate><guid>http://www.cnitblog.com/MartinYao/articles/28364.html</guid><wfw:comment>http://www.cnitblog.com/MartinYao/comments/28364.html</wfw:comment><comments>http://www.cnitblog.com/MartinYao/articles/28364.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/MartinYao/comments/commentRss/28364.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/MartinYao/services/trackbacks/28364.html</trackback:ping><description><![CDATA[<ul>
    <li><a href="http://www.codeproject.com/useritems/TreeViewAjax/ajaxtree.zip">Download source files - 20 Kb</a> </li>
</ul>
<p><img height=453 alt="Sample Image - ajaxtreeview.gif" src="http://www.codeproject.com/useritems/TreeViewAjax/ajaxtreeview.gif" width=512></p>
<h2>Introduction</h2>
<p><span>AJAX or Asynchronous JavaScript and XML is a free framework for quickly creating a new generation of more efficient, more interactive and highly-personalized Web experiences that work across all the most popular browsers. <st1:city w:st="on"><st1:place w:st="on">AJAX</st1:place></st1:city> is relatively new phenomenon, based on technologies that aren&#8217;t quite new, for creating a way for businesses to interact with customers over the internet.</p>
<p><span>&nbsp;</p>
<p><span>In this article using the current <st1:city w:st="on"><st1:place w:st="on">AJAX</st1:place></st1:city> technique,(in it&#8217;s simplest way) to implement an on demand tree view. Since XML is common standard and easy to implement I am using XML in my sample application rather than any database. I have found another article here that to build a <a href="http://www.codeproject.com/aspnet/ajax_treeview.asp">tree view</a> using this <st1:city w:st="on"><st1:place w:st="on">AJAX</st1:place></st1:city>.</p>
<p><span>&nbsp;</p>
<h2><span>History: (Skip this section if you are feeling bored)</h2>
<p><span>This technology was introduced first by Microsoft (from my best knowledge) back in 1999, and had been known as &#8220;DHTML / JavaScript web application with remote calls&#8221;. The whole idea of the technology was to allow an internet browser to make an asynchronous HTTP call to remote pages/services, and update a current web page with the received results without refreshing the whole page. By creators&#8217; opinion, this should have improved customers&#8217; experience, making HTTP pages look and feel very similar to Windows applications.</p>
<p>Because the core implementation of this technology was based on internet browser functionality, the usability was very limited at that time. But several years later, the technology has been reborn with new browsers support and massive implementation by such giants as Google, Amazon.com, eBay, etc.</p>
<p>Today, it&#8217;s known as <st1:city w:st="on"><st1:place w:st="on">AJAX</st1:place></st1:city>, and considered as a natural part of any dynamic web page with advanced user experience. Many ready made frameworks are also available on the internet like <a href="http://ajax.asp.net/Default.aspx">Microsoft AJAX/ATLAS</a> , <a href="http://www.backbase.com/">Enterprise AJAX</a> are few of them.</p>
<h3>Why <st1:city w:st="on"><st1:place w:st="on">AJAX</st1:place></st1:city>:</h3>
<p>This sample application provides a tree view implementation from an XML file. In real scenarios, I have noticed the processing of building the complete tree structure (with many levels-children) and rendering into the browser takes quite long time. After attempting different approaches, one of them was building the tree on demand. But I don&#8217;t like the Post Back (I am sure you too). Then I found is <st1:city w:st="on"><st1:place w:st="on">AJAX</st1:place></st1:city> is the best bet. By this we could avoid a post back and flickering of the browser rather we&#8216;re just changing the portion of the page hence reducing the traffic in the server and enhance the performance.</p>
<h2><span>Working:</h2>
<p><span>The idea is to build a tree structure using UL and LI tags and using a style sheet and some JavaScript all together give some real effects. On Page Load or any Pre Render event, the Root node will be loaded, from that on click of the plus/minus images you can expand or collapse the tree. You can notice that the child node is generated on the fly (off course you can cache the data source). Since <st1:city w:st="on"><st1:place w:st="on">AJAX</st1:place></st1:city> is asynchronous, I am displaying an image to indicate that a request is already made to the server.</p>
<h2>Implementation:</h2>
<p>The Code files include </p>
<p><em>TreeSource.XML</em> -&gt; The source file where the tree structure is defined.</p>
<p><span>&lt;?<span>xml<span> <span>version<span>="1.0"<span> <span>encoding<span>="utf-8"<span> <span>?&gt;</p>
<p><span>&lt;<span>Nodes<span>&gt;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>&lt;<span>Node<span> <span>NodeID<span>="0"<span> <span>NodeName<span>="World"<span> <span>ParentID<span>="-1"&gt;&lt;/<span>Node<span>&gt;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>&lt;<span>Node<span> <span>NodeID<span>="1"<span> <span>NodeName<span>="<st1:place w:st="on">North America</st1:place>"<span> <span>ParentID<span>="0"&gt;&lt;/<span>Node<span>&gt;</p>
<p><span>&lt;<span>Node<span> <span>NodeID<span>="13"<span> <span>NodeName<span>="<st1:country-region w:st="on"><st1:place w:st="on">United States</st1:place></st1:country-region>"<span> <span>ParentID<span>="1"&gt;&lt;/<span>Node<span>&gt;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span></p>
<p><em>AJAXroutine.js</em> -&gt; The JavaScript file contain all the required methods to handle the asynchronous calls and standard <st1:city w:st="on"><st1:place w:st="on">AJAX</st1:place></st1:city> methods. Java script is the core component in this technology as you can see. It handles the change detection, data request receipt, and placing the data on the page.</p>
<p><span>&nbsp;</p>
<p><span>//To toggle the node image and expand collapse</p>
<p><span>function<span> Toggle(node){</p>
<p><span><span><span><span>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>// Unfold the branch if it isn't visible</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>if (node.nextSibling.style)<span>&nbsp;&nbsp; {<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>if (node.childNodes.length &gt; 0)<span>&nbsp;&nbsp;&nbsp;&nbsp; {</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>if (node.childNodes.item(0).nodeName == "IMG"){</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;...</p>
<p><span><span>&nbsp;</p>
<p><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span>The core function for the asynchronous request is</p>
<p><span>//The core function to get the xml http request object</p>
<p><span>function<span> GetXmlHttpObject(){ </p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>var objXMLHttp=<span>null</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>if (window.XMLHttpRequest){</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;...</p>
<p><span><span>&nbsp;</p>
<p><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span>The HTML will looks like the following tags.</p>
<p>&lt;ul id="treeUL" class="treeUL"&gt;</p>
<p>&lt;li id='10'&gt;</p>
<p>&lt;span title='World' valign='bottom' onclick="javascript:if (Toggle(this))showContentsEx('divTree0','0','');"&gt;</p>
<p>The client SPAN will be call the following function to invoke the server request</p>
<p><span>/***********************************************************</p>
<p><span>//Builds the query string and invoke a request to the server </p>
<p><span>//using javascript async call</p>
<p><span>/***********************************************************/</p>
<p><span>function<span> showContentsEx(div, str, lmsg){ ...</p>
<p><em>WebForm1.aspx</em>-&gt; The sample .aspx file. It has the root level hard coded in this sample. I have put some styles to look nice. You can build that section also on the fly. </p>
<p><em>WebForm1.aspx.cs</em> -&gt; The code behind of the .aspx file. </p>
<p><em>GetTreeContents.aspx</em> -&gt; Empty in the design mode</p>
<p>GetTreeContents.aspx.cs -&gt; All the client requests are processing this code behind and renders into the browser. When the client http request is made the following scripts will execute on the server and renders the HTML on the browser. </p>
<span>
<p><span>private<span> <span>void Page_Load(<span>object sender, System.EventArgs e)</p>
<p><span>{<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </p>
<p><span><span><span>if<span> (!IsPostBack)</p>
<p><span><span>&nbsp;{</p>
<p>&nbsp;</p>
<p><span>//id passed thro' the url query string</p>
<p><span>string<span> key = Request.QueryString["q"]; </p>
<p><span>&nbsp;</p>
<p><span>if<span> (key != <span>null)</p>
<p><span>{</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>//read the xml tree file from the web.config file </p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>//add the following key in web.config</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>//&lt;appSettings&gt;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>//&lt;add key="XmlTree" value="~/TreeSource.XML" /&gt;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String path = Server.MapPath(ConfigurationSettings.AppSettings["XmlTree"]);</p>
<p><span>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>//I use XmlDocument object here to manipulate Xml- You can use other objects</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>//You can cache the XmlDocument.OuterXml string</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; XmlDocument xmlTree = <span>new XmlDocument();</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; xmlTree.Load(path);</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.IO.StringReader xmlSR = <span>new System.IO.StringReader(xmlTree.OuterXml);</p>
<p><span>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>//If you want to use database driven tree you skip above codes</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>//but the columns should be there in the data table</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DataSet ds = <span>new DataSet();</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ds.ReadXml(xmlSR);</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DataView dv = ds.Tables[0].DefaultView;</p>
<p><span>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>//filter for the parent</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dv.RowFilter = " ParentID = '" + key + "'";</p>
<p><span>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>//render the brower with a &lt;UL&gt; tag and &lt;LI&gt; list</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>//I have used some styles</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Response.Write ("&lt;ul class='wTreeStyle'&gt;"); </p>
<p><span>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>//write all the children here</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>for(<span>int i=0; i &lt; dv.Count; i++)</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<span>&nbsp;&nbsp;&nbsp;&nbsp; </p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>//formatted &lt;LI&gt; tag - </p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Response.Write (<span>string.Format("&lt;li id='{0}'&gt;&lt;span valign='bottom' title='{1}' onclick=\"javascript:if (Toggle(this))showContentsEx('divTree{0}','{0}','');\"&gt;&lt;IMG<span>&nbsp; align='bottom' SRC='plus.gif'&gt; &lt;span class='treeStyleNode' &gt;{1}&lt;/span&gt;&lt;/span&gt;&lt;span id ='divTree{0}'&gt;&lt;/span&gt;&lt;/li&gt;", </p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dv[i]["NodeID"].ToString(), dv[i]["NodeName"].ToString())); </p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Response.Write ("&lt;/ul&gt;"); </p>
<p><span>}<span>&nbsp;.<span><span>..&nbsp;&nbsp;</p>
<p><span><span><span><span>&nbsp;&nbsp;&nbsp;&nbsp; </p>
<p>Images -&gt; Loading.gif, plus.gif, minus.gif </p>
<p>Web.config - &gt; for the key</p>
<p><span>&lt;<span>add<span> <span>key<span>="XmlTree"<span> <span>value<span>="~/TreeSource.XML"<span> <span>/&gt;</p>
<p>For the simplicity, I am putting only the required codes since I want to share the implementation, you can customize or enhance in your projects.</p>
<h2>Conclusion:</h2>
<p>This article is aimed to illustrate the simplicity of using the <st1:city w:st="on"><st1:place w:st="on">AJAX</st1:place></st1:city> technology in any ASP.NET application. It is up to the developers who want to use the technology in this way. I just did a practical implementation of the technology since the core code is already in place. Currently I tested the application in MS Visual Studio V.7/ MDE 2003 and MS .NET Framework 1.1. And C# - ASP.NET.</p>
<h2>Credits:</h2>
</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>
<img src ="http://www.cnitblog.com/MartinYao/aggbug/28364.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/MartinYao/" target="_blank">玄铁剑</a> 2007-06-12 21:38 <a href="http://www.cnitblog.com/MartinYao/articles/28364.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Using authentication certificates to connect to web services with Windows form applications</title><link>http://www.cnitblog.com/MartinYao/articles/28363.html</link><dc:creator>玄铁剑</dc:creator><author>玄铁剑</author><pubDate>Tue, 12 Jun 2007 13:16:00 GMT</pubDate><guid>http://www.cnitblog.com/MartinYao/articles/28363.html</guid><wfw:comment>http://www.cnitblog.com/MartinYao/comments/28363.html</wfw:comment><comments>http://www.cnitblog.com/MartinYao/articles/28363.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/MartinYao/comments/commentRss/28363.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/MartinYao/services/trackbacks/28363.html</trackback:ping><description><![CDATA[<ul class=download>
    <li><a href="http://www.codeproject.com/csharp/SSLViaCertification/coolcode_src.zip"><u><font color=#0000ff>Download source - 16.3 Kb</font></u></a> </li>
</ul>
<p><img height=100 src="http://www.codeproject.com/csharp/SSLViaCertification/coolcode1.gif" width=130></p>
<h2>Introduction</h2>
<p>This article explains how to use a certificate to connect to, for example, a web service. Do you know the floppies or USB Pens that some banks and IT Companies give to their employees or customers that permit them to connect to services via the internet using a security file and channel? Well, we will learn how to use a similar file in this article, which is divided into six parts:</p>
<ol>
    <li>Environment Preparation
    <li>Server Certificate Request
    <li>Generate the Certificate
    <li>Activate the SSL Security Channel Communication on the IIS Web Server
    <li>Request and Obtain a User Certificate
    <li>Use Certificate with Windows Form </li>
</ol>
<p>For this article I used three different machines:</p>
<ul>
    <li>A Windows 2000 Server with Certification Authority
    <li>Two Windows XP Professional Edition machines, one with IIS Web Server </li>
</ul>
<p>You can use only one Windows XP Professional machine; I use the second machine to ensure that the certification file and the software that I develop are the only two things that I must take away to connect to the web service. Good luck! </p>
<h2>Environment Preparation</h2>
<p>In this section we create the two projects that we will use to try our certification program. I won't explain the two projects in detail; I presume that you are expert enough to create two standard projects with Visual Studio 2005. </p>
<p>All right, start creating a new website project. Choose the ASP.NET Web Service template. Attention! It is important that you use a real web server and not the emulator. Therefore, if you don't have IIS Web Server or something similar installed, install it and then choose the <code>HTTP</code> option from the combo location of the VS Project. For the name of the application, I chose <code>http:<span class=cpp-comment>//localhost/WebServSSL</span></code>. Visual Studio takes care of most of what you need; you only have to configure the website appropriately. If you build the project you'll have just one method in the service, the classic "hello word" example. I suggest we use this for our test, but if you don't like it you can create another function that you prefer. The second project that you must now create is a Windows Application. Create it in the same solution as the Web Service; I called mine <code>testSSLWebServ</code>. In the new project, add a Web Reference from the solution explorer and choose your web service. I called the namespace for the service <code>remoteWebServSSL</code>. All right, the last pass is to add a button on the form and call the unique method of the Web Service that we have. Here 's some code: </p>
<pre lang=cs>remoteWebServSSL.Service srv = new remoteWebServSSL.Service();
MessageBox.Show(<span class=cpp-string>"The Web Service say: "</span> + srv.HelloWorld());</pre>
<p>Important: Some problems can arise from the firewall settings of the machine. Be sure to turn off the firewall; you can configure it in a second time. </p>
<p>Now we can start. </p>
<h2>Server certificate request</h2>
<p>Next, to activate the secure channel on our web site, we must go to the properties of the root Web Server and set the Server Certificate. There's more than one mode to do this; you can choose ones you prefer. I chose to connect to the CA Web Server to ask for the Certificate. Therefore I must create a Certification Request and ask the CA a second time. Click on the Server Certificate button. The certificate wizard will then appear; click Next. In this dialog choose Create a New Certificate and click Next. The only option that you can choose now is "Prepare the request now, but send it later;" again click Next. In this form you must choose the name of the certificate that you will install on your web server. Leave the other parameter as it is and go on. Now write something for the organization and organization unit. Go on. This next step is important: the Common Name required is the name of your machine and is, therefore, the name of the web server that responds to your web server. For example, my machine name is <code>WorkStatio23</code> and the http address that I write when I navigate on my web server is <code>http:<span class=cpp-comment>//WorkStatio23/WebServSSL</span></code>. The Common Name that I must choose is <code>WorkStatio23</code>. Also insert the State/province and City/locality data and then go on. As a last step, save your Certification Request; choose a location and click Next. Read the Request File Summary, click Next and Finish. The Certification Request has now been created. </p>
<h2>Generate the certificate</h2>
<p>Open the generated Text file on your computer from the directory where you chose to save it. Select all the text in the file and copy it. Now it's time to use Windows 2000 Server with the CA. Ensure that your CA is configured to accept the request by the Web Server and that it releases the certificate immediately, without user participation. Connect to the CA Web Server using the related address. For example, my Windows 2000 Server machine is named <code>w2ksrw</code>. Therefore the address to request the certification is <code>http:<span class=cpp-comment>//w2ksrw/CertSrv</span></code>. Next, choose the second option "Request a certificate" and click Next. Choose the "Advanced request" option and click Next again. Choose the second available option: "Submit a certificate request using a base64 encoded PKCS #10 file or a renewal request using a base64 encoded PKCS #7 file." Click Next. Since we already have the Text file code copied to memory, just paste it in the relevant textbox control (the first) and click Next. Now you are ready to download the certificate. Download the two certificates on the result page: "Download CA certificate" and "Download CA certification path." </p>
<h2>Activate the SSL security channel communication on the IIS Web Server</h2>
<p>Select the certificate file with the <em>.p7b</em> extension. For me, this is <em>certnew.p7b</em>. Double click on your file once you find it. Note that the certificate is not valid. Click the "Install certificate" button and then click Next until Finish; confirm the last message and the certificate is now installed. Now finish the activation of the SSL channel on the Web Server. Launch your IIS configuration tool, go on the root Web Server (Default Web Site), right click and select Properties. Return to the Directory Security and re-click the Server Certificate button. Click Next. Note that the options have changed. Choose the "Process the pending request and install the certificate" option and click Next. Browse for the other certificate that you obtain from the CA. Click Next two times and then Finish. Well, you are ready to use your https connection. Try your web server in https and enjoy. For this example, we must enforce the use of the SSL channel on our Web Services. Therefore, on the IIS, go to the properties of WebServSSL, choose the Directory Security tab and click the Edit button. Check the "Require secure channel (SSL)" checkbox and the "Require client certificates" option in the Client Certificates group. Close all windows and try your web services now using your browser.</p>
<p><img height=233 src="http://www.codeproject.com/csharp/SSLViaCertification/coolcode2.gif" width=443></p>
<h2>Request and obtain a user certificate</h2>
<p>This step will be fast and simple. Using your browser, go to the Web Server of the CA using the same address (<code>http:<span class=cpp-comment>//w2ksrw/CertSrv</span></code>). Choose the "Request a certificate" option and click Next. Choose the "Advanced request" option and then Next. Leave the "Submit a certificate request to this CA using a form" option selected and click Next. Fill in the Name and the E-Mail fields under Identifying Information with whatever you want and leave the other field as is. For the Intended Purpose field, choose Client Authentication Certificate. For the last setting, set the "Mark keys as exportable" option. Bypass the other field and click Submit. Answer "yes" to the question and finally install the certificate.</p>
<p>Note: In this step, I have encountered problems using the Certification Authority website. The "Downloading ActiveX Control" message did not disappear. If you have the same problem, you can resolve it by installing a fix for the Windows 2000 Advanced Server. Refer to KB323172 of the Microsoft site and find the relative fix <em>q323172_W2K_SP4_X86_EN.exe</em>.</p>
<p><img height=483 src="http://www.codeproject.com/csharp/SSLViaCertification/coolcode3.gif" width=409></p>
<h2>Use certificate with Windows form</h2>
<p>You can try more tests with your example, if you'd like, before continuing with my instructions here. After you're satisfied, launch the Internet Explorer browser. Select Tools and go to Internet Options. Select the Content tab and click on the Certificates button. You can immediately see the Personal tab selected and, in the list control, you can see your personal certificate (Andy74 for me). Select the specified certificate and click on the Export button. In the wizard panel choose Next, choose the "Yes, export the private key" option and click Next again. Here you can export only in the PFX format; leave the default option and click Next. Insert a password. For simplicity, I chose a very simple password, "password." Then click Next. Choose where to export the certificate and click Next again. Click Finish and OK in the message box. Now, return to your project in Visual Studio and add this line after the creation of the web reference instance: </p>
<pre lang=cs>srv.ClientCertificates.Add(
new System.Security.Cryptography.X509Certificates.X509Certificate2(
@<span class=cpp-string>"c:\Andy74cert.pfx"</span>, <span class=cpp-string>"password"</span>));</pre>
<p>Obviously, change the path of the certificate where you have saved it. Now on the machine where you have the web services, go to the administrative tools section of the control panel and launch the Server Extensions Administrator. Select File -&gt; Add/Remove Snap-in. On the next window click the Add button, select Certificates and finally click the Add button. Select the Computer Account option and click Next. Leave the "local computer" (the computer this console is running on) option selected and click Finish. Close the active window and finally the OK button. Open the Certificates (Local Computer) node in the tree view, then Trusted Root Certification Authorities and the Certificates node. Right click on the node: All Tasks -&gt; Imports to import another wizard. Click Next. Remember the <em>.p7b</em> certificate that we download from the certification authority in the fourth step. Browse to that file and select it; then click Next. Choose "Place all certificates in the following store" and then the "Trusted Root Certification Authorities" folder; click Next and then Finish. All right, you are ready to try your final application. To really test your application, you can use other machines never used up to now. Copy your application and your <em>.pfx</em> file (certificate) to where you want, but remember to place the certification file in the same folder where you load from the application, or use a parameter to load it from whichever you'd prefer. </p>
<img src ="http://www.cnitblog.com/MartinYao/aggbug/28363.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/MartinYao/" target="_blank">玄铁剑</a> 2007-06-12 21:16 <a href="http://www.cnitblog.com/MartinYao/articles/28363.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>