﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-鹰翔宇空-文章分类-技术文摘</title><link>http://www.blogjava.net/TrampEagle/category/6891.html</link><description>学习和生活
</description><language>zh-cn</language><lastBuildDate>Tue, 27 Feb 2007 08:40:34 GMT</lastBuildDate><pubDate>Tue, 27 Feb 2007 08:40:34 GMT</pubDate><ttl>60</ttl><item><title>JAVA字符的编码</title><link>http://www.blogjava.net/TrampEagle/articles/49337.html</link><dc:creator>TrampEagle</dc:creator><author>TrampEagle</author><pubDate>Wed, 31 May 2006 13:55:00 GMT</pubDate><guid>http://www.blogjava.net/TrampEagle/articles/49337.html</guid><wfw:comment>http://www.blogjava.net/TrampEagle/comments/49337.html</wfw:comment><comments>http://www.blogjava.net/TrampEagle/articles/49337.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/TrampEagle/comments/commentRss/49337.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/TrampEagle/services/trackbacks/49337.html</trackback:ping><description><![CDATA[一、概要<br />在JAVA应用程序特别是基于WEB的程序中，经常遇到字符的编码问题。为了防止出现乱码，首先需要了解JAVA是如何处理字符的，这样就可以有目的地在输入/输出环节中增加必要的转码。其次，由于各种服务器有不同的处理方式，还需要多做试验，确保使用中不出现乱码。<br />二、基本概念<br />2．1 JAVA中字符的表达<br />JAVA中有char、byte、String这几个概念。char 指的是一个UNICODE字符，为16位的整数。byte 是字节，字符串在网络传输或存储前需要转换为byte数组。在从网络接收或从存储设备读取后需要将byte数组转换成String。String是字符串，可以看成是由char组成的数组。String 和 char 为内存形式，byte是网络传输或存储的序列化形式。<br /> 举例：<br />英<br />String ying = “英”;<br />char ying = ying.charAt(0);<br />String yingHex = Integer.toHexString(ying);<br />82 F1   <br />byte yingGBBytes = ying.getBytes(“GBK”);<br />GB编码的字节数值<br />D3 A2 
<p>2．2 编码方式的简介<br /> String序列化成byte数组或反序列化时需要选择正确的编码方式。如果编码方式不正确，就会得到一些0x3F的值。常用的字符编码方式有ISO8859_1、GB2312、GBK、UTF-8/UTF-16/UTF-32。<br />ISO8859_1用来编码拉丁文，它由单字节（0－255）组成。<br /> GB2312、GBK用来编码简体中文，它有单字节和双字节混合组成。最高位为1的字节和下一个字节构成一个汉字，最高位为0的字节是ASCII码。<br />UTF-8/UTF-16/UTF-32是国际标准UNICODE的编码方式。 用得最多的是UTF-8，主要是因为它在对拉丁文编码时节约空间。</p><p>UNICODE值 UTF-8编码<br />U-00000000 - U-0000007F: 0xxxxxxx<br />U-00000080 - U-000007FF:  110xxxxx 10xxxxxx <br />U-00000800 - U-0000FFFF:  1110xxxx 10xxxxxx 10xxxxxx <br />U-00010000 - U-001FFFFF:  11110xxx 10xxxxxx 10xxxxxx 10xxxxxx <br />U-00200000 - U-03FFFFFF:  111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx <br />U-04000000 - U-7FFFFFFF:  1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx </p><p>三、J2SE中相关的函数<br />String str =”英”;<br /> //取得GB2312编码的字节<br />byte[] bytesGB2312 = str.getBytes(“GB2312”); </p><p>//取得平台缺省编码的字节(solaris为ISO8859_1,windows为GB2312)<br />byte[] bytesDefault = str.getBytes();</p><p>//用指定的编码将字节转换成字符串<br /> String newStrGB = new String(bytesGB2312, “GB2312”);<br /> <br />//用平台缺省的编码将字节转换成字符串(solaris为ISO8859_1,windows为GB2312)<br /> String newStrDefault = new String(bytesDefault);</p><p> //用指定的编码从字节流里面读取字符<br />InputStream in = xxx;<br />InputStreamReader reader = InputStreamReader( in, “GB2312”);<br />char aChar = reader.read();<br />四、JSP、数据库的编码<br />4．1 JSP中的编码<br />(1) 静态声明:<!--page contentType="text/html;charset=8859_1"--><br />CHARSET有两个作用：<br />JSP文件的编码方式：在读取JSP文件、生成JAVA类时，源JSP文件中汉字的编码<br />JSP输出流的编码方式：在执行JSP时，往response流里面写入数据的编码方式<br />(2) 动态改变:在往response流里面写数据前可以调用response.setContentType()，设定正确的编码类型。<br />(3) 在TOMCAT中，由Request.getParameter() 得到的参数，编码方式都是ISO8859_1。所以如果在浏览器输入框内输入一个汉字“英”，在服务器端就得到一个ISO8859_1编码的（0x00,0xD3,0x00,0xA2）。所以通常在接收参数时转码：<br />String wrongStr = response.getParameter(“name”);<br />String correctStr = new String(wrongStr.getBytes(“ISO8859_1”),”GB2312”);<br />在最新的SERVLET规范里面，也可以在获取参数之前执行如下代码：<br />request.setCharacterEncoding(“GB2312”);</p><p>4．2 数据库的编码<br />(1) 数据库使用UTF-16<br />如果String中是UNICODE字符，写入读出时不需要转码<br />(2) 数据库使用ISO8859_1<br />如果String中是UNICODE字符，写入读出时需要转码<br />写入：String newStr = new String(oldStr.getByte(“GB2312”), “ISO8859_1”);<br />读出：String newStr = new String(oldStr.getByte(“ISO8859_1”),”GB2312”);<br />五、源文件的编码<br />5．1 资源文件<br />资源文件的编码方式和编辑平台相关。在WINDOWS平台下编写的资源文件，以GB2312方式编码。在编译时需要转码，以确保在各个平台上的正确性：<br />native2ascii –encoding GB2312 source.properties<br />这样从资源文件中读出的就是正确的UNICODE字符串。<br />5．2 源文件<br /><font color="#ff0000">源文件的编码方式和编辑平台相关。在WINDOWS平台下开发的源文件，以GB2312方式编码。在编译的时候，需要指定源文件的编码方式：<br />javac –encoding GB2312<br /> JAVA编译后生成的字节文件的编码为UTF-8。</font><br />①最新版TOMCAT4.1.18支持request.setCharacterEncoding(String enc)<br />②资源文件转码成company.name=\u82f1\u65af\u514b<br />③如果数据库使用utf-16则不需要这部分转码<br />④页面上应有<br />转码ⅰ:<br />String s = new String<br />(request.getParameter(“name”).getBytes(“ISO8859_1”),”GB2312”);<br />转码ⅱ:<br />String s = new String(name.getBytes(“GB2312”),”ISO8859_1”);<br />转码ⅲ:<br />String s = new String(name.getBytes(“ISO8859_1”),” GB2312”);<br /></p><img src ="http://www.blogjava.net/TrampEagle/aggbug/49337.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/TrampEagle/" target="_blank">TrampEagle</a> 2006-05-31 21:55 <a href="http://www.blogjava.net/TrampEagle/articles/49337.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>使用Hibernate/Spring/Webwork架构对单个对象的CRUD操作的Demo</title><link>http://www.blogjava.net/TrampEagle/articles/49015.html</link><dc:creator>TrampEagle</dc:creator><author>TrampEagle</author><pubDate>Tue, 30 May 2006 07:33:00 GMT</pubDate><guid>http://www.blogjava.net/TrampEagle/articles/49015.html</guid><wfw:comment>http://www.blogjava.net/TrampEagle/comments/49015.html</wfw:comment><comments>http://www.blogjava.net/TrampEagle/articles/49015.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/TrampEagle/comments/commentRss/49015.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/TrampEagle/services/trackbacks/49015.html</trackback:ping><description><![CDATA[原文引自：<a href="http://www.3doing.net/forums/dispbbs.asp?boardID=57&amp;ID=226&amp;page=2">http://www.3doing.net/forums/dispbbs.asp?boardID=57&amp;ID=226&amp;page=2</a><br /><br />一个简单对象的CRUD操作的Demo，对用户对象的增删查改操作。 <br /><br />仅仅演示系统架构的组成，从单纯的需求实现角度来看，这个Demo非但没有减少代码，反而非常复杂，然而作为了解系统架构的角度来说，这个Demo却是一个完整的架构例子，是一个简单的入手点。 <br /><br />一、系统架构： <br /><br />Hibernate/Spring/Webwork2/FreeMarker <br /><br />使用Hibernate作为持久层框架，Spring封装DAO，作为业务层组件，Webwork2作为Web层的MVC框架，FreeMarker作为View，取代JSP。 <br /><br />二、项目目录： <br /><br />这是一个Eclipse Project的目录，可以直接import到Eclipse里面。 <br /><br />src：源代码和配置文件目录 <br /><br />lib：一些web application不需要的库和框架源代码 <br /><br />simple： web application <br /><br />(缺test case) <br /><br />三、文件： <br /><br />com.javaeye.User： 实体类，表示用户实体 <br />com.javaeye.UserManager: 用户实体的DAO接口 <br />com.javaeye.impl.UserManagerHibernateImpl: 用户实体的DAO接口的Hibernate实现类 <br />com.javaeye.util.ServletFilter: 设定HTML Form提交的字符集，解决Form提交的乱码问题 <br />com.javaeye.action.*: webwork的相关action <br /><br />applicationContext.xml: SpringFramework的bean配置文件 <br />jdbc.properties: 数据库连接配置文件 <br />xwork.xml: Webwork的Action bean配置文件 <br />webwork.properties: webwork配置文件 <br />freemarker.properties: freemarker配置文件 <br />log4j.properties: log4j配置文件 <br /><br />四、运行 <br /><br />将simple目录作为web application发布到相应的Application Server上即可，已经内置HSQLDB，无需另外的数据库支持。
<p>================================================================</p><p>webwork没用过，如果用struts的话，extends DispatchActionSupport更好，一个action class解决问题。由于我做的是对简单的表的CRUD操作，所以没用到CRUD中的R，直接从list方法得数据，当也没那么多jsp页面，采用单页面CRUD，这样一个action class＋一个jsp＋及pojo和manager之类的东西写起来很简单、方便。 <br /><br /><br /></p><table cellspacing="1" cellpadding="3" width="90%" align="center" border="0"><tbody><tr><td><b>java代码: </b></td></tr><tr><td class="code"><div><br /><br /><br />package com.terac.tm.action; <br /><br />import javax.servlet.http.HttpServletRequest; <br />import javax.servlet.http.HttpServletResponse; <br /><br />import org.apache.struts.action.ActionForm; <br />import org.apache.struts.action.ActionForward; <br />import org.apache.struts.action.ActionMapping; <br />import org.apache.struts.action.ActionMessages; <br />import org.apache.struts.action.DynaActionForm; <br /><br />import com.terac.tm.dao.LinkDAO; <br />import com.terac.tm.model.Link; <br /><br />/** <br />* @author andy <br />*/ <br />public class LinkAction extends AuthorizedDispatchAction { <br /><br />    public ActionForward delete(ActionMapping mapping, ActionForm form, <br />            HttpServletRequest request, HttpServletResponse response) <br />            throws Exception {        <br />        ... <br />    } <br />    public ActionForward list(ActionMapping mapping, ActionForm form, <br />            HttpServletRequest request, HttpServletResponse response) <br />            throws Exception {        <br />        ... <br />    } <br />    public ActionForward save(ActionMapping mapping, ActionForm form, <br />            HttpServletRequest request, HttpServletResponse response) <br />            throws Exception {        <br />        ... <br />    } <br /></div><div>==========================================</div><div>To andy163: <br /><br />我想了一下，明白了当你使用Struts的时候，为什么倾向于合并了。因为Struts的Action是所有线程一个单一实例的，Action的方法需要的数据都通过方法参数(request, response, actionForm)传递给方法的内部，因此所有的逻辑都局限在方法的内部，在这种情况下，拆开成为多个Action，每个Action一个方法，和你现在这种单个Action，包含多个方法，实质上是没有差别的，逻辑上不会增加任何复杂度，并且配置文件的长度，Action的数量都可以大幅度减少，所以倾向于合并。 <br /><br />而Webwork的Action是每线程一个实例，Action方法需要的数据和组件都是通过getter/setter方式由容器注入，此时合并Action，就会比较混乱，不如拆开逻辑清楚。<br /></div></td></tr></tbody></table><p>==================================================================</p><p>我觉得如果表比较多的情况下，可以考虑对于一个表操作的逻辑都合并到一个Action，表比较少的话还是robbin这样的写法比较清晰。 <br /><br />PS. robbin什么时候写个含一对一，一对多，多对多的例子。呵呵，是不是我要求太高了 <img onmousewheel="return bbimg(this)" style="CURSOR: pointer" onclick="javascript:window.open(this.src);" src="http://forum.javaeye.com/images/smiles/icon_biggrin.gif" onload="javascript:if(this.width&gt;screen.width-500)this.style.width=screen.width-500;" border="0" />==================================================</p><p>这个例子是我讲课的时候，现场做出来的，所以比较简单，很多情况都没有考虑，以后会逐渐补充更加复杂的例子。</p><p>====================================================</p><p>希望能增加分页显示的部份,一直搞不清楚 是要在userManager里写 findUsers(int from, int to) 吗?=================================</p><p>分页的方法很多，最好是在DAO中，减少内存占用量 <br />借用一下别人的例子： <br />public Iterator getInfos(int position, int length) throws Exception { <br />Iterator iterator = null; <br />String queryString = " select info from Info as info order by info.id desc"; <br />Query query = getHibernateTemplate().createQuery(getSession(), queryString); <br />//设置游标的起始点 <br />query.setFirstResult(position); <br />//设置游标的长度 <br />query.setMaxResults(length); <br />//记录生成 <br />List list = query.list(); <br />//把查询到的结果放入迭代器 <br />iterator = list.iterator(); <br />return iterator; <br />}</p><img src ="http://www.blogjava.net/TrampEagle/aggbug/49015.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/TrampEagle/" target="_blank">TrampEagle</a> 2006-05-30 15:33 <a href="http://www.blogjava.net/TrampEagle/articles/49015.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>网管必须知道的命令</title><link>http://www.blogjava.net/TrampEagle/articles/48571.html</link><dc:creator>TrampEagle</dc:creator><author>TrampEagle</author><pubDate>Sun, 28 May 2006 03:30:00 GMT</pubDate><guid>http://www.blogjava.net/TrampEagle/articles/48571.html</guid><wfw:comment>http://www.blogjava.net/TrampEagle/comments/48571.html</wfw:comment><comments>http://www.blogjava.net/TrampEagle/articles/48571.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/TrampEagle/comments/commentRss/48571.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/TrampEagle/services/trackbacks/48571.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 常用的几条														NET														命令：																																																						================================================== 														（与远程主机建立空管连接）													...&nbsp;&nbsp;<a href='http://www.blogjava.net/TrampEagle/articles/48571.html'>阅读全文</a><img src ="http://www.blogjava.net/TrampEagle/aggbug/48571.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/TrampEagle/" target="_blank">TrampEagle</a> 2006-05-28 11:30 <a href="http://www.blogjava.net/TrampEagle/articles/48571.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>用 Selenium 自动化验收测试</title><link>http://www.blogjava.net/TrampEagle/articles/30264.html</link><dc:creator>TrampEagle</dc:creator><author>TrampEagle</author><pubDate>Sat, 11 Feb 2006 07:08:00 GMT</pubDate><guid>http://www.blogjava.net/TrampEagle/articles/30264.html</guid><wfw:comment>http://www.blogjava.net/TrampEagle/comments/30264.html</wfw:comment><comments>http://www.blogjava.net/TrampEagle/articles/30264.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/TrampEagle/comments/commentRss/30264.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/TrampEagle/services/trackbacks/30264.html</trackback:ping><description><![CDATA[原文引自：<A href="http://www-128.ibm.com/developerworks/cn/java/wa-selenium-ajax/">http://www-128.ibm.com/developerworks/cn/java/wa-selenium-ajax/</A><BR>（目前，对着一块了解不多，也许以后就要加以重视了）<BR><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR vAlign=top>
<TD width="100%">
<H1></H1>
<P id=subtitle>如何使用 Selenium 测试工具对 Ruby on Rails 和 Ajax 应用程序进行功能测试</P><IMG class=display-img height=6 alt="" src="http://www.ibm.com/i/c.gif" width=1></TD>
<TD class=no-print width=192><IMG height=18 alt=developerWorks src="http://www-128.ibm.com/developerworks/cn/i/dw.gif" width=192></TD></TR></TBODY></TABLE>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR vAlign=top>
<TD width=10><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=10></TD>
<TD width="100%">
<TABLE class=no-print cellSpacing=0 cellPadding=0 width=160 align=right border=0>
<TBODY>
<TR>
<TD width=10><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=10></TD>
<TD>
<TABLE cellSpacing=0 cellPadding=0 width=150 border=0>
<TBODY>
<TR>
<TD class=v14-header-1-small>文档选项</TD></TR></TBODY></TABLE>
<TABLE class=v14-gray-table-border cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=no-padding width=150>
<TABLE cellSpacing=0 cellPadding=0 width=143 border=0><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=8> 
<FORM name=email action=https://www-130.ibm.com/developerworks/secure/email-it.jsp><INPUT type=hidden value="验收测试（也称功能测试）是用来测试手工任务的功能的，但是手工测试这些任务可能很花时间，并且容易出现人为的错误。在本文中，作者为架构师、开发人员和测试人员展示了如何使用 Selenium 测试工具来自动化验收测试。通过自动化测试，可以节省时间，并消除测试人员所犯的错误。文中还给出了一个例子，以演示如何将 Selenium 应用到现实中使用 Ruby on Rails 和 Ajax 的项目上。" name=body><INPUT type=hidden value="用 Selenium 自动化验收测试" name=subject><INPUT type=hidden value=cn name=lang>
<SCRIPT language=JavaScript type=text/javascript>
<!--
document.write('<tr valign="top"><td width="8"><img src="//www.ibm.com/i/c.gif" width="8" height="1" alt=""/></td><td width="16"><img src="//www.ibm.com/i/v14/icons/em.gif" height="16" width="16" vspace="3" alt="将此页作为电子邮件发送" /></td><td width="122"><p><a class="smallplainlink" href="javascript:document.email.submit();"><b>将此页作为电子邮件发送</b></a></p></td></tr>');
//-->
</SCRIPT>
 
<TBODY>
<TR vAlign=top>
<TD width=8><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=8></TD>
<TD width=16><IMG height=16 alt=将此页作为电子邮件发送 src="http://www.ibm.com/i/v14/icons/em.gif" width=16 vspace=3></TD>
<TD width=122>
<P><A class=smallplainlink href="javascript:document.email.submit();"><B><FONT color=#5c81a7 size=2>将此页作为电子邮件发送</FONT></B></A></P></TD></TR><NOSCRIPT><tr valign="top"><td width="8"><img src="//www.ibm.com/i/c.gif" width="8" height="1" alt="" /></td><td width="16"><img src="//www.ibm.com/i/c.gif" height="16" width="16" alt="" /></td><td width="122" class="small"><p><span class="ast">未显示需要 JavaScript 的文档选项</span></p></td></tr></NOSCRIPT></FORM>
<TR vAlign=top>
<TD width=8><FONT color=#5c81a7 size=2><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=8></FONT></TD>
<TD width=16><FONT color=#5c81a7 size=2><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/fw_bold.gif" width=16 vspace=3 border=0></FONT></TD>
<TD width=122>
<P><A class=smallplainlink href="javascript:void forumWindow()"><B><FONT color=#5c81a7 size=2>讨论</FONT></B></A></P></TD></TR>
<TR vAlign=top>
<TD width=8><FONT color=#5c81a7 size=2><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=8></FONT></TD>
<TD width=16><FONT color=#5c81a7 size=2><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/dn.gif" width=16 vspace=3 border=0></FONT></TD>
<TD width=122>
<P><A class=smallplainlink href="http://www-128.ibm.com/developerworks/cn/java/wa-selenium-ajax/#download"><B><FONT color=#996699 size=2>样例代码</FONT></B></A></P></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR>
<TABLE cellSpacing=0 cellPadding=0 width=150 border=0>
<TBODY>
<TR>
<TD class=v14-header-1-small>对此页的评价</TD></TR></TBODY></TABLE>
<TABLE class=v14-gray-table-border cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=no-padding width=150>
<TABLE cellSpacing=0 cellPadding=0 width=143 border=0>
<TBODY>
<TR vAlign=top>
<TD width=8><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=8></TD>
<TD><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/d_bold.gif" width=16 vspace=3 border=0></TD>
<TD width=125>
<P><A class=smallplainlink href="http://www-128.ibm.com/developerworks/cn/java/wa-selenium-ajax/#rate"><B><FONT color=#996699 size=2>帮助我们改进这些内容</FONT></B></A></P></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR></TD></TR></TBODY></TABLE>
<P>级别: 中级</P>
<P><A href="http://www-128.ibm.com/developerworks/cn/java/wa-selenium-ajax/#author"><FONT color=#996699>Christian Hellsten </FONT></A>, IT 专家, IBM<BR></P>
<P>2006 年 1 月 04 日</P>
<BLOCKQUOTE>验收测试（也称功能测试）是用来测试手工任务的功能的，但是手工测试这些任务可能很花时间，并且容易出现人为的错误。在本文中，作者为架构师、开发人员和测试人员展示了如何使用 Selenium 测试工具来自动化验收测试。通过自动化测试，可以节省时间，并消除测试人员所犯的错误。文中还给出了一个例子，以演示如何将 Selenium 应用到现实中使用 Ruby on Rails 和 Ajax 的项目上。</BLOCKQUOTE>
<P>Web 应用程序的验收测试常常涉及一些手工任务，例如打开一个浏览器，并执行一个测试用例中所描述的操作。但是手工执行的任务容易出现操作人员人为的错误，也比较费时间。因此，尽可能将这些任务自动化，以消除人为因素，这是一种很好的做法。于是 Selenium 之类的测试工具就有了用武之地。Selenium 帮助您自动化验收测试，从而可以构建经过更严格测试、因而更为可靠也更易于维护的软件。</P>
<P>验收测试也称黑盒测试和功能测试，是测试和检验应用程序是否能按照涉众（stakeholder）的功能性需求、非功能性需求和其他重要需求来运行的一种方法。验收测试是单元测试和组合测试的补充，后两者通常是使用 xUnit 框架编写的。验收测试也可以使用编程语言来编写，但是 Selenium 和其他类似的工具，例如 Fitnesse，也支持用特定于工具的文档格式编写测试。</P>
<P>验收测试与单元测试和组合测试有以下不同之处：</P>
<UL>
<LI>应用程序是作为一个完整的端到端实体来测试的，而不是像单元测试和组合测试那样，只是测试一个类或一组类。 
<LI>验收测试是在用户界面（例如一个浏览器）上执行的，而不是在 Web 应用程序界面上执行的。 
<LI>编写测试用例的人不一定知道应用程序的内部结构，因此也被称作黑盒测试。非技术性用户也可以编写验收测试。</LI></UL>
<P><A name=IDAXCK4><SPAN class=atitle><FONT face=Arial size=4>背景知识</FONT></SPAN></A></P>
<P>在讨论 Selenium 之前，我要介绍关于以下三个话题的一些背景知识，因为这些话题虽然不是本文的主题，但是和本文密切相关：</P>
<UL>
<LI>持续集成 
<LI>Ajax 
<LI>Ruby/Ruby on Rails</LI></UL>
<P><A name=IDABDK4><SPAN class=smalltitle><STRONG><FONT face=Arial>持续集成</FONT></STRONG></SPAN></A></P>
<P>持续集成的目标是自动化构建和测试过程，以便每天自动运行一次或多次这些过程，而不是每个月手动地运行一次。使用持续集成的最大好处是，代码的更改会定期地自动被集成。如果系统受损，没有构建成功，Apache Continuum 和 Luntbuild 之类的持续集成工具可以自动通过发送电子邮件通知团队（见 <A href="http://www-128.ibm.com/developerworks/cn/java/wa-selenium-ajax/#resources"><FONT color=#996699>参考资料</FONT></A>）。</P>
<P><A name=IDALDK4><SPAN class=smalltitle><STRONG><FONT face=Arial>Ajax</FONT></STRONG></SPAN></A></P>
<P>Ajax 是 Asynchronous JavaScript and XML 的缩写，这是为相当老的技术新创造的一个术语。Ajax 背后的主要思想是，由于只需更新部分页面而不是整个页面，所以 Web 应用程序可以更快地对用户操作做出响应。</P>
<P>Ajax 将更多的复杂性引入到 Web 应用程序中，这一点也反映在测试中。这是因为 Ajax 就像它的名称所表明的那样，使用 JavaScript 和异步 HTTP 请求来更新页面内容。每个浏览器在实现中与其他浏览器相比有一些小小的不同。Selenium 是测试和检测这些差异的很好的工具，因为它在大多数流行的浏览器中都能够运行。</P>
<P><A name=IDARDK4><SPAN class=smalltitle><STRONG><FONT face=Arial>Ruby/Ruby on Rails</FONT></STRONG></SPAN></A></P>
<P>Ruby 是一种开放源码的解释型脚本语言，用于快捷、容易地进行面向对象程序设计。它提供了大量的库，而且简单易用，还具有可扩展性和可移植性。该语言是由 Yukihiro “Matz” Matsumoto 创造的，目的是让程序员将更多的注意力放在手头的任务上，摆脱语法的烦恼。</P>
<P>Rails 是由 David Heinemeier Hansson 创造的一种全栈的（full-stack）、开放源码的 Ruby Web 框架。Rails 的目标是使现实中的应用程序编写起来需要的代码更少，并且比 J2EE 和 XML 之类的语言更容易。所有层都能够无缝地一起工作，因此可以使用一种语言编写从模板到控制流乃至业务逻辑的各种东西。Rails 使用 YAML 而不是 XML 配置文件以及注释形式的反射和运行时扩展。这里不存在编译阶段 —— 程序修改后将直接运行。</P><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><BR></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/cn/java/wa-selenium-ajax/#main"><B><FONT color=#996699>回页首</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=IDAXDK4><SPAN class=atitle><FONT face=Arial size=4>什么是 Selenium？</FONT></SPAN></A></P>
<P>Selenium 是 ThoughtWorks 专门为 Web 应用程序编写的一个验收测试工具。据 Selenium 主页所说，与其他测试工具相比，使用 Selenium 的最大好处是：</P>
<BLOCKQUOTE><I>Selenium 测试直接在浏览器中运行，就像真实用户所做的一样。Selenium 测试可以在 Windows、Linux 和 MacintoshAnd 上的 Internet Explorer、Mozilla 和 Firefox 中运行。其他测试工具都不能覆盖如此多的平台。</I></BLOCKQUOTE>
<P>使用 Selenium 和在浏览器中运行测试还有很多其他好处。下面是主要的两大好处：</P>
<UL>
<LI>通过编写模仿用户操作的 Selenium 测试脚本，可以从终端用户的角度来测试应用程序。 
<LI>通过在不同浏览器中运行测试，更容易发现浏览器的不兼容性。</LI></UL>
<P>Selenium 的核心，也称 <I>browser bot</I>，是用 JavaScript 编写的。这使得测试脚本可以在受支持的浏览器中运行。browser bot 负责执行从测试脚本接收到的命令，测试脚本要么是用 HTML 的表布局编写的，要么是使用一种受支持的编程语言编写的。</P>
<P>Selenium 适用于以下浏览器：</P>
<TABLE cellSpacing=2 cellPadding=2 border=1>
<TBODY>
<TR>
<TD style="VERTICAL-ALIGN: top; TEXT-ALIGN: left">&nbsp;&nbsp;</TD>
<TD style="VERTICAL-ALIGN: top; TEXT-ALIGN: left"><B>Internet Explorer</B></TD>
<TD style="VERTICAL-ALIGN: top; TEXT-ALIGN: left"><B>Mozilla</B></TD>
<TD style="VERTICAL-ALIGN: top; TEXT-ALIGN: left"><B>Firefox</B></TD>
<TD style="VERTICAL-ALIGN: top; TEXT-ALIGN: left"><B>Safari</B></TD></TR>
<TR>
<TD style="VERTICAL-ALIGN: top; TEXT-ALIGN: left"><B>Windows XP</B></TD>
<TD style="VERTICAL-ALIGN: top; TEXT-ALIGN: left">6.0</TD>
<TD style="VERTICAL-ALIGN: top; TEXT-ALIGN: left">1.6+, 1.7+</TD>
<TD style="VERTICAL-ALIGN: top; TEXT-ALIGN: left">0.8+, 0.9+, 1.0</TD>
<TD style="VERTICAL-ALIGN: top; TEXT-ALIGN: left">&nbsp;&nbsp;</TD></TR>
<TR>
<TD style="VERTICAL-ALIGN: top; TEXT-ALIGN: left"><B>Red Hat Linux</B></TD>
<TD style="VERTICAL-ALIGN: top; TEXT-ALIGN: left">&nbsp;&nbsp;</TD>
<TD style="VERTICAL-ALIGN: top; TEXT-ALIGN: left">1.6+, 1.7+</TD>
<TD style="VERTICAL-ALIGN: top; TEXT-ALIGN: left">0.8+, 0.9+, 1.0+</TD>
<TD style="VERTICAL-ALIGN: top; TEXT-ALIGN: left">&nbsp;&nbsp;</TD></TR>
<TR>
<TD style="VERTICAL-ALIGN: top; TEXT-ALIGN: left"><B>Mac OS X 10.3</B></TD>
<TD style="VERTICAL-ALIGN: top; TEXT-ALIGN: left"><I>不支持</I></TD>
<TD style="VERTICAL-ALIGN: top; TEXT-ALIGN: left">1.6+, 1.7+</TD>
<TD style="VERTICAL-ALIGN: top; TEXT-ALIGN: left">0.8+, 0.9+, 1.0+</TD>
<TD style="VERTICAL-ALIGN: top; TEXT-ALIGN: left">1.3+</TD></TR></TBODY></TABLE><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><BR></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/cn/java/wa-selenium-ajax/#main"><B><FONT color=#996699>回页首</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=IDAMGK4><SPAN class=atitle><FONT face=Arial size=4>Selenium 命令</FONT></SPAN></A></P>
<P>通过 Selenium 命令，脚本编写者可以描述 browser bot 在浏览器中所执行的操作。可以将这些命令分成两类 —— <I>操作（action）</I> 和<I>断言（assertion）</I>：</P>
<UL>
<LI>操作模拟用户与 Web 应用程序的交互。例如，单击一个按钮和填写一个表单，这些都是常见的用户操作，可以用 Selenium 命令来自动化这些操作。 
<LI>断言验证一个命令的预期结果。常见的断言包括验证页面内容或当前位置是否正确。</LI></UL>
<P>在 Selenium 网站上可以找到可用命令的完整列表（见 <A href="http://www-128.ibm.com/developerworks/cn/java/wa-selenium-ajax/#resources"><FONT color=#996699>参考资料</FONT></A>）。</P><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><BR></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/cn/java/wa-selenium-ajax/#main"><B><FONT color=#996699>回页首</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=IDAAHK4><SPAN class=atitle><FONT face=Arial size=4>Selenium 模式</FONT></SPAN></A></P>
<P>可以按两种模式来使用 Selenium：<I>test runner</I> 和 <I>driven</I>。这两种模式在复杂性和编写方式方面有所不同。driven 测试脚本编写起来往往要更复杂一些，因为它们是用编程语言编写的。但是如果使用 Python 或 Ruby 之类的高级动态编程语言，那么这种复杂性方面的差异就很小。</P>
<P>两种模式之间最大的不同点在于，如果使用 driven 脚本，测试有一部分在浏览器之外运行，而如果使用 test runner 脚本的话，测试是完全在浏览器中运行的。</P>
<P>不管是 test runner 还是 driven 测试用例，都可以与持续集成工具集成。</P><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><BR></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/cn/java/wa-selenium-ajax/#main"><B><FONT color=#996699>回页首</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=IDANHK4><SPAN class=atitle><FONT face=Arial size=4>test runner 模式</FONT></SPAN></A></P>
<P>Selenium test runner 脚本，也称<I>测试用例（test case）</I>，是用 HTML 语言通过一个简单的表布局编写的，如 <A href="http://www-128.ibm.com/developerworks/cn/java/wa-selenium-ajax/#code1"><FONT color=#996699>清单 1</FONT></A> 所示。</P><BR><A name=code1><B>清单 1. Selenium 测试用例的结构</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">  &lt;table border="1"&gt;
    &lt;tr&gt;
      &lt;td&gt;First command&lt;/td&gt;
      &lt;td&gt;Target&lt;/td&gt;
      &lt;td&gt;Value&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Second command&lt;/td&gt;
      &lt;td&gt;Target&lt;/td&gt;
      &lt;td&gt;Value&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/table&gt;
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P>test runner 脚本通常与所测试的应用程序（AUT）部署在同一个服务器上。这是因为 browser bot 使用 JavaScript 来模拟用户操作。这些脚本在一个受限制的沙箱环境中运行。如果需要绕过这些限制，可以使用一个代理。</P>
<P>test runner 脚本使用与 xUnit 框架相同的测试套件（test suite）和测试用例概念。测试用例和命令按照它们在测试套件和测试用例中出现的顺序依次执行。在 <A href="http://www-128.ibm.com/developerworks/cn/java/wa-selenium-ajax/#code1"><FONT color=#996699>清单 1</FONT></A> 中：</P>
<UL>
<LI>第一列包含<I>命令</I> 或<I>断言</I>。 
<LI>第二列包含命令或断言的<I>目标（target）</I>。这里可以用多种受支持的组件定位符中的一种来指定目标。通常使用的是组件的 ID 或名称，但 XPath 和 DOM 定位符也是受支持的。 
<LI>第三列包含用于为命令或断言指定参数的<I>值</I>。例如，当使用 <CODE>type</CODE> 命令时，这一列可能就是一个文本域所期望的值。</LI></UL>
<P>即使对于非技术人员来说，test runner 脚本也易于阅读和编写。当在一个浏览器中打开 <A href="http://www-128.ibm.com/developerworks/cn/java/wa-selenium-ajax/#code1"><FONT color=#996699>清单 1</FONT></A> 中的例子时，将得到类似这样的一个表：</P>
<TABLE border=1>
<TBODY>
<TR>
<TD>First command</TD>
<TD>Target</TD>
<TD>Value</TD></TR>
<TR>
<TD>Second command</TD>
<TD>Target</TD>
<TD>Value</TD></TR></TBODY></TABLE>
<P>接下来，我将描述如何使用命令和断言编写一个简单但是完整的测试用例。</P>
<P><A name=IDAQBYIB><SPAN class=smalltitle><STRONG><FONT face=Arial>测试用例实例</FONT></STRONG></SPAN></A></P>
<P>执行 <A href="http://www-128.ibm.com/developerworks/cn/java/wa-selenium-ajax/#code2"><FONT color=#996699>清单 2</FONT></A> 中的测试脚本时，它将执行以下操作：</P>
<OL>
<LI>通过进入 /change_address_form.html 打开变更地址页面。 
<LI>在 ID 为 <CODE>address_field</CODE> 的文本框中输入 <CODE>Betelgeuse state prison</CODE>。 
<LI>单击名为 <CODE>Submit</CODE> 的输入区。注意，这里使用 XPath 找到 <I>Submit</I> 按钮，这导致表单数据被发送到服务器。 
<LI>验证页面是否包含文本 <CODE>Address change successful</CODE>。</LI></OL><BR><A name=code2><B>清单 2. 在测试用例中使用命令和断言的例子</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">  &lt;table&gt;
    &lt;tr&gt;
      &lt;td&gt;open&lt;/td&gt;
      &lt;td&gt;/change_address_form.html&lt;/td&gt;
      &lt;td&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;type&lt;/td&gt;
      &lt;td&gt;address_field&lt;/td&gt;
      &lt;td&gt;Betelgeuse state prison&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;clickAndWait&lt;/td&gt;
      &lt;td&gt;//input[@name='Submit']&lt;/td&gt;
      &lt;td&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;verifyTextPresent&lt;/td&gt;
      &lt;td&gt;Address change successful&lt;/td&gt;
      &lt;td&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/table&gt;
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P><A name=IDA2CYIB><SPAN class=smalltitle><STRONG><FONT face=Arial>测试套件</FONT></STRONG></SPAN></A></P>
<P>要达到对应用程序的完全测试覆盖，通常需要不止一个测试用例。这就是 Selenium 使用测试套件的原因。测试套件用于将具有类似功能的一些测试用例编成一组，以便让它们按顺序运行。</P>
<P>测试套件和测试用例一样，都是用简单的 HTML 表编写的。Selenium 执行的缺省测试套件的名称是 TestSuite.html。<A href="http://www-128.ibm.com/developerworks/cn/java/wa-selenium-ajax/#code3"><FONT color=#996699>清单 3</FONT></A> 展示了一个测试套件，该套件像通常的用户一样测试应用程序。注意，测试套件使用一个只包含一列的表，表中的每一行指向一个包含某个测试用例的文件。</P><BR><A name=code3><B>清单 3. 测试套件示例</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">  &lt;table&gt;
    &lt;tr&gt;
      &lt;td&gt;Test suite for the whole application&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;a href="test_main_page.html"&gt;Access main page&lt;/a&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;a href="test_login.html"&gt;Login to application&lt;/a&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;a href="test_address_change.html"&gt;Change address&lt;/a&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;a href="test_logout.html"&gt;Logout from application&lt;/a&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/table&gt;
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P>接下来我将把目光转移到 driven 测试脚本。</P><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><BR></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/cn/java/wa-selenium-ajax/#main"><B><FONT color=#996699>回页首</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=IDAQDYIB><SPAN class=atitle><FONT face=Arial size=4>driven 模式</FONT></SPAN></A></P>
<P>driven Selenium 脚本是用多种受支持的编程语言中的一种编写的 —— 目前可用的有 Java、Ruby 和 Python 驱动程序。这些脚本在浏览器之外的一个单独的进程中运行。驱动程序的任务是执行测试脚本，并通过与运行在浏览器中的 browser bot 进行通信来驱动浏览器。驱动程序与 browser bot 之间的通信使用一种简单的特定于 Selenium 的连接语言 Selenese。</P>
<P>driven 脚本比 test runner 脚本更强大、更灵活，可以将它们与 xUnit 框架集成。driven 脚本的缺点（与 test runner 脚本相比）是，这种脚本编写和部署起来更复杂。这是因为驱动程序必须执行以下任务：</P>
<UL>
<LI>启动服务器。 
<LI>部署所测试的应用程序（AUT）。 
<LI>部署测试脚本。 
<LI>启动浏览器。 
<LI>发送命令到 browser bot。 
<LI>验证 browser bot 执行的命令的结果。</LI></UL>
<P>driven 脚本更依赖于应用程序运行时环境。例如，Java 驱动程序使用一个嵌入式 Jetty 或 Tomcat 实例来部署所测试的应用程序。目前，已经有人在致力于将 Selenium 集成到 Ruby on Rails 中，但是在我撰写本文之际，这个集成版本还没有被发布。</P>
<P><A href="http://www-128.ibm.com/developerworks/cn/java/wa-selenium-ajax/#code4"><FONT color=#996699>清单 4</FONT></A> 摘自一个使用 Ruby 驱动程序的 driven 测试脚本。注意，我省略了用于启动服务器和浏览器的步骤，这个测试脚本代码几乎和 test runner 脚本一样简单。</P><BR><A name=code4><B>清单 4. 使用 Ruby 驱动程序的例子</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">    .
    .
    puts selenium.open('/logout.html')
    puts selenium.verify_location('/index.html')
    .
    .
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><FONT face="Lucida Console"><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></FONT></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><FONT face="Lucida Console"><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><BR></FONT></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/cn/java/wa-selenium-ajax/#main"><B><FONT color=#996699>回页首</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=realreq><SPAN class=atitle><FONT face=Arial size=4>现实中的需求</FONT></SPAN></A></P>
<P>在接下来的两节（<A href="http://www-128.ibm.com/developerworks/cn/java/wa-selenium-ajax/#realreq"><FONT color=#996699>现实中的需求</FONT></A> 和 <A href="http://www-128.ibm.com/developerworks/cn/java/wa-selenium-ajax/#realuse"><FONT color=#996699>现实中的用例</FONT></A>）中，我将描述如何在现实场景中使用 Selenium，并针对用 Ruby on Rails 和一点儿 Ajax 技术编写的一个简单的股票报价查看器应用程序编写 Selenium 测试用例。虽然这个应用程序是用 Ruby on Rails 编写的，但是也可以将这个例子应用于任何 Web 应用程序，因为测试脚本是按 test runner 模式以 HTML 编写的。这个示例应用程序是用 Ruby 1.8.3 和 Ruby on Rails 0.14.2 测试的，但是它也可能可以使用更旧的或更新的版本。</P>
<P>如果有 Linux，那么发行版中通常已经包括了 Ruby。在命令提示符下运行 <CODE>ruby -v</CODE>，检查您所拥有的版本。对于大多数平台，都可以在 <A href="http://www.ruby-lang.org/" target=_new><FONT color=#5c81a7>http://www.ruby-lang.org/</FONT></A> 上找到一个 Ruby 发行版。</P>
<P>接下来的步骤是通过 RubyGems 打包系统安装 Ruby on Rails。为此，只需执行 <CODE>gem install rails --include-dependencies</CODE>。在某些平台上，必须执行一些额外的步骤，所以请访问 <A href="http://www.rubyonrails.org/" target=_new><FONT color=#5c81a7>Ruby on Rails</FONT></A> 网站，以获得更多细节。</P>
<P>在我撰写本文之际，目前可用的 Selenium 版本是 0.6。我已经将它集成在示例应用程序中（见 <A href="http://www-128.ibm.com/developerworks/cn/java/wa-selenium-ajax/#download"><FONT color=#996699>下载</FONT></A> 小节），我的做法是从 <A href="http://selenium.thoughtworks.com/" target=_new><FONT color=#5c81a7>http://selenium.thoughtworks.com/</FONT></A> 下载 Selenium Core 包，然后将名为 selenium 的文件夹复制到用于静态内容的文件夹。在 Ruby on Rails 应用程序中，这个文件夹的名称是 public。在 J2EE Web 应用程序中，可以将 selenium 文件夹放在 Web 应用程序的根目录或 WAR 归档文件中。</P>
<P>最后一步是下载示例应用程序。从 <A href="http://www-128.ibm.com/developerworks/cn/java/wa-selenium-ajax/#download"><FONT color=#996699>下载</FONT></A> 小节中获得这个包。解压应用程序，并打开一个命令提示符。然后转入应用程序被解压到的那个目录。为了启动应用程序，运行 <CODE>ruby script/server</CODE>。应该看到 Rails 成功启动了，如 <A href="http://www-128.ibm.com/developerworks/cn/java/wa-selenium-ajax/#fig1"><FONT color=#996699>图 1</FONT></A> 所示。</P><BR><A name=fig1><B>图 1. 从命令提示符下运行 Ruby on Rails</B></A><BR><IMG height=152 alt="从命令提示符下运行 Ruby on Rails" src="http://www-128.ibm.com/developerworks/cn/java/wa-selenium-ajax/command_prompt.jpg" width=440><BR><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><BR></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/cn/java/wa-selenium-ajax/#main"><B><FONT color=#996699>回页首</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=realuse><SPAN class=atitle><FONT face=Arial size=4>现实中的用例</FONT></SPAN></A></P>
<P>在本节中，我将列出示例应用程序的用例。通过这些简化的用例，可以编写模拟用户所执行步骤的验收测试，并验证这些步骤的结果是否与预期相符。股票报价应用程序实现了以下四个用例：</P>
<UL>
<LI>登录 
<LI>查看股票 
<LI>查看股票细节 
<LI>退出</LI></UL>
<P>实现这些用例的代码已经编写好了；可以在 app 目录中找到该代码，测试用例在 public/selenium/tests 文件夹中。</P>
<P><A name=IDACHYIB><SPAN class=smalltitle><STRONG><FONT face=Arial>登录用例</FONT></STRONG></SPAN></A></P>
<P>大多数人都知道登录页面是如何工作的 —— 输入用户名和密码，然后将数据提交到服务器。如果凭证有效，就可以成功登录，并看到受安全保护的资源。在示例应用程序中，这个测试用例包含以下用户操作和断言，必须将它转换成一个 Selenium 测试用例：</P>
<OL>
<LI>单击登录链接。 
<LI>验证系统是否要求用户进行登录。 
<LI>输入用户名。 
<LI>输入密码。 
<LI>按下登录按钮。 
<LI>验证是否登录成功。</LI></OL>
<P><A href="http://www-128.ibm.com/developerworks/cn/java/wa-selenium-ajax/#fig2"><FONT color=#996699>图 2</FONT></A> 展示了用于这些需求的 Selenium 测试用例。注意，我是在运行测试之后截取屏幕快照的。绿色箭头表示成功地通过验证的断言。</P><BR><A name=fig2><B>图 2. 登录和查看股票测试用例</B></A><BR><IMG height=139 alt=登录和查看股票测试用例 src="http://www-128.ibm.com/developerworks/cn/java/wa-selenium-ajax/login_and_view_stocks.jpg" width=261><BR>
<P><A name=IDACIYIB><SPAN class=smalltitle><STRONG><FONT face=Arial>查看股票测试用例</FONT></STRONG></SPAN></A></P>
<P>查看股票页面显示一个公司列表。用于这个页面的测试用例非常简单，所以被包括在前一个测试用例的后面。该测试用例验证当前位置是否为 /main/list_stocks，以及页面是否包含文本 <CODE>Click on a company name to view details</CODE>。</P>
<P><A name=IDAMIYIB><SPAN class=smalltitle><STRONG><FONT face=Arial>查看股票细节用例</FONT></STRONG></SPAN></A></P>
<P>查看股票细节用例是在查看股票页面上触发的。用户在一个公司名称上单击鼠标时，就触发了到服务器的一个 Ajax 请求。服务器的响应包括该公司的详细信息，这些信息将插入到当前页面中，而不必重新装载完整的页面。用于这个用例的测试用例执行以下用户操作：</P>
<OL>
<LI>单击公司名称 <CODE>Acme Oil</CODE>。 
<LI>验证页面上是否显示该公司的详细信息。 
<LI>单击公司名称 <CODE>Acme Automotive</CODE>。 
<LI>验证页面上是否显示该公司的详细信息。</LI></OL>
<P>由于使用了 Ajax，请求是异步发生的。在一般的 Web 应用程序中，所有东西通常都是同步的，因此这一点提出了一种不同的挑战。可以像测试其他功能一样来测试 Ajax 功能。惟一的不同是，必须让 Selenium 暂停，等待 Ajax 命令完成。为此，可以使用 <CODE>pause</CODE> 命令来等待 Ajax 命令的完成。另外，Joseph Moore 在他最近的 blog 贴中提到，还可以使用 <CODE>waitForValue</CODE> 和 <CODE>waitForCondition</CODE> 命令代替 pause 命令（见 <A href="http://www-128.ibm.com/developerworks/cn/java/wa-selenium-ajax/#resources"><FONT color=#996699>参考资料</FONT></A>）。</P>
<P><A href="http://www-128.ibm.com/developerworks/cn/java/wa-selenium-ajax/#fig3"><FONT color=#996699>图 3</FONT></A> 展示了被转换成 Selenium 用例的需求。</P><BR><A name=fig3><B>图 3. 查看股票细节测试用例</B></A><BR><IMG height=113 alt=查看股票细节测试用例 src="http://www-128.ibm.com/developerworks/cn/java/wa-selenium-ajax/view_stock_details.jpg" width=223><BR>
<P>注意 <CODE>pause</CODE> 命令：必须使用这些命令，以便等待异步请求完成和更新页面内容。如果没有 500 毫秒的暂停，测试将失败（如 <A href="http://www-128.ibm.com/developerworks/cn/java/wa-selenium-ajax/#fig4"><FONT color=#996699>图 4</FONT></A> 所示）。</P><BR><A name=fig4><B>图 4. 失败的查看股票细节测试用例</B></A><BR><IMG height=129 alt=失败的查看股票细节测试用例 src="http://www-128.ibm.com/developerworks/cn/java/wa-selenium-ajax/view_stock_details_failed.jpg" width=259><BR>
<P><CODE>pause</CODE> 命令还测试 Ajax 功能的非功能性需求。500 毫秒对于 pause 命令是一个很好的值，因为 Ajax 请求应该快速地执行和完成。可以试着去掉 <CODE>pause</CODE> 命令，看看结果如何。如果测试在您的机器上失败，那么试着将这个值增加到 1000 毫秒。</P>
<P><A name=IDACLYIB><SPAN class=smalltitle><STRONG><FONT face=Arial>退出用例</FONT></STRONG></SPAN></A></P>
<P>退出用例很容易实现，简单来说只有以下两步：</P>
<OL>
<LI>单击退出链接。 
<LI>验证是否成功退出。</LI></OL>
<P><A href="http://www-128.ibm.com/developerworks/cn/java/wa-selenium-ajax/#fig5"><FONT color=#996699>图 5</FONT></A> 展示了最后这个测试用例。</P><BR><A name=fig5><B>图 5. 退出用例</B></A><BR><IMG height=51 alt=退出用例 src="http://www-128.ibm.com/developerworks/cn/java/wa-selenium-ajax/logout.jpg" width=173><BR>
<P>所有测试都被添加到 <A href="http://www-128.ibm.com/developerworks/cn/java/wa-selenium-ajax/#fig6"><FONT color=#996699>图 6</FONT></A> 左侧显示的缺省测试套件中。</P><BR><A name=fig6><B>图 6. 示例应用程序的测试套件</B></A><BR><IMG height=213 alt=示例应用程序的测试套件 src="http://www-128.ibm.com/developerworks/cn/java/wa-selenium-ajax/testsuite_completed.jpg" width=440><BR>
<P><A name=IDANMYIB><SPAN class=smalltitle><STRONG><FONT face=Arial>执行测试套件</FONT></STRONG></SPAN></A></P>
<P>最后要做的是在 Mozilla Firefox 和 Microsoft Internet Explorer 中执行测试套件。为此，在浏览器中打开 <CODE>http://localhost:3000/selenium/TestRunner.html</CODE>，然后单击 <A href="http://www-128.ibm.com/developerworks/cn/java/wa-selenium-ajax/#fig6"><FONT color=#996699>图 6</FONT></A> 中所示的 <I>All</I> 按钮。失败的测试用例和断言将被标记为红色，但是这里，在两个浏览器中所有用例都应该可以成功完成（同样见 <A href="http://www-128.ibm.com/developerworks/cn/java/wa-selenium-ajax/#fig6"><FONT color=#996699>图 6</FONT></A>）。注意，我使用的是 Mozilla Firefox 1.0.7 和 Internet Explorer 6.0。</P>
<P>还可以单步调试测试套件，这意味着 Selenium 将很慢地执行测试套件，这样当测试套件在浏览器中执行时，就可以看到它的每一步。</P><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><BR></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/cn/java/wa-selenium-ajax/#main"><B><FONT color=#996699>回页首</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=IDACNYIB><SPAN class=atitle><FONT face=Arial size=4>结束语</FONT></SPAN></A></P>
<P>Selenium 是软件工程师、设计人员和测试人员的工具箱中又一个有用且重要的工具。通过将该工具与持续集成工具相结合，团队就可以将验收测试自动化，并构建更好的软件，因为他们可以更容易、更早、更频繁地发现 bug。Selenium 的另一个优点是可以节省时间，使开发人员和测试人员不必将时间花在本可以（也应该）自动化的手工任务上，从而让团队将精力放在更有价值的活动上。</P><BR><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><BR></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/cn/java/wa-selenium-ajax/#main"><B><FONT color=#996699>回页首</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><SPAN class=atitle><A name=download><FONT face=Arial size=4>下载</FONT></A></SPAN></P>
<TABLE class=data-table-1 cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TH>描述</TH>
<TH>名字</TH>
<TH style="TEXT-ALIGN: right">大小</TH>
<TH>&nbsp;下载方法</TH></TR>
<TR>
<TD class=tb-row>Sample application</TD>
<TD noWrap>wa-selenium-ajax-example.zip</TD>
<TD style="TEXT-ALIGN: right" noWrap>286KB</TD>
<TD noWrap><A class=fbox href="http://download.boulder.ibm.com/ibmdl/pub/software/dw/web/wa-selenium-ajax-example.zip"><B><FONT color=#5c81a7>HTTP</FONT></B></A></TD></TR></TBODY></TABLE>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR vAlign=top>
<TD colSpan=5><FONT color=#5c81a7><IMG height=12 alt="" src="http://www.ibm.com/i/c.gif" width=12 border=0></FONT></TD></TR>
<TR>
<TD><FONT color=#5c81a7><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/fw.gif" width=16></FONT></TD>
<TD><A class=fbox href="http://www-128.ibm.com/developerworks/cn/whichmethod.html"><FONT color=#5c81a7>关于下载方法的信息</FONT></A></TD>
<TD><FONT color=#5c81a7><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=50></FONT></TD>
<TD><FONT color=#5c81a7><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/sout.gif" width=16></FONT></TD>
<TD><A class=fbox href="http://www.adobe.com/products/acrobat/readstep2.html"><FONT color=#5c81a7>获取 Adobe® Reader®</FONT></A></TD></TR></TBODY></TABLE><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><FONT color=#5c81a7><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></FONT></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><FONT color=#5c81a7><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><BR></FONT></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/cn/java/wa-selenium-ajax/#main"><B><FONT color=#996699>回页首</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=resources><SPAN class=atitle><FONT face=Arial size=4>参考资料 </FONT></SPAN></A></P><B>学习</B><BR>
<UL>
<LI>您可以参阅本文在 developerWorks 全球站点上的 <A href="http://www-128.ibm.com/developerworks/java/library/wa-selenium-ajax/index.html" target=_blank><FONT color=#5c81a7>英文原文</FONT></A>。<BR><BR>
<LI>查看可用 <A href="http://selenium.thoughtworks.com/seleniumReference.html"><FONT color=#5c81a7>Selenium 命令</FONT></A>的完整列表。<BR><BR>
<LI>参阅 Wikipedia 对 <A href="http://en.wikipedia.org/wiki/Acceptance_test"><FONT color=#5c81a7>验收测试</FONT></A> 的定义。<BR><BR>
<LI>看看在这个集体讨论会中大家如何看待 <A href="http://wiki.rubyonrails.com/rails/pages/SeleniumIntegration"><FONT color=#5c81a7>Selenium 到 Ruby on Rails 的集成</FONT></A>。<BR><BR>
<LI>参阅 Wikipedia 对 <A href="http://en.wikipedia.org/wiki/Ajax_%28programming%29"><FONT color=#5c81a7>Ajax</FONT></A> 的定义。<BR><BR>
<LI>尝试 Selenium 的 <CODE>waitForValue</CODE> 和 <CODE>waitForCondition</CODE> 命令 —— Joseph Moore 提出的 <CODE>pause</CODE> 命令的 <A href="http://www.josephmoore.net/2005/11/using-seleniums-waitforvalue.html"><FONT color=#5c81a7>替代品</FONT></A>。<BR><BR>
<LI>钻研 developerworks 上的这些 Ajax 参考资料： 
<UL>
<LI><A href="http://www.ibm.com/developerworks/edu/wa-dw-wa-ajax-i.html?S_TACT=105AGX08&amp;S_CMP=TUT"><FONT color=#5c81a7>Build apps with Asynchronous JavaScript with XML, or Ajax</FONT></A>（2005 年 11 月) 演示了如何用 Ajax 构建支持实时验证的 Web 应用程序。 
<LI><A href="http://www-128.ibm.com/developerworks/cn/java/j-ajax1/"><FONT color=#5c81a7>面向 Java 开发人员的 Ajax: 构建动态的 Java 应用程序</FONT></A>（2005 年 9 月）是关于使用 Ajax 开发应用程序的指南。 
<LI><A href="http://www-128.ibm.com/developerworks/cn/webservices/ws-ajax1/"><FONT color=#5c81a7>AJAX 及使用 E4X 编写 Web 服务脚本，第 1 部分</FONT></A>（2005 年 4 月）是关于 Ajax 的初级读物。 
<LI><A href="http://www-128.ibm.com/developerworks/cn/java/j-ajax3/"><FONT color=#5c81a7>面向 Java 开发人员的 Ajax: 结合 Direct Web Remoting 使用 Ajax</FONT></A>（2005 年 11 月）演示了如何自动化 Ajax 的繁重工作。</LI></UL><BR>
<LI>阅读归档的 <A href="http://www-128.ibm.com/developerworks/cn/views/java/articles.jsp?view_by=search&amp;search_by=%E8%AF%8A%E6%96%AD+Java+%E4%BB%A3%E7%A0%81%EF%BC%9A"><FONT color=#5c81a7>诊断 Java 代码</FONT></A> 系列（developerWorks，2001 年），包括作为最佳编程实践和调试实践的测试任务的自动化。<BR><BR>
<LI>在 developerWorks 上查看所有 <A href="http://www.ibm.com/developerworks/views/linux/libraryview.jsp?search_by=ruby"><FONT color=#5c81a7>Ruby 和 Ruby on Rails 文章</FONT></A>，包括以下难得的资料： 
<UL>
<LI>教程 <A href="http://www-128.ibm.com/developerworks/cn/views/linux/tutorials.jsp?cv_doc_id=98825"><FONT color=#5c81a7>使用 Ruby 进行 socket 编程</FONT></A> 讲解 Ruby 的基础知识，并给出了用 Ruby 语言开发基于套接字的网络应用程序所用的一些最重要的类（2005 年 10 月）。 
<LI><A href="http://www-128.ibm.com/developerworks/cn/linux/l-rubyrails/"><FONT color=#5c81a7>使用 Ruby on Rails 快速开发 Web 应用程序</FONT></A>（2005 年 6 月） 描述了用 MVC 模式进行快速开发的一个框架。 
<LI><A href="http://www-128.ibm.com/developerworks/cn/java/wa-rubyonrails/"><FONT color=#5c81a7>Ruby on Rails 和 J2EE：两者能否共存？</FONT></A>（2005 年 7 月） 将 Ruby on Rails 与 J2EE 做了比较。</LI></UL><BR>
<LI>查看 developerWorks 上的所有 <A href="http://www-128.ibm.com/developerworks/cn/linux/theme/special/index.html#python"><FONT color=#5c81a7>Python 文章</FONT></A>。 <BR><BR></LI></UL><BR><B>获得产品和技术</B><BR>
<UL>
<LI>获取关于 <A href="http://selenium.thoughtworks.com/"><FONT color=#5c81a7>Selenium</FONT></A> 工具的详细信息，该工具是 ThoughtWorks 根据用于企业软件开发的敏捷开发方法而创造的。<BR><BR>
<LI>访问 <A href="http://www.rubyonrails.com/"><FONT color=#5c81a7>Ruby on Rails</FONT></A> 主页和 <A href="http://www.ruby-lang.org/"><FONT color=#5c81a7>Ruby language</FONT></A> 主页，以获得为掌握这些技术所需的各种资料。<BR><BR>
<LI>查看用于构建基于 Java 的项目的持续集成服务器 <A href="http://maven.apache.org/continuum/"><FONT color=#5c81a7>Continuum</FONT></A>。 <BR><BR>
<LI>研究 <A href="http://luntbuild.javaforge.com/"><FONT color=#5c81a7>Luntbuild</FONT></A>，这是一个强大的构建自动化和管理工具，用于自动化构建，并充当构建管理系统。<BR><BR>
<LI>尝试用 <A href="http://fitnesse.org/"><FONT color=#5c81a7>Fitnesse</FONT></A> 作为验收测试框架。<BR><BR>
<LI>获取 <A href="http://www.mozilla.org/"><FONT color=#5c81a7>Mozilla Firefox</FONT></A> 浏览器。<BR><BR>
<LI>获取 <A href="http://seleniumrecorder.mozdev.org/"><FONT color=#5c81a7>Selenium Recorder</FONT></A>，这是一个 Firefox 扩展，通过它可以记录 Selenium 测试脚本。</LI></UL><BR><B>讨论</B><BR>
<UL>
<LI>加入本文的<A href="javascript:void forumWindow()"><FONT color=#5c81a7>论坛</FONT></A> 。(您也可以通过点击文章顶部或者底部的论坛链接参加讨论。)</LI></UL><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><BR></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/cn/java/wa-selenium-ajax/#main"><B><FONT color=#996699>回页首</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=author><SPAN class=atitle><FONT face=Arial size=4>关于作者</FONT></SPAN></A></P>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD colSpan=2><FONT face=Arial size=4><IMG height=5 alt="" src="http://www.ibm.com/i/c.gif" width="100%"></FONT></TD></TR>
<TR vAlign=top align=left>
<TD>
<P><FONT face=Arial size=4></FONT></P></TD>
<TD>
<P>Christian Hellsten 目前是 IBM 业务咨询服务部的 IT 专家。他在 IT 界有六年以上从事大型电子商务项目的经验。他的专业领域包括 Java、J2EE 和 EAI 架构、设计和开发。他还喜欢使用更动态的、高效且有趣的语言和框架，例如 Python、Ruby 和 Ruby on Rails。</P></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><img src ="http://www.blogjava.net/TrampEagle/aggbug/30264.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/TrampEagle/" target="_blank">TrampEagle</a> 2006-02-11 15:08 <a href="http://www.blogjava.net/TrampEagle/articles/30264.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>附录 A – 测试用例</title><link>http://www.blogjava.net/TrampEagle/articles/30154.html</link><dc:creator>TrampEagle</dc:creator><author>TrampEagle</author><pubDate>Fri, 10 Feb 2006 07:06:00 GMT</pubDate><guid>http://www.blogjava.net/TrampEagle/articles/30154.html</guid><wfw:comment>http://www.blogjava.net/TrampEagle/comments/30154.html</wfw:comment><comments>http://www.blogjava.net/TrampEagle/articles/30154.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/TrampEagle/comments/commentRss/30154.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/TrampEagle/services/trackbacks/30154.html</trackback:ping><description><![CDATA[原文引自：<A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/AppendATestCases.mspx">http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/AppendATestCases.mspx</A><BR><BR>
<H1>附录 A – 测试用例</H1>
<H2 class=subtitle></H2>
<DIV class=date>发布日期： 8/19/2004<SPAN class=datePipe> | </SPAN>更新日期： 8/19/2004</DIV>
<DIV class=overview>
<DIV style="WIDTH: 250px"><IMG height=68 alt="" src="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/art/pponline.gif" width=250 border=0><BR>
<P class=figureCaption></P>
<DIV class=figureRule></DIV></DIV>
<P>Microsoft Corporation</P>
<P><B>内容</B>：讨论 Offline Application Block 的测试方法。</P></DIV>
<CENTER><IMG title="" height=6 alt=* src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/3squares.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/architecture/architecture/architecturetopic/AppendATestCases.mspx#EIAA"><IMG height=9 alt=功能测试 hspace=4 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_down.gif" width=7 vspace=2 border=0></A></TD>
<TD class=onThisPage><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/AppendATestCases.mspx#EIAA"><FONT color=#002c99>功能测试</FONT></A></TD></TR>
<TR vAlign=top>
<TD><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/AppendATestCases.mspx#EHAA"><FONT color=#002c99><IMG height=9 alt=白盒测试 hspace=4 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_down.gif" width=7 vspace=2 border=0></FONT></A></TD>
<TD class=onThisPage><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/AppendATestCases.mspx#EHAA"><FONT color=#002c99>白盒测试</FONT></A></TD></TR>
<TR vAlign=top>
<TD><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/AppendATestCases.mspx#EGAA"><FONT color=#002c99><IMG height=9 alt=安全性测试 hspace=4 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_down.gif" width=7 vspace=2 border=0></FONT></A></TD>
<TD class=onThisPage><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/AppendATestCases.mspx#EGAA"><FONT color=#002c99>安全性测试</FONT></A></TD></TR>
<TR vAlign=top>
<TD><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/AppendATestCases.mspx#EFAA"><FONT color=#002c99><IMG height=9 alt=性能测试 hspace=4 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_down.gif" width=7 vspace=2 border=0></FONT></A></TD>
<TD class=onThisPage><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/AppendATestCases.mspx#EFAA"><FONT color=#002c99>性能测试</FONT></A></TD></TR>
<TR vAlign=top>
<TD><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/AppendATestCases.mspx#EEAA"><FONT color=#002c99><IMG height=9 alt=集成测试 hspace=4 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_down.gif" width=7 vspace=2 border=0></FONT></A></TD>
<TD class=onThisPage><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/AppendATestCases.mspx#EEAA"><FONT color=#002c99>集成测试</FONT></A></TD></TR>
<TR vAlign=top>
<TD><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/AppendATestCases.mspx#EDAA"><FONT color=#002c99><IMG height=9 alt=内容测试 hspace=4 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_down.gif" width=7 vspace=2 border=0></FONT></A></TD>
<TD class=onThisPage><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/AppendATestCases.mspx#EDAA"><FONT color=#002c99>内容测试</FONT></A></TD></TR>
<TR vAlign=top>
<TD><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/AppendATestCases.mspx#ECAA"><FONT color=#002c99><IMG height=9 alt=安装测试 hspace=4 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_down.gif" width=7 vspace=2 border=0></FONT></A></TD>
<TD class=onThisPage><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/AppendATestCases.mspx#ECAA"><FONT color=#002c99>安装测试</FONT></A></TD></TR></TBODY></TABLE>
<P>附录 A 说明了针对 Offline Application Block 运行以确保其正常工作的测试。在开发自己的应用程序时，我们可以将它们作为要考虑的测试类型的建议。</P>
<P>这些测试包括以下七个方面： </P>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>功能测试，确保应用程序符合指定的要求 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>白盒测试，测试小范围特定区域的代码 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>安全性测试，测试应用程序及其数据是否受到保护、隐私是否受到保护，以及数据是否正确加密 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>性能测试，测试应用程序在各种情况下的处理和响应时间 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>集成测试，确保应用程序与其他系统和组件配合工作 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>内容测试，验证文档的正确性 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>安装测试，验证应用程序已正确安装在客户端计算机上 </P></TD></TR></TBODY></TABLE>
<P>文档的每个部分都与其中一个类别相对应。我们为每个类别提供了两个表格。 </P>
<P>第一个表格包括： </P>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>范围，是指该测试类别所覆盖的领域 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>时间，是指这些测试应在开发周期的何时开始 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>用于执行这些测试的工具 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>自动测试是否适用于这些类型的测试 </P></TD></TR></TBODY></TABLE>
<P>第二个表格是属于该类别的示例测试的清单。</P><A name=EIAA></A>
<H2>功能测试</H2>
<P>功能测试可确保应用程序符合指定的要求。</P>
<P><B>表</B><B> 1:</B><B>功能测试定义</B></P>
<TABLE class=dataTable id=ECIAA cellSpacing=0 cellPadding=0>
<THEAD>
<TR class=stdHeader vAlign=top>
<TD id=colEBBCIAA>领域</TD>
<TD id=colEABCIAA style="BORDER-RIGHT: #cccccc 1px solid">定义</TD></TR></THEAD>
<TBODY>
<TR class=record vAlign=top>
<TD>
<P class=lastInCell>范围</P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>这些测试覆盖了智能客户端应用程序的功能，以确保它符合指定的要求。它们可提供应用程序的有效性和使用性的反馈，以解决要求和实际应用程序之间的偏差。它们使用基于功能和基于案例的方法来测试图形用户界面 (GUI)。</P></TD></TR>
<TR class=evenRecord vAlign=top>
<TD>
<P class=lastInCell>开始时间</P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>在确定要求后，就应尽早计划测试并撰写文档。随着越来越多的端到端功能投入使用，测试的数量将不断增加。</P></TD></TR>
<TR class=record vAlign=top>
<TD>
<P class=lastInCell>要使用的工具</P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P>要使用的工具包括：</P>
<P>在 GUI 可用之前测试已公开功能的示例窗体。</P>
<P>GUI 记录和播放工具。</P></TD></TR>
<TR class=evenRecord vAlign=top>
<TD>
<P class=lastInCell>自动角色</P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>建议使用自动功能针对新的软件内部版本启用多个快速的版本验证测试。</P></TD></TR></TBODY></TABLE>
<DIV class=dataTableBottomMargin></DIV>
<P><B>表</B><B> 2</B><B>：功能测试</B></P>
<TABLE class=dataTable id=EAIAA cellSpacing=0 cellPadding=0>
<THEAD>
<TR class=stdHeader vAlign=top>
<TD id=colEBBAIAA>成功/失败</TD>
<TD id=colEABAIAA style="BORDER-RIGHT: #cccccc 1px solid">测试</TD></TR></THEAD>
<TBODY>
<TR class=record vAlign=top>
<TD>
<P class=lastInCell></P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>应用程序具有脱机和联机状态的视觉显示。</P></TD></TR>
<TR class=evenRecord vAlign=top>
<TD>
<P class=lastInCell></P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>应用程序允许用户通过 Internet 登录。</P></TD></TR>
<TR class=record vAlign=top>
<TD>
<P class=lastInCell></P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>应用程序允许用户通过企业网络登录。</P></TD></TR>
<TR class=evenRecord vAlign=top>
<TD>
<P class=lastInCell></P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>当用户登录时，应用程序提示用户是进入脱机状态还是联机状态（仅当用户处于联机状态时适用）。</P></TD></TR>
<TR class=record vAlign=top>
<TD>
<P class=lastInCell></P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>当用户注销时，应用程序提示用户同步化工作队列数据（仅当用户在脱机状态下对工作队列进行了更改，才会发生这种情况）。</P></TD></TR>
<TR class=evenRecord vAlign=top>
<TD>
<P class=lastInCell></P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>测试应用程序在联机时显示工作项目。</P></TD></TR>
<TR class=record vAlign=top>
<TD>
<P class=lastInCell></P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>测试应用程序在脱机时显示工作项目。</P></TD></TR>
<TR class=evenRecord vAlign=top>
<TD>
<P class=lastInCell></P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>测试应用程序能够更改要求应用程序联机的工作项目。</P></TD></TR>
<TR class=record vAlign=top>
<TD></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>测试应用程序能够提示用户为已更新的队列数据同步化工作项目。</P></TD></TR>
<TR class=evenRecord vAlign=top>
<TD>
<P class=lastInCell></P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>测试用户可以手动强制系统脱机。</P></TD></TR>
<TR class=record vAlign=top>
<TD>
<P class=lastInCell></P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>测试用户可以手动将系统切换回联机状态。</P></TD></TR>
<TR class=evenRecord vAlign=top>
<TD>
<P class=lastInCell></P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>测试应用程序具有自动连接状态检测策略。</P></TD></TR>
<TR class=record vAlign=top>
<TD>
<P class=lastInCell></P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>测试应用程序具有用于下载工作项目的下载界面。</P></TD></TR>
<TR class=evenRecord vAlign=top>
<TD>
<P class=lastInCell></P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>测试用户可以下载工作项目、脱机、在脱机状态下更新工作项目、联机以及在联机状态下进行同步。</P></TD></TR>
<TR class=record vAlign=top>
<TD>
<P class=lastInCell></P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>测试应用程序在编辑数据时将其标记为 dirty，并检查陈旧数据和废数据是否过期。只能从服务器覆盖陈旧数据。</P></TD></TR>
<TR class=evenRecord vAlign=top>
<TD>
<P class=lastInCell></P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>测试检查 MSDE 缓存存储提供程序是否用于缓存数据。</P></TD></TR>
<TR class=record vAlign=top>
<TD>
<P class=lastInCell></P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>测试检查独立缓存存储提供程序是否用于缓存数据。</P></TD></TR>
<TR class=evenRecord vAlign=top>
<TD>
<P class=lastInCell></P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>测试检查“消息队列”队列存储提供程序是否用于排队数据。</P></TD></TR>
<TR class=record vAlign=top>
<TD>
<P class=lastInCell></P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>测试检查 MSDE 队列存储提供程序是否用于排队数据。</P></TD></TR>
<TR class=evenRecord vAlign=top>
<TD>
<P class=lastInCell></P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>测试在脱机状态下对客户端数据进行的多个更新在联机时进行同步。</P></TD></TR>
<TR class=record vAlign=top>
<TD>
<P class=lastInCell></P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>测试可以为 ConnectionDetectionStrategy 配置多个提供程序。</P></TD></TR>
<TR class=evenRecord vAlign=top>
<TD>
<P class=lastInCell></P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>测试支持使用 ReferenceDataCache 的不同缓存存储区。</P></TD></TR>
<TR class=record vAlign=top>
<TD>
<P class=lastInCell></P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>测试可以为 QueueProvider 配置多个提供程序。</P></TD></TR>
<TR class=evenRecord vAlign=top>
<TD>
<P class=lastInCell></P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>测试 ConnectionDetectionStrategy 的轮询间隔配置。</P></TD></TR>
<TR class=record vAlign=top>
<TD>
<P class=lastInCell></P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>测试 Executor 操作的轮询间隔配置。</P></TD></TR>
<TR class=evenRecord vAlign=top>
<TD>
<P class=lastInCell></P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>测试 QueueProvider 的最大队列消息配置。</P></TD></TR>
<TR class=record vAlign=top>
<TD>
<P class=lastInCell></P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>测试缓存数据的过期配置。</P></TD></TR>
<TR class=evenRecord vAlign=top>
<TD>
<P class=lastInCell></P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>测试用于连接到 MSDE 的连接字符串配置。</P></TD></TR>
<TR class=record vAlign=top>
<TD>
<P class=lastInCell></P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>测试使用自定义提供程序的加密是通过 ICryptographicProvider 接口实现的。</P></TD></TR>
<TR class=evenRecord vAlign=top>
<TD>
<P class=lastInCell></P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>测试下载频率的配置属性。</P></TD></TR>
<TR class=record vAlign=top>
<TD>
<P class=lastInCell></P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>测试错误和边界条件。</P></TD></TR>
<TR class=evenRecord vAlign=top>
<TD>
<P class=lastInCell></P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>测试可以跨多个 Offline Application Block 的运行实例来存储和检索数据。</P></TD></TR></TBODY></TABLE>
<DIV class=dataTableBottomMargin></DIV>
<DIV style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 10px"><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/AppendATestCases.mspx#top"><IMG height=9 alt=返回页首 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_up.gif" width=7 border=0></A><A class=topOfPage href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/AppendATestCases.mspx#top"><FONT color=#002c99>返回页首</FONT></A></DIV><A name=EHAA></A>
<H2>白盒测试</H2>
<P>白盒测试主要用于小范围特定区域的代码。</P>
<P><B>表</B><B> 3: </B><B>白盒测试定义</B></P>
<TABLE class=dataTable id=ECHAA cellSpacing=0 cellPadding=0>
<THEAD>
<TR class=stdHeader vAlign=top>
<TD id=colEBBCHAA>领域</TD>
<TD id=colEABCHAA style="BORDER-RIGHT: #cccccc 1px solid">定义</TD></TR></THEAD>
<TBODY>
<TR class=record vAlign=top>
<TD>
<P class=lastInCell>范围</P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>这些测试覆盖小范围的代码单元，并使用代码演练、代码检查以及使用测试框架的单元级别测试来执行。</P></TD></TR>
<TR class=evenRecord vAlign=top>
<TD>
<P class=lastInCell>开始时间</P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>这些测试在开发周期的早期进行。应在代码刚被开发出来、并且还是可随时演练的小型独立代码单元时执行测试。</P></TD></TR>
<TR class=record vAlign=top>
<TD>
<P>要使用的工具</P>
<P>代码范围的工具</P>
<P>内存分析工具</P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P>要使用的工具包括：</P>
<P>查看代码是否符合开发准则的代码检查</P>
<P>单元测试框架工具（应该能够自动执行）</P></TD></TR>
<TR class=evenRecord vAlign=top>
<TD>
<P class=lastInCell>自动角色</P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>单元测试自动化对于针对仍处于开发中的应用程序执行可重复的全面测试是至关重要的。</P></TD></TR></TBODY></TABLE>
<DIV class=dataTableBottomMargin></DIV>
<P><B>表</B><B> 4</B><B>：白盒测试</B></P>
<TABLE class=dataTable id=EAHAA cellSpacing=0 cellPadding=0>
<THEAD>
<TR class=stdHeader vAlign=top>
<TD id=colEBBAHAA>成功/失败</TD>
<TD id=colEABAHAA style="BORDER-RIGHT: #cccccc 1px solid">测试</TD></TR></THEAD>
<TBODY>
<TR class=record vAlign=top>
<TD>
<P class=lastInCell></P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>演练代码并检查它是否满足质量要求，例如是否符合逻辑，是否符合开发标准和最佳做法，以及是否具有高可读性的注释。</P></TD></TR>
<TR class=evenRecord vAlign=top>
<TD>
<P class=lastInCell></P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>使用代码范围的工具运行代码，以确保所有可用的代码都在进行测试。</P></TD></TR>
<TR class=record vAlign=top>
<TD>
<P class=lastInCell></P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>执行代码的内存分析，以确保正确释放对象和收集垃圾。</P></TD></TR>
<TR class=evenRecord vAlign=top>
<TD>
<P class=lastInCell></P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>使用单元测试框架工具来创建用于测试公共方法的单元测试。在代码开发过程中扩展这些测试，并定期运行它们。单元测试可作为一种快速检查，用于确保代码在更改后不会被损坏。要使单元测试具有更强的逻辑性，请将它们映射到“功能测试”部分中说明的测试用例。</P></TD></TR></TBODY></TABLE>
<DIV class=dataTableBottomMargin></DIV>
<DIV style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 10px"><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/AppendATestCases.mspx#top"><IMG height=9 alt=返回页首 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_up.gif" width=7 border=0></A><A class=topOfPage href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/AppendATestCases.mspx#top"><FONT color=#002c99>返回页首</FONT></A></DIV><A name=EGAA></A>
<H2>安全性测试</H2>
<P>安全性测试用于测试应用程序及其数据是否受到保护，隐私是否受到保护，以及数据是否正确加密。</P>
<P><B>表</B><B> 5</B><B>：安全性测试定义</B></P>
<TABLE class=dataTable id=ECGAA cellSpacing=0 cellPadding=0>
<THEAD>
<TR class=stdHeader vAlign=top>
<TD id=colEBBCGAA>领域</TD>
<TD id=colEABCGAA style="BORDER-RIGHT: #cccccc 1px solid">定义</TD></TR></THEAD>
<TBODY>
<TR class=record vAlign=top>
<TD>
<P class=lastInCell>范围</P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>这些测试覆盖的范围包括：验证隐私是否受到保护、数据是否加密、数据是否防篡改，以及应用程序是否能够承受各种类型的恶意攻击。安全性测试应主要用于独立的系统单元和整个应用程序。</P></TD></TR>
<TR class=evenRecord vAlign=top>
<TD>
<P class=lastInCell>开始时间</P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>在确定要求后，就应计划这些测试。这些测试应在实现安全措施和方法时执行。</P></TD></TR>
<TR class=record vAlign=top>
<TD>
<P class=lastInCell>要使用的工具</P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P>要使用的工具包括：</P>
<P>DOS 攻击模拟</P>
<P>网络探测工具</P>
<P>此外，请参阅清单章节</P></TD></TR>
<TR class=evenRecord vAlign=top>
<TD>
<P class=lastInCell>自动化</P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>不适用</P></TD></TR></TBODY></TABLE>
<DIV class=dataTableBottomMargin></DIV>
<P><B>表</B><B> 6</B><B>：安全性测试</B></P>
<TABLE class=dataTable id=EAGAA cellSpacing=0 cellPadding=0>
<THEAD>
<TR class=stdHeader vAlign=top>
<TD id=colEBBAGAA>成功/失败</TD>
<TD id=colEABAGAA style="BORDER-RIGHT: #cccccc 1px solid">测试</TD></TR></THEAD>
<TBODY>
<TR class=record vAlign=top>
<TD>
<P class=lastInCell></P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>测试应用程序能够将参考数据安全地下载到本地计算机。</P></TD></TR>
<TR class=evenRecord vAlign=top>
<TD>
<P class=lastInCell></P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>测试应用程序能够将更新后的参考数据安全地上载到服务器。</P></TD></TR>
<TR class=record vAlign=top>
<TD>
<P class=lastInCell></P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>测试应用程序能够将对已下载数据的访问权限制给经授权的用户。</P></TD></TR>
<TR class=evenRecord vAlign=top>
<TD>
<P class=lastInCell></P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>测试可以确保通过公司 Intranet 访问服务的安全。</P></TD></TR>
<TR class=record vAlign=top>
<TD>
<P class=lastInCell></P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>测试可以确保通过 Internet 访问服务的安全。</P></TD></TR>
<TR class=evenRecord vAlign=top>
<TD>
<P class=lastInCell></P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>测试可以安全存储已下载的消息。</P></TD></TR>
<TR class=record vAlign=top>
<TD>
<P class=lastInCell></P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>跟踪查看已下载的参考数据是否被篡改。</P></TD></TR>
<TR class=evenRecord vAlign=top>
<TD>
<P class=lastInCell></P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>测试检查是否可以通过正确的登录凭据限制对排队数据的访问。</P></TD></TR>
<TR class=record vAlign=top>
<TD>
<P class=lastInCell></P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>测试检查排队数据是否已正确加密并签名。</P></TD></TR>
<TR class=evenRecord vAlign=top>
<TD>
<P class=lastInCell></P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>测试检查是否可以通过正确的登录凭据限制对缓存数据的访问。</P></TD></TR>
<TR class=record vAlign=top>
<TD>
<P class=lastInCell></P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>测试检查缓存数据是否已正确加密并签名。</P></TD></TR>
<TR class=evenRecord vAlign=top>
<TD>
<P class=lastInCell></P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>测试检查是否可以通过第三方证书（例如 Verisign）使用服务进行下载。</P></TD></TR>
<TR class=record vAlign=top>
<TD>
<P class=lastInCell></P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>测试检查程序集是否在部署服务器上进行加密和数字签名，并在客户端下载时进行验证。</P></TD></TR>
<TR class=evenRecord vAlign=top>
<TD>
<P class=lastInCell></P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>测试检查为服务生成的代理是否具有有效的终结点，以防止欺骗。</P></TD></TR>
<TR class=record vAlign=top>
<TD>
<P class=lastInCell></P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>测试检查多个用户的缓存数据和排队数据共享一个客户端是否安全。</P></TD></TR>
<TR class=evenRecord vAlign=top>
<TD>
<P class=lastInCell></P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>验证 DemandReflectPermission 在使用反射的代码中正确实现。</P></TD></TR></TBODY></TABLE>
<DIV class=dataTableBottomMargin></DIV>
<DIV style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 10px"><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/AppendATestCases.mspx#top"><IMG height=9 alt=返回页首 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_up.gif" width=7 border=0></A><A class=topOfPage href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/AppendATestCases.mspx#top"><FONT color=#002c99>返回页首</FONT></A></DIV><A name=EFAA></A>
<H2>性能测试</H2>
<P>性能测试用于测试应用程序在各种情况下的处理和响应时间。</P>
<P><B>表</B><B> 7</B><B>：性能测试定义</B></P>
<TABLE class=dataTable id=ECFAA cellSpacing=0 cellPadding=0>
<THEAD>
<TR class=stdHeader vAlign=top>
<TD id=colEBBCFAA>领域</TD>
<TD id=colEABCFAA style="BORDER-RIGHT: #cccccc 1px solid">定义</TD></TR></THEAD>
<TBODY>
<TR class=record vAlign=top>
<TD>
<P class=lastInCell>范围</P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>这些测试覆盖了在各种数据加载、内存压力条件、网络可用性以及不同的连接速度下，应用程序的处理和响应性能。</P></TD></TR>
<TR class=evenRecord vAlign=top>
<TD>
<P class=lastInCell>开始时间</P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>在确定要求后，就应该计划这些测试。可以通过使用内部测试存根和性能测试工具来对已完成的单元进行测试。随着越来越多的端到端功能投入使用，测试的数量将不断增加。</P></TD></TR>
<TR class=record vAlign=top>
<TD>
<P class=lastInCell>要使用的工具</P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P>要使用的工具包括：</P>
<P>性能测试工具或自定义测试工具（应自动化）</P>
<P>PerfMon</P>
<P>内存清理工具</P>
<P>磁盘空间清理工具</P>
<P>性能分析工具</P></TD></TR>
<TR class=evenRecord vAlign=top>
<TD>
<P class=lastInCell>自动化</P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>建议使用自动功能针对较新的软件内部版本启用多个快速的性能测试。这将确保应用程序符合可接受的性能标准，同时还有助于比较研究。</P></TD></TR></TBODY></TABLE>
<DIV class=dataTableBottomMargin></DIV>
<P><B>表</B><B> 8</B><B>：性能测试</B></P>
<TABLE class=dataTable id=EAFAA cellSpacing=0 cellPadding=0>
<THEAD>
<TR class=stdHeader vAlign=top>
<TD id=colEBBAFAA>成功/失败</TD>
<TD id=colEABAFAA style="BORDER-RIGHT: #cccccc 1px solid">测试</TD></TR></THEAD>
<TBODY>
<TR class=record vAlign=top>
<TD>
<P class=lastInCell></P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>测试检查在超出延迟时间段后是否会发生下载。</P></TD></TR>
<TR class=evenRecord vAlign=top>
<TD>
<P class=lastInCell></P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>测试检查在超出延迟时间段后是否会发生上载。</P></TD></TR>
<TR class=record vAlign=top>
<TD>
<P class=lastInCell></P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>通过在一段时间内在某一行中数百次地下载项目来测试性能。</P></TD></TR>
<TR class=evenRecord vAlign=top>
<TD>
<P class=lastInCell></P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>通过在一段时间内在某一行中数百次地上载项目来测试性能。</P></TD></TR>
<TR class=record vAlign=top>
<TD>
<P class=lastInCell></P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>通过在一段时间内下载和上载不同大小的数据来测试性能。</P></TD></TR>
<TR class=evenRecord vAlign=top>
<TD>
<P class=lastInCell></P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>通过使用不同的带宽和延迟时间下载和上载数据来测试性能。</P></TD></TR>
<TR class=record vAlign=top>
<TD>
<P class=lastInCell></P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>通过使用有限的内存和磁盘空间下载和上载数据来测试性能。</P></TD></TR>
<TR class=evenRecord vAlign=top>
<TD>
<P class=lastInCell></P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P>分析以下类型的数据：</P>
<P>线程数量</P>
<P>系统池资源</P>
<P>争用</P>
<P>进程工作集</P>
<P>系统队列</P>
<P>进程 CPU 上下文</P>
<P>内存和 IO</P>
<P>网络</P>
<P>系统资源</P>
<P>进程可用性</P>
<P>异常</P>
<P>进程资源</P>
<P>事务处理时间。</P></TD></TR></TBODY></TABLE>
<DIV class=dataTableBottomMargin></DIV>
<DIV style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 10px"><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/AppendATestCases.mspx#top"><IMG height=9 alt=返回页首 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_up.gif" width=7 border=0></A><A class=topOfPage href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/AppendATestCases.mspx#top"><FONT color=#002c99>返回页首</FONT></A></DIV><A name=EEAA></A>
<H2>集成测试</H2>
<P>集成测试可确保应用程序与其他系统和组件配合工作。</P>
<P><B>表</B><B> 9</B><B>：集成测试定义</B></P>
<TABLE class=dataTable id=ECEAA cellSpacing=0 cellPadding=0>
<THEAD>
<TR class=stdHeader vAlign=top>
<TD id=colEBBCEAA>领域</TD>
<TD id=colEABCEAA style="BORDER-RIGHT: #cccccc 1px solid">定义</TD></TR></THEAD>
<TBODY>
<TR class=record vAlign=top>
<TD>
<P class=lastInCell>范围</P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>这些测试覆盖了应用程序与外部组件和已存在系统的集成。测试应用程序与 Web 服务的交互就是一个示例。这些测试可验证整个工作流，以及组成应用程序的各个组件之间的所有交互。</P></TD></TR>
<TR class=evenRecord vAlign=top>
<TD>
<P class=lastInCell>开始时间</P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>在确定要求后，就应该计划这些测试。随着不同系统部件的集成，测试的数量将不断增加。</P></TD></TR>
<TR class=record vAlign=top>
<TD>
<P class=lastInCell>要使用的工具</P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>不适用。</P></TD></TR>
<TR class=evenRecord vAlign=top>
<TD>
<P class=lastInCell>自动化</P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>不适用。</P></TD></TR></TBODY></TABLE>
<DIV class=dataTableBottomMargin></DIV>
<P><B>表</B><B> 10</B><B>：集成测试</B></P>
<TABLE class=dataTable id=EAEAA cellSpacing=0 cellPadding=0>
<THEAD>
<TR class=stdHeader vAlign=top>
<TD id=colEBBAEAA>成功/失败</TD>
<TD id=colEABAEAA style="BORDER-RIGHT: #cccccc 1px solid">测试</TD></TR></THEAD>
<TBODY>
<TR class=record vAlign=top>
<TD>
<P class=lastInCell></P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>测试客户端应用程序代码和 Web 服务之间的集成。</P></TD></TR>
<TR class=evenRecord vAlign=top>
<TD>
<P class=lastInCell></P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>测试工作项目的下载和随后的已修改工作项目的上载。</P></TD></TR>
<TR class=record vAlign=top>
<TD>
<P class=lastInCell></P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>测试脱机状态下工作项目的排队，以及重新联机后排队消息的处理。</P></TD></TR>
<TR class=evenRecord vAlign=top>
<TD>
<P class=lastInCell></P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>验证不同队列提供程序与不同缓存提供程序的配合工作。</P></TD></TR>
<TR class=record vAlign=top>
<TD>
<P class=lastInCell></P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>在脱机状态下将多个更新应用到工作项目，并在重新联机时检查同步。</P></TD></TR>
<TR class=evenRecord vAlign=top>
<TD>
<P class=lastInCell></P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>测试在服务器端筛选参考数据的方法，以下载数据库的适用部分。</P></TD></TR>
<TR class=record vAlign=top>
<TD>
<P class=lastInCell></P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>测试应用程序可以根据服务器通知，为用户显示与其他用户所作更改冲突的工作项目列表。</P></TD></TR>
<TR class=evenRecord vAlign=top>
<TD>
<P class=lastInCell></P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>测试应用程序可以根据定义的时间段将日志与服务器日志进行同步。</P></TD></TR>
<TR class=record vAlign=top>
<TD>
<P class=lastInCell></P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>测试在客户端修改数据的同时、该数据正在服务器中进行更新所导致的冲突情况。</P></TD></TR>
<TR class=evenRecord vAlign=top>
<TD>
<P class=lastInCell></P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>测试能否通过在服务器上添加新的数据（工作项目）来刷新客户端。</P></TD></TR>
<TR class=record vAlign=top>
<TD>
<P class=lastInCell></P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>测试添加到客户端的新数据（工作项目）能否与服务器进行同步。</P></TD></TR>
<TR class=evenRecord vAlign=top>
<TD>
<P class=lastInCell></P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>修改服务器中工作项目的架构并更新客户端。</P></TD></TR>
<TR class=record vAlign=top>
<TD>
<P class=lastInCell></P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>测试两个或更多独立联机客户端同时更新服务器上的数据的情况。</P></TD></TR>
<TR class=evenRecord vAlign=top>
<TD>
<P class=lastInCell></P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>测试两个或更多脱机客户端修改数据并更新服务器的情况。</P></TD></TR>
<TR class=record vAlign=top>
<TD>
<P class=lastInCell></P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>确保测试用例覆盖了所支持平台上应用程序工作流的验证。</P></TD></TR></TBODY></TABLE>
<DIV class=dataTableBottomMargin></DIV>
<DIV style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 10px"><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/AppendATestCases.mspx#top"><IMG height=9 alt=返回页首 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_up.gif" width=7 border=0></A><A class=topOfPage href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/AppendATestCases.mspx#top"><FONT color=#002c99>返回页首</FONT></A></DIV><A name=EDAA></A>
<H2>内容测试</H2>
<P>内容测试用于验证文档的正确性。</P>
<P><B>表</B><B> 11</B><B>：内容测试定义</B></P>
<TABLE class=dataTable id=ECDAA cellSpacing=0 cellPadding=0>
<THEAD>
<TR class=stdHeader vAlign=top>
<TD id=colEBBCDAA>领域</TD>
<TD id=colEABCDAA style="BORDER-RIGHT: #cccccc 1px solid">定义</TD></TR></THEAD>
<TBODY>
<TR class=record vAlign=top>
<TD>
<P class=lastInCell>范围</P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>这些测试可验证在安装和操作过程中为用户提供的说明内容。这些测试覆盖了补充的用户指南和帮助文件。这些测试可验证内容的使用性、正确性和完整性。</P></TD></TR>
<TR class=evenRecord vAlign=top>
<TD>
<P class=lastInCell>要使用的工具</P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>内容测试大多通过手动完成。熟悉应用程序的使用性和测试工程师能够指出矛盾或缺少的信息，以及应用程序和文档之间的差异。</P></TD></TR>
<TR class=record vAlign=top>
<TD>
<P class=lastInCell>要使用的工具</P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>不适用。</P></TD></TR>
<TR class=evenRecord vAlign=top>
<TD>
<P class=lastInCell>自动化</P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>不适用。</P></TD></TR></TBODY></TABLE>
<DIV class=dataTableBottomMargin></DIV>
<P><B>表</B><B> 12</B><B>：内容测试</B></P>
<TABLE class=dataTable id=EADAA cellSpacing=0 cellPadding=0>
<THEAD>
<TR class=stdHeader vAlign=top>
<TD id=colEBBADAA>成功/失败</TD>
<TD id=colEABADAA style="BORDER-RIGHT: #cccccc 1px solid">测试</TD></TR></THEAD>
<TBODY>
<TR class=record vAlign=top>
<TD>
<P class=lastInCell></P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>测试内容在技术上的正确性。</P></TD></TR>
<TR class=evenRecord vAlign=top>
<TD>
<P class=lastInCell></P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>测试有关操作、流程和时间安排的步骤的顺序是否正确。</P></TD></TR>
<TR class=record vAlign=top>
<TD>
<P class=lastInCell></P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>测试内容的相关性。</P></TD></TR>
<TR class=evenRecord vAlign=top>
<TD>
<P class=lastInCell></P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>测试内容不是模棱两可并且是易于遵循的。</P></TD></TR>
<TR class=record vAlign=top>
<TD>
<P class=lastInCell></P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>测试内容的一致和统一。</P></TD></TR>
<TR class=evenRecord vAlign=top>
<TD>
<P class=lastInCell></P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>测试内容为每个主题都提供了完整的信息。</P></TD></TR>
<TR class=record vAlign=top>
<TD>
<P class=lastInCell></P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>测试需要进一步说明的主题带有有助于更好理解的适当示例。</P></TD></TR>
<TR class=evenRecord vAlign=top>
<TD>
<P class=lastInCell></P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>测试主题带有对相应和相关主题的引用。</P></TD></TR>
<TR class=record vAlign=top>
<TD>
<P class=lastInCell></P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>测试内容是否考虑到期望用户的技术水平和熟悉的交流语言。</P></TD></TR></TBODY></TABLE>
<DIV class=dataTableBottomMargin></DIV>
<DIV style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 10px"><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/AppendATestCases.mspx#top"><IMG height=9 alt=返回页首 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_up.gif" width=7 border=0></A><A class=topOfPage href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/AppendATestCases.mspx#top"><FONT color=#002c99>返回页首</FONT></A></DIV><A name=ECAA></A>
<H2>安装测试</H2>
<P>安装测试用于验证应用程序是否正确安装在客户端计算机上。</P>
<P><B>表</B><B> 13</B><B>：安装测试定义</B></P>
<TABLE class=dataTable id=ECCAA cellSpacing=0 cellPadding=0>
<THEAD>
<TR class=stdHeader vAlign=top>
<TD id=colEBBCCAA>领域</TD>
<TD id=colEABCCAA style="BORDER-RIGHT: #cccccc 1px solid">定义</TD></TR></THEAD>
<TBODY>
<TR class=record vAlign=top>
<TD>
<P class=lastInCell>范围</P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>这些测试可验证应用程序最后交付的功能和客户端计算机上的安装，并验证所有适用文件的交付。</P></TD></TR>
<TR class=evenRecord vAlign=top>
<TD>
<P class=lastInCell>开始时间</P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>这些测试通常在开发和测试周期的最后进行。</P></TD></TR>
<TR class=record vAlign=top>
<TD>
<P class=lastInCell>要使用的工具</P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>要使用的工具包括 GUI 记录和播放工具（应该能够自动运行）。</P></TD></TR>
<TR class=evenRecord vAlign=top>
<TD>
<P class=lastInCell>自动角色</P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>使用在功能测试过程中创建的版本验证测试 (BVT) 自动脚本，以便在安装后快速验证应用程序的主要功能。这就提供了验证安装是否正确的快速方法。</P></TD></TR>
<TR class=record vAlign=top>
<TD>
<P class=lastInCell>自动工具</P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>针对已安装的应用程序执行 BVT 测试所使用的 GUI 记录和播放工具。该工具提供了验证安装的快速方法。</P></TD></TR></TBODY></TABLE>
<DIV class=dataTableBottomMargin></DIV>
<P>表 14：安装测试</P>
<TABLE class=dataTable id=EACAA cellSpacing=0 cellPadding=0>
<THEAD>
<TR class=stdHeader vAlign=top>
<TD id=colEBBACAA>成功/失败</TD>
<TD id=colEABACAA style="BORDER-RIGHT: #cccccc 1px solid">测试</TD></TR></THEAD>
<TBODY>
<TR class=record vAlign=top>
<TD>
<P class=lastInCell></P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>测试应用程序安装、运行脚本时未出现错误，以及所有主要功能都能通过测试。</P></TD></TR>
<TR class=evenRecord vAlign=top>
<TD>
<P class=lastInCell></P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>测试安装在客户端计算机上的所有文件的版本（包括代码和内容）都正确。</P></TD></TR>
<TR class=record vAlign=top>
<TD>
<P class=lastInCell></P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>测试可以卸载应用程序并测试清理的验证。</P></TD></TR>
<TR class=evenRecord vAlign=top>
<TD>
<P class=lastInCell></P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>在安装中验证文件的命名标准。</P></TD></TR>
<TR class=record vAlign=top>
<TD>
<P class=lastInCell></P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>验证安装程序在遇到错误情况（例如磁盘空间不足）时可以正常退出。</P></TD></TR>
<TR class=evenRecord vAlign=top>
<TD>
<P class=lastInCell></P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>在安装过程中验证注册表项，以及在卸载过程中验证注册表的清理。</P></TD></TR>
<TR class=record vAlign=top>
<TD>
<P class=lastInCell></P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>执行全新的计算机安装。这台计算机带有新安装的操作系统和少量必需的已安装组件。</P></TD></TR>
<TR class=evenRecord vAlign=top>
<TD>
<P class=lastInCell></P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>测试具有不同软件配置的计算机上的安装。</P></TD></TR>
<TR class=record vAlign=top>
<TD>
<P class=lastInCell></P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>测试安装程序创建了正确的 Start 菜单项。</P></TD></TR>
<TR class=evenRecord vAlign=top>
<TD>
<P class=lastInCell></P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>测试安装程序将文件置于正确的文件夹中。</P></TD></TR>
<TR class=record vAlign=top>
<TD>
<P class=lastInCell></P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>测试程序集是否在部署服务器上进行加密和数字签名，并在客户端下载时进行验证。</P></TD></TR></TBODY></TABLE>
<DIV class=dataTableBottomMargin></DIV>
<P><A href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnpag/html/offline-AP01.asp" target=_blank><FONT color=#002c99>转到原英文页面</FONT></A></P><img src ="http://www.blogjava.net/TrampEagle/aggbug/30154.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/TrampEagle/" target="_blank">TrampEagle</a> 2006-02-10 15:06 <a href="http://www.blogjava.net/TrampEagle/articles/30154.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Offline Application Block 配置和安全注意事项</title><link>http://www.blogjava.net/TrampEagle/articles/30153.html</link><dc:creator>TrampEagle</dc:creator><author>TrampEagle</author><pubDate>Fri, 10 Feb 2006 07:05:00 GMT</pubDate><guid>http://www.blogjava.net/TrampEagle/articles/30153.html</guid><wfw:comment>http://www.blogjava.net/TrampEagle/comments/30153.html</wfw:comment><comments>http://www.blogjava.net/TrampEagle/articles/30153.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/TrampEagle/comments/commentRss/30153.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/TrampEagle/services/trackbacks/30153.html</trackback:ping><description><![CDATA[原文引自：<A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/4OffAppBlocConfSecuCons.mspx">http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/4OffAppBlocConfSecuCons.mspx</A><BR><BR>
<H1>Offline Application Block 配置和安全注意事项</H1>
<H2 class=subtitle></H2>
<DIV class=date>发布日期： 8/19/2004<SPAN class=datePipe> | </SPAN>更新日期： 8/19/2004</DIV>
<DIV class=overview>
<DIV style="WIDTH: 250px"><IMG height=68 alt="" src="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/art/pponline.gif" width=250 border=0><BR>
<P class=figureCaption></P>
<DIV class=figureRule></DIV></DIV>
<P>Microsoft Corporation </P>
<P><B>内容</B>：第 4 章提供了成功部署的步骤和重要的安全威胁以及需要考虑的对策。</P></DIV>
<CENTER><IMG title="" height=6 alt=* src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/3squares.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/architecture/architecture/architecturetopic/4OffAppBlocConfSecuCons.mspx#EKAA"><IMG height=9 alt=部署要求 hspace=4 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_down.gif" width=7 vspace=2 border=0></A></TD>
<TD class=onThisPage><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/4OffAppBlocConfSecuCons.mspx#EKAA"><FONT color=#002c99>部署要求</FONT></A></TD></TR>
<TR vAlign=top>
<TD><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/4OffAppBlocConfSecuCons.mspx#EJAA"><FONT color=#002c99><IMG height=9 alt=配置 hspace=4 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_down.gif" width=7 vspace=2 border=0></FONT></A></TD>
<TD class=onThisPage><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/4OffAppBlocConfSecuCons.mspx#EJAA"><FONT color=#002c99>配置</FONT></A></TD></TR>
<TR vAlign=top>
<TD><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/4OffAppBlocConfSecuCons.mspx#EIAA"><FONT color=#002c99><IMG height=9 alt=ConnectionManagerProviders hspace=4 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_down.gif" width=7 vspace=2 border=0></FONT></A></TD>
<TD class=onThisPage><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/4OffAppBlocConfSecuCons.mspx#EIAA"><FONT color=#002c99>ConnectionManagerProviders</FONT></A></TD></TR>
<TR vAlign=top>
<TD><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/4OffAppBlocConfSecuCons.mspx#EHAA"><FONT color=#002c99><IMG height=9 alt=QueueManagerProviders hspace=4 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_down.gif" width=7 vspace=2 border=0></FONT></A></TD>
<TD class=onThisPage><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/4OffAppBlocConfSecuCons.mspx#EHAA"><FONT color=#002c99>QueueManagerProviders</FONT></A></TD></TR>
<TR vAlign=top>
<TD><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/4OffAppBlocConfSecuCons.mspx#EGAA"><FONT color=#002c99><IMG height=9 alt=CryptographicSettings hspace=4 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_down.gif" width=7 vspace=2 border=0></FONT></A></TD>
<TD class=onThisPage><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/4OffAppBlocConfSecuCons.mspx#EGAA"><FONT color=#002c99>CryptographicSettings</FONT></A></TD></TR>
<TR vAlign=top>
<TD><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/4OffAppBlocConfSecuCons.mspx#EFAA"><FONT color=#002c99><IMG height=9 alt=CacheManagerSettings hspace=4 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_down.gif" width=7 vspace=2 border=0></FONT></A></TD>
<TD class=onThisPage><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/4OffAppBlocConfSecuCons.mspx#EFAA"><FONT color=#002c99>CacheManagerSettings</FONT></A></TD></TR>
<TR vAlign=top>
<TD><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/4OffAppBlocConfSecuCons.mspx#EEAA"><FONT color=#002c99><IMG height=9 alt=异常管理 hspace=4 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_down.gif" width=7 vspace=2 border=0></FONT></A></TD>
<TD class=onThisPage><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/4OffAppBlocConfSecuCons.mspx#EEAA"><FONT color=#002c99>异常管理</FONT></A></TD></TR>
<TR vAlign=top>
<TD><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/4OffAppBlocConfSecuCons.mspx#EDAA"><FONT color=#002c99><IMG height=9 alt=安全注意事项 hspace=4 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_down.gif" width=7 vspace=2 border=0></FONT></A></TD>
<TD class=onThisPage><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/4OffAppBlocConfSecuCons.mspx#EDAA"><FONT color=#002c99>安全注意事项</FONT></A></TD></TR>
<TR vAlign=top>
<TD><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/4OffAppBlocConfSecuCons.mspx#ECAA"><FONT color=#002c99><IMG height=9 alt=小结 hspace=4 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_down.gif" width=7 vspace=2 border=0></FONT></A></TD>
<TD class=onThisPage><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/4OffAppBlocConfSecuCons.mspx#ECAA"><FONT color=#002c99>小结</FONT></A></TD></TR></TBODY></TABLE>
<P>在开发和测试基于 Offline Application Block 的应用程序后，就可以在产品环境中部署该应用程序了。本章讨论如何通过为各种提供程序配置应用程序来部署应用程序。本章还讨论了在产品环境中部署应用程序时需要考虑的安全威胁和对策。</P><A name=EKAA></A>
<H2>部署要求</H2>
<P>在部署使用 Offline Application Block 的应用程序之前，请确保目标计算机安装了下列软件组件： </P>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>Microsoft_ Windows_ XP Professional 操作系统 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>Microsoft .NET Framework 1.1 版 </P></TD></TR></TBODY></TABLE>
<P>此外，如果您要使用 Offline Application Block 随附的提供程序，则必须安装下列软件： </P>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>用于队列存储提供程序的 Microsoft 消息队列 (MSMQ) </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>用于队列/缓存存储提供程序的 Microsoft SQL Server™ 桌面引擎 (MSDE) </P></TD></TR></TBODY></TABLE>
<DIV style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 10px"><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/4OffAppBlocConfSecuCons.mspx#top"><IMG height=9 alt=返回页首 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_up.gif" width=7 border=0></A><A class=topOfPage href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/4OffAppBlocConfSecuCons.mspx#top"><FONT color=#002c99>返回页首</FONT></A></DIV><A name=EJAA></A>
<H2>配置</H2>
<P>提供程序是在您在 Microsoft Visual Studio_ .NET 部署系统项目中创建的 App.config 文件中定义的。在该文件中，每个类型的提供程序都对应于一个区段。 </P>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>ConnectionManagerProviders</B> – 此区段包含用于检测应用程序物理连接状态的策略的配置信息。该应用程序块随附的连接检测提供程序使用 Windows 网络 API (WinInet) 来检测网络连接是否存在。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>QueueManagerProviders</B> – 此区段包含用于存储消息的提供程序（包括 <B>MSMQQueueStorageProvider</B>、<B>MSDEQueueStorageProvider</B>、<B>IsolatedStorageQueueStorageProvider</B> 和 <B>InMemoryQueueStorageProvider</B>）的配置信息。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>CryptographicSettings</B> – 此区段包含用于加密提供程序设置的配置信息。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>CacheManagerSettings</B> – 此区段包含用于 Caching Application Block 的配置信息。有关 Caching Application Block 的详细信息，请参阅 Caching Application Block 文档。Offline Application Block 将独立存储缓存提供程序作为用于缓存的附加提供程序。 </P></TD></TR></TBODY></TABLE>
<P>“ConnectionManagerProviders”和“QueueManagerProviders”区段都具有一个名为 &lt;<B>providers</B>&gt; 的元素，它包含“连接管理”和“消息数据管理”子系统的策略。每个策略或提供程序都在 &lt;<B>providers</B>&gt; 区段下有一个相应的子元素。表 4.1 中列出了每个提供程序所具有的公共特性集。</P>
<P><B>表</B><B> 4.1 提供程序的公共特性</B></P>
<TABLE class=dataTable id=EBJAA cellSpacing=0 cellPadding=0>
<THEAD>
<TR class=stdHeader vAlign=top>
<TD id=colEDBBJAA>特性名</TD>
<TD id=colECBBJAA>说明</TD>
<TD id=colEBBBJAA>是否必需</TD>
<TD id=colEABBJAA style="BORDER-RIGHT: #cccccc 1px solid">默认值</TD></TR></THEAD>
<TBODY>
<TR class=record vAlign=top>
<TD>
<P class=lastInCell>name</P></TD>
<TD>
<P class=lastInCell>提供程序的好记的名称。</P></TD>
<TD>
<P class=lastInCell>是</P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>无</P></TD></TR>
<TR class=evenRecord vAlign=top>
<TD>
<P class=lastInCell>type</P></TD>
<TD>
<P class=lastInCell>与提供程序相对应的类的完全限定类型名称。</P></TD>
<TD>
<P class=lastInCell>是</P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>无</P></TD></TR>
<TR class=record vAlign=top>
<TD>
<P class=lastInCell>enabled</P></TD>
<TD>
<P class=lastInCell>指定启用配置文件中的哪个提供程序。您只能将这些提供程序的其中一个设置为 <B>true</B>，以便脱机功能正常工作。</P></TD>
<TD>
<P class=lastInCell>否</P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>False</P></TD></TR></TBODY></TABLE>
<DIV class=dataTableBottomMargin></DIV>
<P>每个提供程序所需的自定义属性被表示为子元素。</P>
<DIV style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 10px"><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/4OffAppBlocConfSecuCons.mspx#top"><IMG height=9 alt=返回页首 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_up.gif" width=7 border=0></A><A class=topOfPage href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/4OffAppBlocConfSecuCons.mspx#top"><FONT color=#002c99>返回页首</FONT></A></DIV><A name=EIAA></A>
<H2>ConnectionManagerProviders</H2>
<P>以下配置文件摘录显示了 ConnectionManagersProviders 区段是如何在 App.config 中定义的： </P><PRE class=codeSample>...
&lt;configuration&gt;
  &lt;configSections&gt;
&lt;section name="ConnectionManagerProviders" type="Microsoft.ApplicationBlocks.SmartClient.Offline.MultiProviderConfigHandler,Microsoft.ApplicationBlocks.SmartClient.Offline,Version=1.0.0.0,Culture=neutral,PublicKeyToken=null" /&gt;
  &lt;/configSections&gt;
&lt;/configuration&gt;
...
</PRE>
<P>以下配置文件摘录显示了 <B>ConnectionManagerProviders</B> 区段的详细内容：</P><PRE class=codeSample>...
&lt;ConnectionManagerProviders&gt;
  &lt;provider name="connectionDetectionStrategy" enabled="true" 
    type=
"Microsoft.ApplicationBlocks.SmartClient.Offline.WinINetDetectionStrategy, Microsoft.ApplicationBlocks.SmartClient.Offline.Providers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"&gt;
     &lt;pollingInterval&gt;2&lt;/pollingInterval&gt;
   &lt;/provider&gt;
  &lt;/ConnectionManagerProviders&gt;
...
</PRE>
<P>表 4.2 按照 ConnectionManagerProviders 特性在配置文件中的显示顺序说明它们的设置。</P>
<P><B>表</B><B>4.2：ConnectionManagerProviders 特性的默认设置</B></P>
<TABLE class=dataTable id=EBIAA cellSpacing=0 cellPadding=0>
<THEAD>
<TR class=stdHeader vAlign=top>
<TD id=colECBBIAA>特性/子元素</TD>
<TD id=colEBBBIAA>说明</TD>
<TD id=colEABBIAA style="BORDER-RIGHT: #cccccc 1px solid">默认值</TD></TR></THEAD>
<TBODY>
<TR class=record vAlign=top>
<TD>
<P class=lastInCell>name</P></TD>
<TD>
<P class=lastInCell>指定提供程序的名称。</P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>无</P></TD></TR>
<TR class=evenRecord vAlign=top>
<TD>
<P class=lastInCell>enabled</P></TD>
<TD>
<P class=lastInCell>指定启用配置文件中的哪个提供程序。可接受的值为 true 或 false。您只能将这些提供程序的其中一个设置为 true，以便脱机功能正常工作。</P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>false</P></TD></TR>
<TR class=record vAlign=top>
<TD>
<P class=lastInCell>type</P></TD>
<TD>
<P class=lastInCell>指定提供程序的完全限定名。</P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>无</P></TD></TR>
<TR class=evenRecord vAlign=top>
<TD>
<P class=lastInCell>pollingInterval</P></TD>
<TD>
<P class=lastInCell>指定“连接管理器”检测连接状态的频率。必须设置此特性。建议设置为 2 秒钟。</P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>无</P></TD></TR></TBODY></TABLE>
<DIV class=dataTableBottomMargin></DIV>
<P>Offline Application Block 提供了 <B>ConnectionDetectionStratergy</B>，后者使用 Windows API <B>WinINetDetectionStrategy</B> 类来检测与网络的连接性。 </P>
<DIV style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 10px"><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/4OffAppBlocConfSecuCons.mspx#top"><IMG height=9 alt=返回页首 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_up.gif" width=7 border=0></A><A class=topOfPage href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/4OffAppBlocConfSecuCons.mspx#top"><FONT color=#002c99>返回页首</FONT></A></DIV><A name=EHAA></A>
<H2>QueueManagerProviders</H2>
<P>下列提供程序可用于队列存储： </P>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>InMemoryQueueStorageProvider</B> – 该提供程序将队列数据存储在内存数据结构中。因为当应用程序关闭时存储的数据会丢失，所以该提供程序不适于存储持久性数据。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>MSDEQueueStorageProvider</B> – 该提供程序将队列数据存储在“桌面引擎”中。自定义属性 <B>Connection</B><B>String</B> 定义了用于连接到 MSDE 数据库的连接字符串。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>MSMQQueueStorageProvider</B> – 该提供程序将队列数据存储在“消息队列”中。该提供程序允许在配置文件中指定消息队列的名称，并且它将尝试打开具有该名称的队列。如果它无法打开该队列，就会尝试以给定的名称创建一个专用队列。在部署基于该块的应用程序的过程中，您应通过管理手段（使用 Microsoft 管理控制台 [MMC] 单元）手动创建消息队列，以便其安全权限能够正确配置。请注意，该块假设所使用的消息队列是专用队列。自定义属性 <B>queueName</B> 定义该队列的名称。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>IsolatedStorageQueueStorageProvider</B> – 该提供程序存储一个用户的队列数据，并防止其他用户对该数据进行访问。 </P></TD></TR></TBODY></TABLE>
<P>以下配置文件摘录显示了 <B>QueueManagerProviders</B> 区段是如何在 App.config 文件中定义的：</P><PRE class=codeSample>...
&lt;configuration&gt;
  &lt;configSections&gt;
&lt;section name="QueueManagerProviders" type="Microsoft.ApplicationBlocks.SmartClient.Offline.MultiProviderConfigHandler,Microsoft.ApplicationBlocks.SmartClient.Offline,Version=1.0.0.0,Culture=neutral,PublicKeyToken=null" /&gt;
   &lt;/configSections&gt;
&lt;/configuration&gt;
</PRE>
<P>以下配置文件摘录显示了 <B>QueueManagerProviders</B> 区段的详细内容： </P><PRE class=codeSample>...
   &lt;QueueManagerProviders&gt;
      &lt;provider name="queueManagerStorageProvider"
enabled="false"            type="Microsoft.ApplicationBlocks.SmartClient.Offline.IsolatedStorageQueueStorageProvider,Microsoft.ApplicationBlocks.SmartClient.Offline.Providers,Version=1.0.0.0,Culture=neutral,PublicKeyToken=null"
       encrypted="false"
       signed="false"
       cryptoProvider="default" /&gt;
      
&lt;provider name="queueManagerStorageProvider" 
enabled="true"             type="Microsoft.ApplicationBlocks.SmartClient.Offline.InMemoryQueueStorageProvider,Microsoft.ApplicationBlocks.SmartClient.Offline.Providers,Version=1.0.0.0,Culture=neutral,PublicKeyToken=null" 
       encrypted="false" 
       signed="false" 
       cryptoProvider="default"/&gt;
      
&lt;provider name="queueManagerStorageProvider" 
enabled="false"       type="Microsoft.ApplicationBlocks.SmartClient.Offline.MSMQQueueStorageProvider,Microsoft.ApplicationBlocks.SmartClient.Offline.Providers,Version=1.0.0.0,Culture=neutral,PublicKeyToken=null"
       encrypted="false" 
       signed="false" 
       cryptoProvider="default"&gt;
       &lt;queueName&gt;InsuranceClaimsClientQueue&lt;/queueName&gt; 
      &lt;/provider&gt;
      
&lt;provider name="queueManagerStorageProvider" 
        enabled="false" 
               type="Microsoft.ApplicationBlocks.SmartClient.Offline.MSDEQueueStorageProvider,Microsoft.ApplicationBlocks.SmartClient.Offline.Providers,Version=1.0.0.0,Culture=neutral,PublicKeyToken=null"
       encrypted="false" 
       signed="false" 
       cryptoProvider="default"&gt;
       &lt;applicationName&gt;Insurance Claims Client&lt;/applicationName&gt;
&lt;connectionString&gt;Initial Catalog=QueueDatabase;Data Source=[MsdeServerName]\[MsdeInstanceName];Integrated security=true&lt;/connectionString&gt;
      &lt;/provider&gt;
   &lt;/QueueManagerProviders&gt;
...
</PRE>
<P>表 4.3 按照 QueueManagerProviders 特性在配置文件中的显示顺序说明它们的设置。</P>
<P><B>表</B><B> 4.3：QueueManagerProviders 特性的默认设置</B></P>
<TABLE class=dataTable id=EBHAA cellSpacing=0 cellPadding=0>
<THEAD>
<TR class=stdHeader vAlign=top>
<TD id=colECBBHAA>特性/子元素</TD>
<TD id=colEBBBHAA>说明</TD>
<TD id=colEABBHAA style="BORDER-RIGHT: #cccccc 1px solid">默认值</TD></TR></THEAD>
<TBODY>
<TR class=record vAlign=top>
<TD>
<P class=lastInCell>Name</P></TD>
<TD>
<P class=lastInCell>指定提供程序的名称。</P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>无</P></TD></TR>
<TR class=evenRecord vAlign=top>
<TD>
<P class=lastInCell>enabled</P></TD>
<TD>
<P class=lastInCell>指定启用配置文件中的哪个提供程序。可接受的值为 true 或 false。您只能将这些提供程序的其中一个设置为 true，以便脱机功能正常工作。</P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>false</P></TD></TR>
<TR class=record vAlign=top>
<TD>
<P class=lastInCell>type</P></TD>
<TD>
<P class=lastInCell>指定提供程序的完全限定名。</P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>无</P></TD></TR>
<TR class=evenRecord vAlign=top>
<TD>
<P class=lastInCell>encrypted</P></TD>
<TD>
<P class=lastInCell>指定是否在写入数据前对其进行加密，以及是否在读取数据前对其进行解密。</P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>false</P></TD></TR>
<TR class=record vAlign=top>
<TD>
<P class=lastInCell>signed</P></TD>
<TD>
<P class=lastInCell>指定是否在写入数据前对其进行签名，以及是否在读取数据前对其进行验证。</P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>无</P></TD></TR>
<TR class=evenRecord vAlign=top>
<TD>
<P class=lastInCell>cryptoProvider</P></TD>
<TD>
<P class=lastInCell>设置提供程序的名称。在此例中，加密提供程序设置为 default。</P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>默认提供程序</P></TD></TR></TBODY></TABLE>
<DIV class=dataTableBottomMargin></DIV>
<P><B>cryptoProvider</B> 特性将在下面的“加密设置”部分中进行更全面地讨论。</P>
<DIV style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 10px"><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/4OffAppBlocConfSecuCons.mspx#top"><IMG height=9 alt=返回页首 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_up.gif" width=7 border=0></A><A class=topOfPage href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/4OffAppBlocConfSecuCons.mspx#top"><FONT color=#002c99>返回页首</FONT></A></DIV><A name=EGAA></A>
<H2>CryptographicSettings</H2>
<P>配置文件中队列存储提供程序的每个提供程序节点都具有一个与之关联的 <B>cryptoProvider</B> 特性，该特性指定要使用的 <B>cryptoProvider</B>。配置文件中还有另一个名为 <B>crytpographicSettings</B> 的区段，其中可以定义许多不同的加密提供程序，实现不同的加密算法和公钥。每个加密提供程序都由一个名称标识，并且每个 <B>QueueStorageProviders</B> 元素中的 <B>cryptoProvider</B> 特性标识要用于该 <B>QueueStorageProvider</B> 的加密提供程序。</P>
<P><B>要点</B> 经常更改密钥以防止恶意访问、或未经授权的用户获得对该密钥的访问权以及通向您网络的非法入口是非常重要的。</P>
<H3>生成自己的对称密钥 </H3>
<TABLE class=numberedList cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR vAlign=top>
<TD class=listNumber noWrap align=right>
<P>1.</P></TD>
<TD>
<P>在 Visual Studio .NET 2003 中打开 <B>ValidationKeyGeneration.sln</B>。该密钥包含在脱机解决方案和快速入门中，它位于 &lt;<I>安装位置</I>&gt;\Offline\QuickStarts\Utility\ValidationKeyGeneration。此外，您还可以按以下方式访问“Validation Key Generation”：依次单击 <B>Start</B>、<B>All Programs</B>、<B>Microsoft Application Blocks for .NET</B>、<B>Offline</B>，然后单击 <B>Validation Key Generation</B>。 </P></TD></TR>
<TR vAlign=top>
<TD class=listNumber noWrap align=right>
<P>2.</P></TD>
<TD>
<P>构建解决方案。 </P></TD></TR>
<TR vAlign=top>
<TD class=listNumber noWrap align=right>
<P>3.</P></TD>
<TD>
<P>运行 <B>ValidationKeyGeneration.exe</B>。 </P></TD></TR>
<TR vAlign=top>
<TD class=listNumber noWrap align=right>
<P>4.</P></TD>
<TD>
<P>单击 <B>Generate</B> 按钮。一个新密钥将出现在文本框中。 </P></TD></TR>
<TR vAlign=top>
<TD class=listNumber noWrap align=right>
<P>5.</P></TD>
<TD>
<P>从文本框中复制该密钥，并将其用于配置文件中。 </P></TD></TR></TBODY></TABLE>
<P>有关加密的详细信息，请参阅下列文章： </P>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><I>Cryptography</I>，位于 Microsoft Research Web 站点 (<A href="http://research.microsoft.com/crypto/" target=_blank><FONT color=#002c99>http://research.microsoft.com/crypto/</FONT></A>) </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><I>Cryptography and Secure Communications</I>，位于 TechNet Web 站点 (<A href="http://www.microsoft.com/technet/security/topics/crypto/default.mspx" target=_blank><FONT color=#002c99>http://www.microsoft.com/technet/security/topics/crypto/default.mspx</FONT></A>) </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><I>Cryptography</I> ，位于 MSDN Web 站点 (<A href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/security/security/cryptography_portal.asp" target=_blank><FONT color=#002c99>http://msdn.microsoft.com/library/default.asp?url=/library/en-us/security/security/cryptography_portal.asp</FONT></A>) </P></TD></TR></TBODY></TABLE>
<H3>使用具有加密功能的队列提供程序来协助确保数据的安全</H3>
<P>使用队列提供程序有助于您更安全地存储数据。以下配置文件摘录显示了 <B>CryptographicSettings</B> 区段是如何在 App.config 文件中定义的： </P><PRE class=codeSample>...
&lt;configuration&gt;
   &lt;configSections&gt;
&lt;section name="crytpographicSettings" type="Microsoft.ApplicationBlocks.Common.Crypto.CryptographicConfigurationHandler, Microsoft.ApplicationBlocks.Common,Version=1.0.0.0,Culture=neutral,PublicKeyToken=null" /&gt;...
   &lt;/configSections&gt;
&lt;/configuration&gt;
</PRE>
<P>以下配置文件摘录显示了 <B>QueueManagerProviders</B> 区段的详细内容：</P><PRE class=codeSample>...
    &lt;crytpographicSettings&gt;      
      &lt;cryptographicInfo name="default" 
                     type="Microsoft.ApplicationBlocks.Common.Crypto.DPAPIProvider, Microsoft.ApplicationBlocks.Common, Version=1.0.0.0 Culture=neutral, PublicKeyToken=null" 
validationKey= "Create a new validation key using the validation key generation utility and place here"
                validation="SHA1"/&gt;   
    &lt;/crytpographicSettings&gt;...
</PRE>
<P>有关配置文件及其设置的详细信息，请参阅 MSDN (<A href="http://msdn.microsoft.com/library/en-us/dnbda/html/cmab.asp" target=_blank><FONT color=#002c99>http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnbda/html/cmab.asp</FONT></A>) 上的“Configuration Management Application Block”。</P>
<P><B>注</B> 要获得完整的配置设置，请参阅保险理赔快速入门的 App.config 文件。 </P>
<DIV style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 10px"><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/4OffAppBlocConfSecuCons.mspx#top"><IMG height=9 alt=返回页首 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_up.gif" width=7 border=0></A><A class=topOfPage href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/4OffAppBlocConfSecuCons.mspx#top"><FONT color=#002c99>返回页首</FONT></A></DIV><A name=EFAA></A>
<H2>CacheManagerSettings</H2>
<P>Offline Application Block 使用四个缓存提供程序：<B>IsolatedStorageCacheStorage</B>（由 Offline Application Block 提供）、<B>SingletonCacheStorage</B>、<B>SQLServerCacheStorage</B>（由 MSDE 提供）和 <B>MmfCacheStorage</B>。</P>
<P>在 &lt;<B>CacheManagerSettings</B>&gt; 区段下的 &lt;<B>StorageInfo</B>&gt; 元素中，您必须指定独立存储提供程序的以下两个特性： </P>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>AssemblyName</B> – 程序集的名称，其中提供程序类的位置是：<B>Microsoft.ApplicationBlocks.Offline.Providers</B>。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>ClassName</B> – 类名，它对应的提供程序是：<B>Microsoft.ApplicationBlocks.Offline.Providers.IsolatedStorageCache</B>。 </P></TD></TR></TBODY></TABLE>
<P>以下配置文件摘录显示了 <B>CacheManagerSettings</B> 区段是如何在 App.config 文件中定义的：</P><PRE class=codeSample>...
&lt;configuration&gt;
   &lt;configSections&gt;
&lt;section name="CacheManagerSettings" type="Microsoft.ApplicationBlocks.Cache.CacheConfigurationHandler,
Microsoft.ApplicationBlocks.Cache,Version=1.0.0.0,Culture=neutral,
PublicKeyToken=
null" /&gt;...
   &lt;/configSections&gt;
&lt;/configuration&gt;
  
</PRE>
<P>以下配置文件摘录显示了 <B>CacheManagerSettings</B> 区段的详细内容：</P><PRE class=codeSample>...
&lt;CacheManagerSettings&gt;
&lt;DataProtectionInfo AssemblyName="Microsoft.ApplicationBlocks.Cache, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" ClassName="Microsoft.ApplicationBlocks.Cache.DataProtection.DefaultDataProtection"
ValidationKey="Create a new validation key using the validation key generation utility and place here"
Validation="SHA1" /&gt;

&lt;StorageInfo AssemblyName="Microsoft.ApplicationBlocks.SmartClient.Offline.Providers,
Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" ClassName="Microsoft.ApplicationBlocks.SmartClient.Offline.IsolatedStorageCacheStorage" Mode="InProc" Validated="false" Encrypted="false" /&gt;                &lt;ScavengingInfo AssemblyName="Microsoft.ApplicationBlocks.Cache, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" ClassName="Microsoft.ApplicationBlocks.Cache.Scavenging.LruScavenging"
 MemoryPollingPeriod="60" UtilizationForScavenging="80" 
MaximumSize="100" /&gt;
&lt;/CacheManagerSettings&gt;
...
</PRE>
<P>有关 <B>SingletonCacheStorage</B>、<B>SQLServerCacheStorage</B> (MSDE) 和 <B>MmfCacheStorage</B> 的详细信息，请参阅 <A href="http://msdn.microsoft.com/library/en-us/dnpag/html/CachingBlock.asp" target=_blank><FONT color=#002c99>http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnpag/html/CachingBlock.asp</FONT></A> 上的 Caching Application Block 文档。</P>
<DIV style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 10px"><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/4OffAppBlocConfSecuCons.mspx#top"><IMG height=9 alt=返回页首 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_up.gif" width=7 border=0></A><A class=topOfPage href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/4OffAppBlocConfSecuCons.mspx#top"><FONT color=#002c99>返回页首</FONT></A></DIV><A name=EEAA></A>
<H2>异常管理</H2>
<P>Offline Application Block 中的异常是使用 Exception Management Application Block (EMAB) 来记录的。事件记录在 Windows 事件日志中，它是 EMAB 记录所有日志事件的默认位置。如果要将事件记录到其他存储区（例如 SQL Server、消息队列或 Windows 管理规范 (WMI)），您可以使用 Logging Application Block，它提供了更可靠、更具可扩展性的模式进行记录。有关详细信息，请参阅 MSDN (<A href="http://msdn.microsoft.com/library/en-us/dnpag/html/logging.asp" target=_blank><FONT color=#002c99>http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnpag/html/Logging.asp?frame=true</FONT></A>) 上的 Logging Application Block。</P>
<DIV style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 10px"><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/4OffAppBlocConfSecuCons.mspx#top"><IMG height=9 alt=返回页首 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_up.gif" width=7 border=0></A><A class=topOfPage href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/4OffAppBlocConfSecuCons.mspx#top"><FONT color=#002c99>返回页首</FONT></A></DIV><A name=EDAA></A>
<H2>安全注意事项</H2>
<P>Offline Application Block 有许多项目需要进行保护，以防止未经授权的访问或恶意攻击。这些资产包括： </P>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>缓存的数据 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>排入队列的数据 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>用于访问缓存和队列的凭据 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>用户凭据 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>通过网络传输的用户数据 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>部署的程序集 </P></TD></TR></TBODY></TABLE>
<P>此外，运行智能客户端应用程序的系统及其使用的所有 Web 服务也很容易受到攻击。三种最常见的攻击类型是欺骗、引诱和重播。</P>
<H3>欺骗攻击</H3>
<P>欺骗攻击有几种形式。通常，攻击者会创建一个虚假状况来欺骗目标泄露敏感信息。欺骗攻击的一个示例是 IP 欺骗。当网络外的攻击者通过使用位于网络 IP 地址范围内的 IP 地址，或者受信任的外部 IP 地址（可以访问网络上的特定资源）来伪装成受信任用户时，就会发生这种攻击。此外，如果攻击者可以访问 IP 安全 (IPSec) 的安全参数，就可以伪装成经授权可以连接到公司网络的远程用户。欺骗攻击的其他示例包括：传输控制协议 (TCP) 欺骗 — 通过伪造的地址发送 Internet 数据包；域名服务器 (DNS) 欺骗 — 攻击者伪造有关哪些计算机名与哪些网络地址相对应的信息。</P>
<H3>引诱攻击</H3>
<P>引诱攻击是非法的权限提升。此类攻击可欺骗受信任的代码对攻击代码进行调用，之后此攻击代码将获得合法代码的权限。例如，您可能会通过电子邮件接收一个不信任的程序。由于您怀疑该程序的来源，您在计算机中添加了一个新的本地帐户，它具有普通、受限的用户权限而不是您自己的高级别权限。然后，使用二级登录服务在受限帐户下运行该程序。执行代码没有任何问题，因此您就认为它是合法的。</P>
<P>第二天，您收到很多电子邮件，抱怨您向他们发送了垃圾邮件。还有几个新文件添加到了 System32 目录中。这就是引诱攻击的结果，它是由您认为安全的程序导致的。当该程序启动时，它会检查是否运行在特权安全上下文中。如果发现它未处于特权安全上下文中，就认为实际用户是以更多权限进行登录的。通过调用 User32.dll 中的一些函数，它可模拟一个实际用户，欺骗 Windows 资源管理器启动新的程序副本。当新程序启动后，它就继承了 Windows 资源管理器标记的副本。由于用户是以较高权限登录的，因此该程序会继承这些权限，并可能危及文件、电子邮件以及操作系统的安全。</P>
<H3>重播攻击</H3>
<P>重播攻击是通过记录和重播先前发送的有效消息（或部分消息）来欺骗身份验证系统。可以记录任何固定的身份验证信息（例如密码），并在以后用于伪造受信任的消息。</P>
<H3>威胁和对策</H3>
<P>下表列出了针对 Offline Application Block 项目、运行智能客户端应用程序的系统，以及使用 Offline Application Block 的应用程序所使用的 Web 服务的可能安全威胁和相关的对策。有关威胁和对策的详细信息，请参阅 MSDN (<A href="http://msdn.microsoft.com/library/en-us/dnnetsec/html/ThreatCounter.asp" target=_blank><FONT color=#002c99>http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnnetsec/html/ThreatCounter.asp</FONT></A>) 上的 Improving Web Application Security: Threats and Countermeasures。</P>
<P><B>表</B><B> 4.4：安全威胁和对策</B></P>
<TABLE class=dataTable id=EAADAA cellSpacing=0 cellPadding=0>
<THEAD>
<TR class=stdHeader vAlign=top>
<TD id=colEEBAADAA>威胁</TD>
<TD id=colEDBAADAA>目标</TD>
<TD id=colECBAADAA>风险</TD>
<TD id=colEBBAADAA>攻击技术</TD>
<TD id=colEABAADAA style="BORDER-RIGHT: #cccccc 1px solid">对策</TD></TR></THEAD>
<TBODY>
<TR class=record vAlign=top>
<TD>
<P class=lastInCell>攻击者获得对缓存的访问权。</P></TD>
<TD>
<P class=lastInCell>缓存中的用户数据</P></TD>
<TD>
<P class=lastInCell>危及敏感用户数据的安全。</P></TD>
<TD>
<P class=lastInCell>攻击者直接绕过脱机块的基础结构，对缓存数据进行访问。</P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>加密数据并保护访问该缓存所需的凭据。</P></TD></TR>
<TR class=evenRecord vAlign=top>
<TD>
<P class=lastInCell>攻击者获得对队列的访问权，并将消息填充在其中。</P></TD>
<TD>
<P class=lastInCell>队列</P></TD>
<TD>
<P class=lastInCell>消息中可能会包含能够中断甚至终止服务的恶意数据。</P></TD>
<TD>
<P class=lastInCell>攻击者绕过脱机块的基础结构，直接填充队列。</P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>确保通过适当的凭据对队列的访问权进行限制，同时也保护该凭据，最好使用数据保护应用程序接口 (DPAPI)。</P></TD></TR>
<TR class=record vAlign=top>
<TD>
<P class=lastInCell>攻击者获得对用于访问队列和缓存的凭据的访问权。</P></TD>
<TD>
<P class=lastInCell>队列和缓存的凭据</P></TD>
<TD>
<P class=lastInCell>可能会危及凭据的安全。</P></TD>
<TD>
<P class=lastInCell>如果存储在配置中的数据未加密，则可用于特权用户。</P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>使用 DPAPI 或等效 API 来存储敏感信息。</P></TD></TR>
<TR class=evenRecord vAlign=top>
<TD>
<P class=lastInCell>攻击者获得对用户凭据的访问权。</P></TD>
<TD>
<P class=lastInCell>存储在客户端应用程序中的用户凭据</P></TD>
<TD>
<P class=lastInCell>当应用程序遇到错误并将错误记录到日志中，或者当计算机受到攻击时，可能会危及用户凭据的安全。</P></TD>
<TD>
<P class=lastInCell>计算机受到攻击后，用户凭据会作为错误报告的一部分存储在日志中，而特权用户可以对该日志进行访问。</P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>使用 .NET framework 中的 <B>CredentialCache</B> 类来存储凭据，即使在内存中也是如此。</P></TD></TR>
<TR class=record vAlign=top>
<TD>
<P class=lastInCell>攻击者欺骗业务功能服务。</P></TD>
<TD>
<P class=lastInCell>用户和应用程序数据</P></TD>
<TD>
<P class=lastInCell>可能会危及发送到应用程序的用户和应用程序数据的安全。</P></TD>
<TD>
<P class=lastInCell>网络探测</P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>验证为服务生成的代理具有有效终结点。</P></TD></TR>
<TR class=evenRecord vAlign=top>
<TD>
<P class=lastInCell>攻击者在数据传输到 Web 服务的过程中获得对数据的访问权。</P></TD>
<TD>
<P class=lastInCell>数据</P></TD>
<TD>
<P class=lastInCell>可能会危及数据的安全。</P></TD>
<TD>
<P class=lastInCell>网络探测</P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>使用安全传输（例如 SSL 或 IPSec），并在通过网络发送数据前对其进行加密。</P></TD></TR>
<TR class=record vAlign=top>
<TD>
<P class=lastInCell>攻击者欺骗下载程序集或部署恶意程序集。</P></TD>
<TD>
<P class=lastInCell>系统</P></TD>
<TD>
<P class=lastInCell>可能会危及运行该应用程序的系统的安全。</P></TD>
<TD>
<P class=lastInCell>可能的攻击包括欺骗程序集和部署服务器中的恶意程序集。</P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>要防止欺骗程序集，请使用受信任的第三方证书服务，如 VeriSign。要防止部署服务器上的恶意程序集，请加密并数字签名存储在那里的程序集，并在客户端下载时验证程序集。</P></TD></TR>
<TR class=evenRecord vAlign=top>
<TD>
<P class=lastInCell>攻击者在客户端上部署恶意程序集。</P></TD>
<TD>
<P class=lastInCell>系统和用户数据</P></TD>
<TD>
<P class=lastInCell>可能会危及用户数据和运行该应用程序的系统的安全。</P></TD>
<TD>
<P class=lastInCell>在客户端上部署恶意程序集。</P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>公开地应用到组成应用程序的所有程序集，并使用 <B>SecurityAction.FullDemand</B>，这样调用堆栈中的所有程序集都将验证公钥 <B>StrongNameIdentityPermission(SecurityAction.FullDemand, PublicKey=""</B>)。</P></TD></TR>
<TR class=record vAlign=top>
<TD>
<P class=lastInCell>攻击者访问非托管资源</P></TD>
<TD>
<P class=lastInCell>系统</P></TD>
<TD>
<P class=lastInCell>可能会危及运行该应用程序的系统的安全</P></TD>
<TD>
<P class=lastInCell>如果使用非托管资源的代码具有所需的权限，则可能会受到引诱攻击。</P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>要求 <B>SecurityPermission</B> 访问与非托管资源进行交互的类中的非托管资源。</P></TD></TR>
<TR class=evenRecord vAlign=top>
<TD>
<P class=lastInCell>攻击者使用反射来创建类实例。</P></TD>
<TD>
<P class=lastInCell>系统和用户数据</P></TD>
<TD>
<P class=lastInCell>特权代码可能会被恶意程序集运行，从而危及系统和用户数据的安全。</P></TD>
<TD>
<P class=lastInCell>引诱攻击</P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>要求在使用反射的所有代码上使用 <B>ReflectionPermission</B>。</P></TD></TR>
<TR class=record vAlign=top>
<TD>
<P class=lastInCell>攻击者使用重播攻击。</P></TD>
<TD>
<P class=lastInCell>提供业务功能的服务</P></TD>
<TD>
<P class=lastInCell>可能会危及业务功能的安全。</P></TD>
<TD>
<P class=lastInCell>在传输数据的过程中对其进行探测。</P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>使用安全传输（例如 SSL 或 IPSec），并对消息使用加密和数字签名。</P></TD></TR>
<TR class=evenRecord vAlign=top>
<TD>
<P class=lastInCell>攻击者获得对事件日志的访问权。</P></TD>
<TD>
<P class=lastInCell>系统和用户数据</P></TD>
<TD>
<P class=lastInCell>可能的风险包括：攻击者可能使用恶意日志消息终止系统；大量错误消息可能会屏蔽审核跟踪，因为日志太大而无法进行分析；根据日志的配置，可能会覆盖有效数据。</P></TD>
<TD>
<P class=lastInCell>使用错误记录的基础结构，攻击者可以将大量消息记录到日志中。</P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>可能的对策包括：在访问日志以执行读取或写入操作时要求正确的权限；对于 Windows 事件日志，请仅使用 <B>EventLogPermission</B> 来写入日志，除非组件同时支持读取和写入；不提供更新或删除日志的权限；将日志配置为在满载时发出错误，而不是覆盖它本身。</P></TD></TR>
<TR class=record vAlign=top>
<TD>
<P class=lastInCell>攻击者使用引诱攻击。</P></TD>
<TD>
<P class=lastInCell>系统和用户数据</P></TD>
<TD>
<P class=lastInCell>可能会危及系统和用户数据的安全。</P></TD>
<TD>
<P class=lastInCell>恶意程序集调用受信任的程序集来代表它执行操作。</P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>使用 <B>StrongNameIdentityPermission</B> 和 <B>SecurityAction.FullDemand</B> 来确保调用堆栈中的所有程序集都来自签名方。</P></TD></TR></TBODY></TABLE>
<DIV class=dataTableBottomMargin></DIV>
<DIV style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 10px"><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/4OffAppBlocConfSecuCons.mspx#top"><IMG height=9 alt=返回页首 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_up.gif" width=7 border=0></A><A class=topOfPage href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/4OffAppBlocConfSecuCons.mspx#top"><FONT color=#002c99>返回页首</FONT></A></DIV><A name=ECAA></A>
<H2>小结</H2>
<P>要部署 Offline Application Block，请配置您的提供程序并确定您要实施的安全措施。使用 App.config 文件来配置连接检测提供程序、队列管理器提供程序、加密设置以及缓存设置。检查本章中概述的安全注意事项，并确定适用于您环境的对策。最后，测试应用程序的部署，以确保它符合您环境的安全和性能要求。 </P>
<P><A href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnpag/html/offline-ch04.asp" target=_blank><FONT color=#002c99>转到原英文页面</FONT></A></P><img src ="http://www.blogjava.net/TrampEagle/aggbug/30153.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/TrampEagle/" target="_blank">TrampEagle</a> 2006-02-10 15:05 <a href="http://www.blogjava.net/TrampEagle/articles/30153.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>使用 Offline Application Block 进行开发</title><link>http://www.blogjava.net/TrampEagle/articles/30151.html</link><dc:creator>TrampEagle</dc:creator><author>TrampEagle</author><pubDate>Fri, 10 Feb 2006 07:04:00 GMT</pubDate><guid>http://www.blogjava.net/TrampEagle/articles/30151.html</guid><wfw:comment>http://www.blogjava.net/TrampEagle/comments/30151.html</wfw:comment><comments>http://www.blogjava.net/TrampEagle/articles/30151.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/TrampEagle/comments/commentRss/30151.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/TrampEagle/services/trackbacks/30151.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 原文引自：http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter1Introduction.mspx使用 Offline Application Block 进行开发发布日期： 8/19/2004 | 更新日期： 8/19/...&nbsp;&nbsp;<a href='http://www.blogjava.net/TrampEagle/articles/30151.html'>阅读全文</a><img src ="http://www.blogjava.net/TrampEagle/aggbug/30151.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/TrampEagle/" target="_blank">TrampEagle</a> 2006-02-10 15:04 <a href="http://www.blogjava.net/TrampEagle/articles/30151.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Offline Application Block 的设计</title><link>http://www.blogjava.net/TrampEagle/articles/30149.html</link><dc:creator>TrampEagle</dc:creator><author>TrampEagle</author><pubDate>Fri, 10 Feb 2006 07:02:00 GMT</pubDate><guid>http://www.blogjava.net/TrampEagle/articles/30149.html</guid><wfw:comment>http://www.blogjava.net/TrampEagle/comments/30149.html</wfw:comment><comments>http://www.blogjava.net/TrampEagle/articles/30149.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/TrampEagle/comments/commentRss/30149.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/TrampEagle/services/trackbacks/30149.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 原文引自：http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter1Introduction.mspxOffline Application Block 的设计发布日期： 8/6/2004 | 更新日期： 8/6/2004...&nbsp;&nbsp;<a href='http://www.blogjava.net/TrampEagle/articles/30149.html'>阅读全文</a><img src ="http://www.blogjava.net/TrampEagle/aggbug/30149.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/TrampEagle/" target="_blank">TrampEagle</a> 2006-02-10 15:02 <a href="http://www.blogjava.net/TrampEagle/articles/30149.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>智能客户端 Offline Application Block</title><link>http://www.blogjava.net/TrampEagle/articles/30148.html</link><dc:creator>TrampEagle</dc:creator><author>TrampEagle</author><pubDate>Fri, 10 Feb 2006 07:00:00 GMT</pubDate><guid>http://www.blogjava.net/TrampEagle/articles/30148.html</guid><wfw:comment>http://www.blogjava.net/TrampEagle/comments/30148.html</wfw:comment><comments>http://www.blogjava.net/TrampEagle/articles/30148.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/TrampEagle/comments/commentRss/30148.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/TrampEagle/services/trackbacks/30148.html</trackback:ping><description><![CDATA[原文引自：<A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter1Introduction.mspx">http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter1Introduction.mspx</A><BR>
<H1>智能客户端 Offline Application Block</H1>
<H2 class=subtitle></H2>
<DIV class=date>发布日期： 8/20/2004<SPAN class=datePipe> | </SPAN>更新日期： 8/20/2004</DIV>
<DIV style="HEIGHT: 18px"></DIV>
<DIV style="WIDTH: 250px"><IMG height=68 alt="" src="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/art/pponline.gif" width=250 border=0><BR>
<P class=figureCaption></P>
<DIV class=figureRule></DIV></DIV>
<P><A href="http://www.microsoft.com/resources/practices/completelist.asp" target=_blank><FONT color=#002c99>patterns &amp; practices Library</FONT></A></P>
<P><A href="http://shop.microsoft.com/practices" target=_blank><FONT color=#002c99>Patterns and Practices 联机图书商店</FONT></A></P>
<P>Naveen Yajaman、Edward Jezierski、Brenton Webster、David Hill、Mohammad Al-Sabt (Microsoft Corporation)；Brian Button (Murphy and Associates)；Prashant Bansode、Guru Sundaram (Infosys Technologies Ltd)；Bharat Rawal (Volt)；Blaine Wastell (Ascentium Corporation)；Roberta Leibovitz (Modeled Computation LLC)</P>
<P><B>摘要</B> 本页简要介绍 Offline Application Block，它主要是为开发人员提供的体系结构模型。这些开发人员希望在他们的智能客户端应用程序中增添脱机功能。该应用程序块讲述如何： </P>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>检测网络连通性的有无。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>缓存需要的数据，即使当网络连接不可用时，也可以使应用程序能继续工作。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>当网络连接变为可用时，同步客户端应用程序状态和（或）服务器的数据。 </P></TD></TR></TBODY></TABLE>
<P><B>下载</B></P>
<P>请在 <A href="http://www.microsoft.com/downloads/details.aspx?FamilyId=BD864EB5-56B3-43A5-A964-6F23566DF0AB&amp;displaylang=en" target=_blank><FONT color=#002c99>Microsoft.com Download Center</FONT></A> 下载 Offline Application Block。</P>
<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/architecture/architecture/architecturetopic/SCOfflineAppBlockcover.mspx#EGAA"><IMG height=9 alt=本指南面向的读者 hspace=4 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_down.gif" width=7 vspace=2 border=0></A></TD>
<TD class=onThisPage><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCOfflineAppBlockcover.mspx#EGAA"><FONT color=#002c99>本指南面向的读者</FONT></A></TD></TR>
<TR vAlign=top>
<TD><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCOfflineAppBlockcover.mspx#EFAA"><FONT color=#002c99><IMG height=9 alt=本指南的内容 hspace=4 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_down.gif" width=7 vspace=2 border=0></FONT></A></TD>
<TD class=onThisPage><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCOfflineAppBlockcover.mspx#EFAA"><FONT color=#002c99>本指南的内容</FONT></A></TD></TR>
<TR vAlign=top>
<TD><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCOfflineAppBlockcover.mspx#EEAA"><FONT color=#002c99><IMG height=9 alt=社区 hspace=4 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_down.gif" width=7 vspace=2 border=0></FONT></A></TD>
<TD class=onThisPage><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCOfflineAppBlockcover.mspx#EEAA"><FONT color=#002c99>社区</FONT></A></TD></TR>
<TR vAlign=top>
<TD><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCOfflineAppBlockcover.mspx#EDAA"><FONT color=#002c99><IMG height=9 alt=反馈和支持 hspace=4 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_down.gif" width=7 vspace=2 border=0></FONT></A></TD>
<TD class=onThisPage><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCOfflineAppBlockcover.mspx#EDAA"><FONT color=#002c99>反馈和支持</FONT></A></TD></TR>
<TR vAlign=top>
<TD><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCOfflineAppBlockcover.mspx#ECAA"><FONT color=#002c99><IMG height=9 alt=参与人员 hspace=4 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_down.gif" width=7 vspace=2 border=0></FONT></A></TD>
<TD class=onThisPage><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCOfflineAppBlockcover.mspx#ECAA"><FONT color=#002c99>参与人员</FONT></A></TD></TR></TBODY></TABLE><A name=EGAA></A>
<H2>本指南面向的读者</H2>
<P>本指南服务的对象是： </P>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>软件开发人员 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>应用程序架构师 </P></TD></TR></TBODY></TABLE>
<DIV style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 10px"><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCOfflineAppBlockcover.mspx#top"><IMG height=9 alt=返回页首 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_up.gif" width=7 border=0></A><A class=topOfPage href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCOfflineAppBlockcover.mspx#top"><FONT color=#002c99>返回页首</FONT></A></DIV><A name=EFAA></A>
<H2>本指南的内容</H2>
<P>本指南讲解 Offline Application Block 的设计与特性，并且示范如何使用应用程序块在智能客户端应用程序中增添脱机功能。</P>
<P><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/Scdnpagoffline_ch01.mspx"><FONT color=#002c99>第 1 章：简介</FONT></A></P>
<P>第 1 章介绍 Offline Application Block，并站在高层次上对该体系结构进行说明。此外，本章还包括了一个客户方案，它将在第 2 章中进行详述。</P>
<P><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/Scdnpagoffline2des_ch01.mspx"><FONT color=#002c99>第 2 章：Offline Application Block 的设计</FONT></A></P>
<P>第 2 章验证 Offline Application Block 的特性、体系结构和设计，以及与支持它的其他组件（例如，Caching Application Block）的关系。 </P>
<P><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/3DevUsOfflineAppBloc.mspx"><FONT color=#002c99>第 3 章：使用 Offline Application Block 进行开发</FONT></A></P>
<P>在完全理解 Offline Application Block 的特性后，您就可以开始用它进行部署。第 3 章详述构建应用程序块以及要使用该块需要对应用程序进行修改的过程。本章还论述 QuickStarts，利用它您可以了解需要实现自己的脱机解决方案的每一个过程。</P>
<P><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/4OffAppBlocConfSecuCons.mspx"><FONT color=#002c99>第 4 章：Offline Application Block 配置和安全注意事项</FONT></A></P>
<P>创建应用程序之后，您必须对其进行配置、安全保护和部署。第 4 章讲述使用应用程序配置文件以及部署应用程序的每个过程。此外，本章还论述可能存在的安全威胁以及您可以抵御这些威胁的措施。 </P>
<P><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/AppendATestCases.mspx"><FONT color=#002c99>附录 A：测试用例</FONT></A></P>
<P>本附录文档中的测试用例，用于验证 Offline Application Block 运行的正确与否。</P>
<P><B>自述文件</B></P>
<P>ReadMe.txt 文件是一个单独的文档，它包括疑难解答提示以及有关一些已知问题的信息。要阅读，单击“开始”，单击“所有程序”，单击“Microsoft Application Blocks for .NET”，单击“Offline”然后单击“ReadMe”。</P>
<DIV style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 10px"><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCOfflineAppBlockcover.mspx#top"><IMG height=9 alt=返回页首 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_up.gif" width=7 border=0></A><A class=topOfPage href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCOfflineAppBlockcover.mspx#top"><FONT color=#002c99>返回页首</FONT></A></DIV><A name=EEAA></A>
<H2>社区</H2>
<P>本指南与许多 patterns &amp; practices 标题一样，与新闻组关联以便提供社区支持。要使用该新闻组进行提问、发送反馈或者与其他用户进行联系以分享好的想法，请访问 <A href="http://workspaces.gotdotnet.com/pnpsc" target=_blank><FONT color=#002c99>http://workspaces.gotdotnet.com/pnpsc</FONT></A>。</P>
<DIV style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 10px"><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCOfflineAppBlockcover.mspx#top"><IMG height=9 alt=返回页首 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_up.gif" width=7 border=0></A><A class=topOfPage href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCOfflineAppBlockcover.mspx#top"><FONT color=#002c99>返回页首</FONT></A></DIV><A name=EDAA></A>
<H2>反馈和支持</H2>
<P>您是否有任何问题、意见？和建议？要对本指南发送反馈，请发送电子邮件至 <A href="mailto:devfdbck@microsoft.com" target=_blank><FONT color=#002c99>devfdbck@microsoft.com</FONT></A>。 </P>
<P>Offline Application Block 设计用于帮助您在自己的智能应用程序中增添脱机功能。在部署自己的解决方案时，示例代码的确能够承担起一个体系结构模型的作用，引导您深入使用。示例代码以源代码的形式提供，您可以原样使用，也可以针对自己的应用程序进行自定义。通过 Microsoft 产品支持可以获得收费支持。</P>
<DIV style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 10px"><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCOfflineAppBlockcover.mspx#top"><IMG height=9 alt=返回页首 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_up.gif" width=7 border=0></A><A class=topOfPage href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCOfflineAppBlockcover.mspx#top"><FONT color=#002c99>返回页首</FONT></A></DIV><A name=ECAA></A>
<H2>参与人员</H2>
<P>非常感谢以下这些顾问人员，他们提供了很有价值的帮助： </P>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>Maarten Mullender，Microsoft Corporation </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>Mark Bolter，Microsoft Corporation </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>Scott Densmore，Microsoft Corporation </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>Tim Osborn，Ascentium Corporation </P></TD></TR></TBODY></TABLE>
<P>感谢在编写和发布过程中，对我们的工作给予帮助的人们： </P>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>Carlos Farre，Solutions IQ </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>Matthew Evans，Microsoft </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>Sharon Smith，Microsoft </P></TD></TR></TBODY></TABLE>
<P><A href="http://msdn.microsoft.com/library/en-us/dnpag/html/offline.asp?frame=true" target=_blank><FONT color=#002c99>转到原英文页面</FONT></A></P><img src ="http://www.blogjava.net/TrampEagle/aggbug/30148.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/TrampEagle/" target="_blank">TrampEagle</a> 2006-02-10 15:00 <a href="http://www.blogjava.net/TrampEagle/articles/30148.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>智能客户端体系结构与设计指南 第 8 章 — 智能客户端应用程序性能</title><link>http://www.blogjava.net/TrampEagle/articles/30147.html</link><dc:creator>TrampEagle</dc:creator><author>TrampEagle</author><pubDate>Fri, 10 Feb 2006 06:58:00 GMT</pubDate><guid>http://www.blogjava.net/TrampEagle/articles/30147.html</guid><wfw:comment>http://www.blogjava.net/TrampEagle/comments/30147.html</wfw:comment><comments>http://www.blogjava.net/TrampEagle/articles/30147.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/TrampEagle/comments/commentRss/30147.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/TrampEagle/services/trackbacks/30147.html</trackback:ping><description><![CDATA[原文引自：<A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter1Introduction.mspx">http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter1Introduction.mspx</A><BR><BR>
<H1>第 8 章 — 智能客户端应用程序性能</H1>
<H2 class=subtitle></H2>
<DIV class=date>发布日期： 08/20/2004<SPAN class=datePipe> | </SPAN>更新日期： 08/20/2004</DIV>
<DIV class=overview>
<DIV style="WIDTH: 250px"><IMG height=68 alt=pponline src="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/art/pponline.gif" width=250 border=0><BR>
<P class=figureCaption><A href="http://www.microsoft.com/resources/practices/default.mspx" target=_blank></A></P>
<DIV class=figureRule></DIV></DIV>
<P><B>智能客户端体系结构与设计指南</B></P>
<P>David Hill、Brenton Webster、Edward A. Jezierski、Srinath Vasireddy、Mohammad Al-Sabt，Microsoft Corporation，Blaine Wastell Ascentium Corporation，Jonathan Rasmusson 和 Paul Gale ThoughtWorks 和 Paul Slater Wadeware LLC</P>
<P><B>相关链接</B></P>
<P>Microsoft® <I>patterns &amp; practices</I> 库 <A href="http://www.microsoft.com/resources/practices/default.mspx" target=_blank><FONT color=#002c99>http://www.microsoft.com/resources/practices/default.mspx</FONT></A></P>
<P><I>.NET </I><I>的应用程序体系结构：设计应用程序和服务</I><A href="http://msdn.microsoft.com/library/en-us/dnbda/html/distapp.asp" target=_blank><FONT color=#002c99>http://msdn.microsoft.com/library/en-us/dnbda/html/distapp.asp</FONT></A></P>
<P><B>摘要：</B>本章讨论如何优化您的智能客户端应用程序。本章分析您可以在设计时采取的步骤，并介绍如何调整智能客户端应用程序以及诊断任何性能问题。</P></DIV>
<CENTER><IMG title="" height=6 alt=* src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/3squares.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/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter8SCAppPerf.mspx#EFAA"><IMG height=9 alt=针对性能进行设计 hspace=4 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_down.gif" width=7 vspace=2 border=0></A></TD>
<TD class=onThisPage><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter8SCAppPerf.mspx#EFAA"><FONT color=#002c99>针对性能进行设计</FONT></A></TD></TR>
<TR vAlign=top>
<TD><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter8SCAppPerf.mspx#EEAA"><FONT color=#002c99><IMG height=9 alt=性能调整和诊断 hspace=4 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_down.gif" width=7 vspace=2 border=0></FONT></A></TD>
<TD class=onThisPage><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter8SCAppPerf.mspx#EEAA"><FONT color=#002c99>性能调整和诊断</FONT></A></TD></TR>
<TR vAlign=top>
<TD><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter8SCAppPerf.mspx#EDAA"><FONT color=#002c99><IMG height=9 alt=小结 hspace=4 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_down.gif" width=7 vspace=2 border=0></FONT></A></TD>
<TD class=onThisPage><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter8SCAppPerf.mspx#EDAA"><FONT color=#002c99>小结</FONT></A></TD></TR>
<TR vAlign=top>
<TD><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter8SCAppPerf.mspx#ECAA"><FONT color=#002c99><IMG height=9 alt=参考资料 hspace=4 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_down.gif" width=7 vspace=2 border=0></FONT></A></TD>
<TD class=onThisPage><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter8SCAppPerf.mspx#ECAA"><FONT color=#002c99>参考资料</FONT></A></TD></TR></TBODY></TABLE>
<P>智能客户端应用程序可以提供比 Web 应用程序更丰富和响应速度更快的用户界面，并且可以利用本地系统资源。如果应用程序的大部分驻留在用户的计算机上，则应用程序不需要到 Web 服务器的持续的往返行程。这有利于提高性能和响应性。然而，要实现智能客户端应用程序的全部潜能，您应该在应用程序的设计阶段仔细考虑性能问题。通过在规划和设计您的应用程序时解决性能问题，可以帮助您及早控制成本，并减小以后陷入性能问题的可能性。</P>
<P><B>注</B>改善智能客户端应用程序的性能并不仅限于应用程序设计问题。您可以在整个应用程序生存期中采取许多个步骤来使 .NET 代码具有更高的性能。虽然 .NET 公共语言运行库 (CLR) 在执行代码方面非常有效，但您可以使用多种技术来提高代码的性能，并防止在代码级引入性能问题。有关这些问题的详细信息，请参阅<B></B><A href="http://msdn.microsoft.com/perf" target=_blank><FONT color=#002c99>http://msdn.microsoft.com/perf</FONT></A>。</P>
<P>在应用程序的设计中定义现实的性能要求并识别潜在的问题显然是重要的，但是性能问题通常只在编写代码之后对其进行测试时出现。在这种情况下，您可以使用一些工具和技术来跟踪性能问题。</P>
<P>本章分析如何设计和调整您的智能客户端应用程序以获得最佳性能。它讨论了许多设计和体系结构问题（包括线程处理和缓存注意事项），并且分析了如何增强应用程序的 Windows 窗体部分的性能。本章还介绍了您可以用来跟踪和诊断智能客户端应用程序性能问题的一些技术和工具。</P><A name=EFAA></A>
<H2>针对性能进行设计</H2>
<P>您可以在应用程序设计或体系结构级完成许多工作，以确保智能客户端应用程序具有良好的性能。您应该确保在设计阶段尽可能早地制定现实的且可度量的性能目标，以便评估设计折衷，并且提供最划算的方法来解决性能问题。只要可能，性能目标就应该基于实际的用户和业务要求，因为这些要求受到应用程序所处的操作环境的强烈影响。性能建模是一种结构化的且可重复的过程，您可以使用该过程来管理您的应用程序并确保其实现性能目标。有关详细信息，请参阅 <I>Improving .NET Application Performance and Scalability</I> 中的第 2 章“Performance Modeling”，网址为：<A href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnpag/html/scalenetchapt02.asp" target=_blank><FONT color=#002c99>http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnpag/html/scalenetchapt02.asp</FONT></A>。</P>
<P>智能客户端通常是较大的分布式应用程序的组成部分。很重要的一点是在完整应用程序的上下文中考虑智能客户端应用程序的性能，包括该客户端应用程序使用的所有位于网络中的资源。微调并优化应用程序中的每一个组件通常是不必要或不可能的。相反，性能调整应该基于优先级、时间、预算约束和风险。一味地追求高性能通常并不是一种划算的策略。</P>
<P>智能客户端还将需要与用户计算机上的其他应用程序共存。当您设计智能客户端应用程序时，您应该考虑到您的应用程序将需要与客户端计算机上的其他应用程序共享系统资源，例如，内存、CPU 时间和网络利用率。</P>
<P><B>注</B>有关设计可伸缩的高性能远程服务的信息，请参阅<B></B><I>Improving .NET Performance and Scalability</I>，网址为：<A href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnpag/html/scalenet.asp" target=_blank><FONT color=#002c99>http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnpag/html/scalenet.asp</FONT></A>。本指南包含有关如何优化 .NET 代码以获得最佳性能的详细信息。</P>
<P>要设计高性能的智能客户端，请考虑下列事项： </P>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>在适当的位置缓存数据</B>。数据缓存可以显著改善智能客户端应用程序的性能，使您可以在本地使用数据，而不必经常从网络检索数据。但是，敏感数据或频繁更改的数据通常不适合进行缓存。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>优化网络通讯</B>。如果通过“健谈的”接口与远程层服务进行通讯，并且借助于多个请求/响应往返行程来执行单个逻辑操作，则可能消耗系统和网络资源，从而导致低劣的应用程序性能。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>有效地使用线程</B>。如果您使用用户界面 (UI) 线程执行阻塞 I/O 绑定调用，则 UI 似乎不对用户作出响应。因为创建和关闭线程需要系统开销，所以创建大量不必要的线程可能导致低劣的性能。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>有效地使用事务</B>。如果客户端具有本地数据，则使用原子事务可帮助您确保该数据是一致的。因为数据是本地的，所以事务也是本地的而不是分布式的。对于脱机工作的智能客户端而言，对本地数据进行的任何更改都是暂时的。客户端在重新联机时需要同步更改。对于非本地数据而言，在某些情况下可以使用分布式事务（例如，当服务位于具有良好连接性的同一物理位置并且服务支持它时）。诸如 Web 服务和消息队列之类的服务不支持分布式事务。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>优化应用程序启动时间</B>。较短的应用程序启动时间使用户可以更为迅速地开始与应用程序交互，从而使用户立刻对应用程序的性能和可用性产生好感。应该对您的应用程序进行适当的设计，以便在应用程序启动时仅加载那些必需的程序集。因为加载每个程序集都会引起性能开销，所以请避免使用大量程序集。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>有效地管理可用资源</B>。低劣的设计决策（例如，实现不必要的完成器，未能在 <B>Dispose</B> 方法中取消终止，或者未能释放非托管资源）可能导致在回收资源时发生不必要的延迟，并且可能造成使应用程序性能降低的资源泄漏。如果应用程序未能正确地释放资源，或者应用程序显式强制进行垃圾回收，则可能会妨碍 CLR 有效地管理内存。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>优化</B><B> Windows </B><B>窗体性能</B>。智能客户端应用程序依靠 Windows 窗体来提供内容丰富且响应迅速的用户界面.您可以使用多种技术来确保 Windows 窗体提供最佳性能。这些技术包括降低用户界面的复杂性，以及避免同时加载大量数据。 </P></TD></TR></TBODY></TABLE>
<P>在许多情况下，从用户角度感受到的应用程序性能起码与应用程序的实际性能同样重要。您可以通过对设计进行某些特定的更改来创建在用户看来性能高得多的应用程序，例如：使用后台异步处理（以使 UI 能作出响应）；显示进度栏以指示任务的进度；提供相应的选项以便用户取消长期运行的任务。</P>
<P>本节将专门详细讨论这些问题。</P>
<H3>数据缓存原则</H3>
<P>缓存是一种能够改善应用程序性能并提供响应迅速的用户界面的重要技术。您应该考虑下列选项： </P>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>缓存频繁检索的数据以减少往返行程</B>。如果您的应用程序必须频繁地与网络服务交互以检索数据，则应该考虑在客户端缓存数据，从而减少通过网络重复获取数据的需要。这可以极大地提高性能，提供对数据的近乎即时的访问，并且消除了可能对智能客户端应用程序性能造成不利影响的网络延迟和中断风险。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>缓存只读引用数据</B>。只读引用数据通常是理想的缓存对象。此类数据用于提供进行验证和用户界面显示所需的数据，例如，产品说明、ID 等等。因为客户端无法更改此类数据，所以通常可以在客户端缓存它而无须进行任何进一步的特殊处理。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>缓存要发送给位于网络上的服务的数据</B>。您应该考虑缓存要发送给位于网络上的服务的数据。例如，如果您的应用程序允许用户输入由在多个窗体中收集的一些离散数据项组成的定单信息，则请考虑允许用户输入全部数据，然后在输入过程的结尾在一个网络调用中发送定单信息。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>尽量少地缓存高度不稳定的数据</B>。在缓存任何不稳定的数据之前，您需要考虑在其变得陈旧或者由于其他原因变得不可用之前，能够将其缓存多长时间。如果数据高度不稳定并且您的应用程序依赖于最新信息，则或许只能将数据缓存很短一段时间（如果可以缓存）。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>尽量少地缓存敏感数据</B>。您应该避免在客户端上缓存敏感数据，因为在大多数情况下，您无法保证客户端的物理安全。但是，如果您必须在客户端上缓存敏感数据，则您通常将需要加密数据，该操作本身也会影响性能。 </P></TD></TR></TBODY></TABLE>
<P>有关数据缓存的其他问题的详细信息，请参阅本指南的<A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter2HandlingData.mspx"><FONT color=#002c99>第 2 章</FONT></A>。另请参阅 <I>Improving .NET Application Performance and Scalability</I> 的第 3 章“Design Guidelines for Application Performance”(<A href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnpag/html/scalenetchapt03.asp" target=_blank><FONT color=#002c99>http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnpag/html/scalenetchapt03.asp</FONT></A>) 的“Caching”一节以及 <I>Improving .NET Application Performance and Scalability</I> 的第 4 章“Architecture and Design Review of .NET Application for Performance and Scalability”(<A href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnpag/html/scalenetchapt04.asp" target=_blank><FONT color=#002c99>http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnpag/html/scalenetchapt04.asp</FONT></A>)。</P>
<H3>网络通讯原则</H3>
<P>您将面临的另一个决策是如何设计和使用网络服务，例如，Web 服务。特别地，您应该考虑与网络服务交互的粒度、同步性和频率。要获得最佳的性能和可伸缩性，您应该在单个调用中发送更多的数据，而不是在多个调用中发送较少量的数据。例如，如果您的应用程序允许用户在定单中输入多个项，则较好的做法是为所有项收集数据，然后将完成的采购定单一次性发送给服务，而不是在多个调用中发送单个项的详细信息。除了降低与进行大量网络调用相关联的系统开销以外，这还可以减少服务和/或客户端内的复杂状态管理的需要。</P>
<P>应该将您的智能客户端应用程序设计为尽可能地使用异步通讯，因为这将有助于使用户界面快速响应以及并行执行任务。有关如何使用 <B>BeginInvoke</B> 和 <B>EndInvoke</B> 方法异步启动调用和检索数据的详细信息，请参阅“Asynchronous Programming Overview”(<A href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/cpovrasynchronousprogrammingoverview.asp" target=_blank><FONT color=#002c99>http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/cpovrasynchronousprogrammingoverview.asp</FONT></A>)。</P>
<P><B>注</B>有关设计和构建偶尔连接到网络的智能客户端应用程序的详细信息，请参阅<A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter3GettingConnected.mspx"><FONT color=#002c99>第 3 章“建立连接”</FONT></A>和<A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter4OccConnSC.mspx"><FONT color=#002c99>第 4 章“偶尔连接的智能客户端”</FONT></A>。</P>
<H3>线程处理原则</H3>
<P>在应用程序内使用多个线程可能是一种提高其响应性和性能的好方法。特别地，您应该考虑使用线程来执行可以在后台安全地完成且不需要用户交互的处理。通过在后台执行此类工作，可以使用户能够继续使用应用程序，并且使应用程序的主用户界面线程能够维持应用程序的响应性。</P>
<P>适合于在单独的线程上完成的处理包括： </P>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>应用程序初始化</B>。请在后台线程上执行漫长的初始化，以便用户能够尽快地与您的应用程序交互，尤其是在应用程序功能的重要或主要部分并不依赖于该初始化完成时。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>远程服务调用</B>。请在单独的后台线程上通过网络进行所有远程调用。很难（如果不是无法）保证位于网络上的服务的响应时间。在单独的线程上执行这些调用可以减少发生网络中断或延迟的风险，从而避免对应用程序性能造成不利影响。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>IO </B><B>绑定处理</B>。应该在单独的线程上完成诸如在磁盘上搜索和排序数据之类的处理。通常，这种工作要受到磁盘 I/O 子系统而不是处理器可用性的限制，因此当该工作在后台执行时，您的应用程序可以有效地维持其响应性。 </P></TD></TR></TBODY></TABLE>
<P>尽管使用多个线程的性能好处可能很显著，但需要注意，线程使用它们自己的资源，并且使用太多的线程可能给处理器（它需要管理线程之间的上下文切换）造成负担。要避免这一点，请考虑使用线程池，而不是创建和管理您自己的线程。线程池将为您有效地管理线程，重新使用现有的线程对象，并且尽可能地减小与线程创建和处置相关联的系统开销。</P>
<P>如果用户体验受到后台线程所执行的工作的影响，则您应该总是让用户了解工作的进度。以这种方式提供反馈可以增强用户对您的应用程序的性能的感觉，并且防止他或她假设没有任何事情发生。请努力确保用户可以随时取消漫长的操作。</P>
<P>您还应该考虑使用 <B>Application</B> 对象的 <B>Idle</B> 事件来执行简单的操作。<B>Idle</B> 事件提供了使用单独的线程来进行后台处理的简单替代方案。当应用程序不再有其他用户界面消息需要处理并且将要进入空闲状态时，该事件将激发。您可以通过该事件执行简单的操作，并且利用用户不活动的情况。例如：</P>
<P><B>[C#]</B></P><PRE class=codeSample>public Form1() 
{ 
InitializeComponent(); 
Application.Idle += new EventHandler( OnApplicationIdle ); 
} 
private void OnApplicationIdle( object sender, EventArgs e ) 
{ 
} 
</PRE>
<P><B>[Visual Basic .NET]</B></P><PRE class=codeSample>Public Class Form1 
    Inherits System.Windows.Forms.Form 
    Public Sub New() 
        MyBase.New() 
        InitializeComponent() 
        AddHandler Application.Idle, AddressOf OnApplicationIdle 
    End Sub 
    Private Sub OnApplicationIdle(ByVal sender As System.Object, ByVal e As System.EventArgs) 
    End Sub 
End Class 
</PRE>
<P><B>注</B>有关在智能客户端中使用多个线程的详细信息，请参阅<A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter6UsingMultThr.mspx"><FONT color=#002c99>第 6 章“使用多个线程”</FONT></A>。</P>
<H3>事务原则</H3>
<P>事务可以提供重要的支持，以确保不会违反业务规则并维护数据一致性。事务可以确保一组相关任务作为一个单元成功或失败。您可以使用事务来维护本地数据库和其他资源（包括消息队列的队列）之间的一致性。</P>
<P>对于需要在网络连接不可用时使用脱机缓存数据的智能客户端应用程序，您应该将事务性数据排队，并且在网络连接可用时将其与服务器进行同步。</P>
<P>您应该避免使用涉及到位于网络上的资源的分布式事务，因为这些情况可能导致与不断变化的网络和资源响应时间有关的性能问题。如果您的应用程序需要在事务中涉及到位于网络上的资源，则应该考虑使用补偿事务，以便使您的应用程序能够在本地事务失败时取消以前的请求。尽管补偿事务在某些情况下可能不适用，但它们使您的应用程序能够按照松耦合方式在事务的上下文内与网络资源交互，从而减少了不在本地计算机控制之下的资源对应用程序的性能造成不利影响的可能性。</P>
<P><B>注</B>有关在智能客户端中使用事务的详细信息，请参阅<A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter3GettingConnected.mspx"><FONT color=#002c99>第 3 章“建立连接”</FONT></A>。</P>
<H3>优化应用程序启动时间</H3>
<P>快速的应用程序启动时间几乎可以使用户立即开始与应用程序交互，从而使用户立刻对应用程序的性能和可用性产生好感。</P>
<P>当应用程序启动时，首先加载 CLR，再加载应用程序的主程序集，随后加载为解析从应用程序的主窗体中引用的对象的类型所需要的所有程序集。CLR 在该阶段<I>不会</I> 加载所有相关程序集；它仅加载包含主窗体类上的成员变量的类型定义的程序集。在加载了这些程序集之后，实时 (JIT) 编译器将在方法运行时编译方法的代码（从 <B>Main</B> 方法开始）。同样，JIT 编译器<I>不会</I> 编译您的程序集中的所有代码。相反，将根据需要逐个方法地编译代码。</P>
<P>要尽可能减少应用程序的启动时间，您应该遵循下列原则： </P>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>尽可能减少应用程序主窗体类中的成员变量</B>。这将在 CLR 加载主窗体类时尽可能减少必须解析的类型数量。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>尽量不要立即使用大型基类程序集（</B><B>XML </B><B>库或</B><B> ADO.NET </B><B>库）中的类型</B>。这些程序集的加载很费时间。使用应用程序配置类和跟踪开关功能时将引入 XML 库。如果要优先考虑应用程序启动时间，请避免这一点。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>尽可能使用惰性加载</B>。仅在需要时获取数据，而不是提前加载和冻结 UI。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>将应用程序设计为使用较少的程序集</B>。带有大量程序集的应用程序会招致性能开销增加。这些开销来自加载元数据、访问 CLR 中的预编译映像中的各种内存页以加载程序集（如果它是用本机映像生成器工具 Ngen.exe 预编译的）、JIT 编译时间、安全检查等等。您应该考虑基于程序集的使用模式来合并程序集，以便降低相关联的性能开销。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>避免设计将多个组件的功能组合到一个组件中的单一类</B>。将设计分解到多个只须在实际调用时进行编译的较小类。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>将应用程序设计为在初始化期间对网络服务进行并行调用</B>。通过在初始化期间调用可以并行运行的网络服务，可以利用服务代理提供的异步功能。这有助于释放当前执行的线程并且并发地调用服务以完成任务。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>使用</B><B> NGEN.exe </B><B>编译和试验</B><B> NGen </B><B>和非</B><B> NGen </B><B>程序集，并且确定哪个程序集保存了最大数量的工作集页面</B>。NGEN.exe（它随附在 .NET Framework 中）用于预编译程序集以创建本机映像，该映像随后被存储在全局程序集缓存的特殊部分，以便应用程序下次需要它时使用。通过创建程序集的本机映像，可以使程序集更快地加载和执行，因为 CLR 不需要动态生成程序集中包含的代码和数据结构。有关详细信息，请参阅 <I>Improving .NET Application Performance and Scalability</I> 的第 5 章“Improving Managed Code Performance”中的“Working Set Considerations”和“NGen.exe Explained”部分，网址为：<A href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnpag/html/scalenetchapt05.asp" target=_blank><FONT color=#002c99>http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnpag/html/scalenetchapt05.asp</FONT></A>。 </P>
<P><B>注</B>如果您使用 NGEN 预编译程序集，则会立即加载它的所有依赖程序集。</P></TD></TR></TBODY></TABLE>
<H3>管理可用资源</H3>
<P>公共语言运行库 (CLR) 使用垃圾回收器来管理对象生存期和内存使用。这意味着无法再访问的对象将被垃圾回收器自动回收，并且自动回收内存。由于多种原因无法再访问对象。例如，可能没有对该对象的任何引用，或者对该对象的所有引用可能来自其他可作为当前回收周期的一部分进行回收的对象。尽管自动垃圾回收使您的代码不必负责管理对象删除，但这意味着您的代码不再对对象的确切删除时间具有显式控制。</P>
<P>请考虑下列原则，以确保您能够有效地管理可用资源： </P>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>确保在被调用方对象提供</B><B> Dispose </B><B>方法时该方法得到调用</B>。如果您的代码调用了支持 <B>Dispose</B> 方法的对象，则您应该确保在使用完该对象之后立即调用此方法。调用 <B>Dispose</B> 方法可以确保抢先释放非托管资源，而不是等到发生垃圾回收。除了提供 <B>Dispose</B> 方法以外，某些对象还提供其他管理资源的方法，例如，<B>Close </B>方法。在这些情况下，您应该参考文档资料以了解如何使用其他方法。例如，对于 <B>SqlConnection</B> 对象而言，调用 <B>Close</B> 或 <B>Dispose</B> 都足可以抢先将数据库连接释放回连接池中。一种可以确保您在对象使用完毕之后立即调用 <B>Dispose</B> 的方法是使用 Visual C# .NET 中的 <B>using</B> 语句或 Visual Basic .NET 中的 <B>Try/Finally</B> 块。 </P>
<P>下面的代码片段演示了 <B>Dispose</B> 的用法。 </P>
<P>C# 中的 <B>using</B> 语句示例： </P><PRE class=codeSample>using( StreamReader myFile = new StreamReader("C:\\ReadMe.Txt")){ 
string contents = myFile.ReadToEnd(); 
//... use the contents of the file 
} // dispose is called and the StreamReader's resources released 
</PRE>
<P>Visual Basic .NET 中的 <B>Try/Finally</B> 块示例： </P><PRE class=codeSample>Dim myFile As StreamReader 
myFile = New StreamReader("C:\\ReadMe.Txt") 
Try 
String contents = myFile.ReadToEnd() 
'... use the contents of the file 
Finally 
myFile.Close() 
End Try 
</PRE>
<P><B>注</B>在 C# 和 C++ 中，Finalize 方法是作为析构函数实现的。在 Visual Basic .NET 中，<B>Finalize</B> 方法是作为 <B>Object</B> 基类上的 <B>Finalize</B> 子例程的重写实现的。</P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>如果您在客户端调用过程中占据非托管资源，则请提供</B><B> Finalize </B><B>和</B><B> Dispose </B><B>方法</B>。如果您在公共或受保护的方法调用中创建访问非托管资源的对象，则应用程序需要控制非托管资源的生存期。在图 8.1 中，第一种情况是对非托管资源的调用，在此将打开、获取和关闭资源。在此情况下，您的对象无须提供 <B>Finalize</B> 和 <B>Dispose</B> 方法。在第二种情况下，在方法调用过程中占据非托管资源；因此，您的对象应该提供 <B>Finalize</B> 和 <B>Dispose</B> 方法，以便客户端在使用完该对象后可以立即显式释放资源。 </P>
<DIV style="WIDTH: 450px"><IMG height=439 alt=ppt-pic_f01 src="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/art/ppt-pic_f01.gif" width=450 border=0><BR>
<P class=figureCaption><B>图</B><B> 8.1</B><B>：</B><B>Dispose </B><B>和</B><B> Finalize </B><B>方法调用的用法</B></P>
<DIV class=figureRule></DIV></DIV></TD></TR></TBODY></TABLE>
<P>垃圾回收通常有利于提高总体性能，因为它将速度的重要性置于内存利用率之上。只有当内存资源不足时，才需要删除对象；否则，将使用所有可用的应用程序资源以使您的应用程序受益。但是，如果您的对象保持对非托管资源（例如，窗口句柄、文件、GDI 对象和网络连接）的引用，则程序员通过在这些资源不再使用时显式释放它们可以获得更好的性能。如果您要在客户端方法调用过程中占据非托管资源，则对象应该允许调用方使用 <B>IDisposable</B> 接口（它提供 <B>Dispose</B> 方法）显式管理资源。通过实现 <B>IDisposable</B>，对象将通知它可被要求明确进行清理，而不是等待垃圾回收。实现 <B>IDisposable</B> 的对象的调用方在使用完该对象后将简单地调用 <B>Dispose</B> 方法，以便它可以根据需要释放资源。</P>
<P>有关如何在某个对象上实现 <B>IDisposable</B> 的详细信息，请参阅 <I>Improving .NET Application Performance and Scalability</I> 中的第 5 章“Improving Managed Code Performance”，网址为：<A href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnpag/html/scalenetchapt05.asp" target=_blank><FONT color=#002c99>http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnpag/html/scalenetchapt05.asp</FONT></A>。</P>
<P><B>注</B>如果您的可处置对象派生自另一个也实现了 IDisposable 接口的对象，则您应该调用基类的 <B>Dispose</B> 方法以使其可以清理它的资源。您还应该调用实现了 <B>IDisposable</B> 接口的对象所拥有的所有对象上的 <B>Dispose</B>。</P>
<P><B>Finalize</B> 方法也使您的对象可以在删除时显式释放其引用的任何资源。由于垃圾回收器所具有的非确定性，在某些情况下，<B>Finalize</B> 方法可能长时间不会被调用。实际上，如果您的应用程序在垃圾回收器删除对象之前终止，则该方法可能永远不会被调用。然而，需要使用 <B>Finalize</B> 方法作为一种后备策略，以防调用方没有显式调用 <B>Dispose</B> 方法（<B>Dispose</B> 和 <B>Finalize</B> 方法共享相同的资源清理代码）。通过这种方式，可能在某个时刻释放资源，即使这发生在最佳时刻之后。</P>
<P><B>注</B>要确保 Dispose 和 Finalize 中的清理代码不会被调用两次，您应该调用 <B>GC.SuppressFinalize</B> 以通知垃圾回收器不要调用 <B>Finalize</B> 方法。</P>
<P>垃圾回收器实现了 <B>Collect</B> 方法，该方法强制垃圾回收器删除所有对象挂起删除。不应该从应用程序内调用该方法，因为回收周期在高优先级线程上运行。回收周期可能冻结所有 UI 线程，从而使得用户界面停止响应。</P>
<P>有关详细信息，请参阅 <I>Improving .NET Application Performance and Scalability</I> 中的“Garbage Collection Guidelines”、“Finalize and Dispose Guidelines”、“Dispose Pattern”和“Finalize and Dispose Guidelines”，网址为：<A href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnpag/html/scalenetchapt05.asp" target=_blank><FONT color=#002c99>http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnpag/html/scalenetchapt05.asp</FONT></A>。</P>
<H3>优化 Windows 窗体性能</H3>
<P>Windows 窗体为智能客户端应用程序提供了内容丰富的用户界面，并且您可以使用许多种技术来帮助确保 Windows 窗体提供最佳性能。在讨论特定技术之前，对一些可以显著提高 Windows 窗体性能的高级原则进行回顾是有用的。 </P>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>小心创建句柄</B>。Windows 窗体将句柄创建虚拟化（即，它动态创建和重新创建窗口句柄对象）。创建句柄对象的系统开销可能非常大；因此，请避免进行不必要的边框样式更改或者更改 MDI 父对象。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>避免创建带有太多子控件的应用程序</B>。Microsoft? Windows? 操作系统限制每个进程最多有 10,000 个控件，但您应该避免在窗体上使用成百上千个控件，因为每个控件都要消耗内存资源。 </P></TD></TR></TBODY></TABLE>
<P>本节的其余部分讨论您可以用来优化应用程序用户界面性能的更为具体的技术。</P>
<H4>使用 BeginUpdate 和 EndUpdate</H4>
<P>许多 Windows 窗体控件（例如，<B>ListView</B> 和 <B>TreeView</B> 控件）实现了 <B>BeginUpdate</B> 和 <B>EndUpdate</B> 方法，它们在操纵基础数据或控件属性时取消了控件的重新绘制。通过使用 <B>BeginUpdate</B> 和 <B>EndUpdate</B> 方法，您可以对控件进行重大更改，并且避免在应用这些更改时让控件经常重新绘制自身。此类重新绘制会导致性能显著降低，并且用户界面闪烁且不反应。</P>
<P>例如，如果您的应用程序具有一个要求添加大量节点项的树控件，则您应该调用 <B>BeginUpdate</B>，添加所有必需的节点项，然后调用 <B>EndUpdate</B>。下面的代码示例显示了一个树控件，该控件用于显示许多个客户的层次结构表示形式及其定单信息。</P>
<P><B>[C#]</B></P><PRE class=codeSample>// Suppress repainting the TreeView until all the objects have been created. 
TreeView1.BeginUpdate(); 
// Clear the TreeView. 
TreeView1.Nodes.Clear(); 
// Add a root TreeNode for each Customer object in the ArrayList. 
foreach( Customer customer2 in customerArray ) 
{ 
    TreeView1.Nodes.Add( new TreeNode( customer2.CustomerName ) ); 
    // Add a child TreeNode for each Order object in the current Customer. 
    foreach( Order order1 in customer2.CustomerOrders ) 
    { 
        TreeView1.Nodes[ customerArray.IndexOf(customer2) ].Nodes.Add( 
             new TreeNode( customer2.CustomerName + "." + order1.OrderID ) ); 
    } 
} 
// Begin repainting the TreeView. 
TreeView1.EndUpdate(); 
</PRE>
<P><B>[Visual Basic .NET]</B></P><PRE class=codeSample>       ' Suppress repainting the TreeView until all the objects have been created. 
        TreeView1.BeginUpdate() 
' Clear the TreeView 
TreeView1.Nodes.Clear() 
' Add a root TreeNode for each Customer object in the ArrayList 
For Each customer2 As Customer In customerArray 
     TreeView1.Nodes.Add(New TreeNode(customer2.CustomerName)) 
     ' Add a child TreeNode for each Order object in the current Customer. 
     For Each order1 As Order In customer2.CustomerOrders 
           TreeView1.Nodes(Array.IndexOf(customerArray, customer2)).Nodes.Add( _ 
                    New TreeNode(customer2.CustomerName &amp; "." &amp; order1.OrderID)) 
     Next 
Next 
' Begin repainting the TreeView. 
TreeView1.EndUpdate() 
</PRE>
<P>即使在您不希望向控件添加许多对象时，您也应该使用 <B>BeginUpdate</B> 和 <B>EndUpdate</B> 方法。在大多数情况下，您在运行之前将不知道要添加的项的确切个数。因此，为了妥善处理大量数据以及应付将来的要求，您应该总是调用 <B>BeginUpdate</B> 和 <B>EndUpdate</B> 方法。</P>
<P><B>注</B>调用 Windows 窗体控件使用的许多 <B>Collection</B> 类的 <B>AddRange</B> 方法时，将自动为您调用 <B>BeginUpdate</B> 和 <B>EndUpdate</B> 方法。</P>
<H4>使用 SuspendLayout 和 ResumeLayout</H4>
<P>许多 Windows 窗体控件（例如，<B>ListView</B> 和 <B>TreeView</B> 控件）都实现了 <B>SuspendLayout</B> 和 <B>ResumeLayout</B> 方法，它们能够防止控件在添加子控件时创建多个布局事件。</P>
<P>如果您的控件以编程方式添加和删除子控件或者执行动态布局，则您应该调用 <B>SuspendLayout</B> 和 <B>ResumeLayout</B> 方法。通过 <B>SuspendLayout</B> 方法，可以在控件上执行多个操作，而不必为每个更改执行布局。例如，如果您调整控件的大小并移动控件，则每个操作都将引发单独的布局事件。</P>
<P>这些方法按照与 <B>BeginUpdate</B> 和 <B>EndUpdate</B> 方法类似的方式操作，并且在性能和用户界面稳定性方面提供相同的好处。</P>
<P>下面的示例以编程方式向父窗体中添加按钮：</P>
<P><B>[C#]</B></P><PRE class=codeSample>private void AddButtons() 
{ 
  // Suspend the form layout and add two buttons. 
  this.SuspendLayout(); 
  Button buttonOK = new Button(); 
  buttonOK.Location = new Point(10, 10); 
  buttonOK.Size = new Size(75, 25); 
  buttonOK.Text = "OK"; 
  Button buttonCancel = new Button(); 
  buttonCancel.Location = new Point(90, 10); 
  buttonCancel.Size = new Size(75, 25); 
  buttonCancel.Text = "Cancel"; 
  this.Controls.AddRange(new Control[]{buttonOK, buttonCancel}); 
  this.ResumeLayout(); 
} 
</PRE>
<P><B>[Visual Basic .NET]</B></P><PRE class=codeSample>Private Sub AddButtons() 
        ' Suspend the form layout and add two buttons 
        Me.SuspendLayout() 
        Dim buttonOK As New Button 
        buttonOK.Location = New Point(10, 10) 
        buttonOK.Size = New Size(75, 25) 
        buttonOK.Text = "OK" 
        Dim buttonCancel As New Button 
        buttonCancel.Location = New Point(90, 10) 
        buttonCancel.Size = New Size(75, 25) 
        buttonCancel.Text = "Cancel" 
        Me.Controls.AddRange(New Control() { buttonOK, buttonCancel } ) 
        Me.ResumeLayout() 
End Sub 
</PRE>
<P>每当您添加或删除控件、执行子控件的自动布局或者设置任何影响控件布局的属性（例如，大小、位置、定位点或停靠属性）时，您都应该使用 <B>SuspendLayout</B> 和 <B>ResumeLayout</B> 方法。</P>
<H4>处理图像</H4>
<P>如果您的应用程序显示大量图像文件（例如，.jpg 和 .gif 文件），则您可以通过以位图格式预先呈现图像来显著改善显示性能。</P>
<P>要使用该技术，请首先从文件中加载图像，然后使用 PARGB 格式将其呈现为位图。下面的代码示例从磁盘中加载文件，然后使用该类将图像呈现为预乘的、Alpha 混合 RGB 格式。例如：</P>
<P><B>[C#]</B></P><PRE class=codeSample>if ( image != null &amp;&amp; image is Bitmap ) 
{ 
Bitmap bm = (Bitmap)image; 
Bitmap newImage = new Bitmap( bm.Width, bm.Height, 
   System.Drawing.Imaging.PixelFormat.Format32bppPArgb ); 
using ( Graphics g = Graphics.FromImage( newImage ) ) 
{ 
g.DrawImage( bm, new Rectangle( 0,0, bm.Width, bm.Height ) ); 
} 
image = newImage; 
} 
</PRE>
<P><B>[Visual Basic .NET]</B></P><PRE class=codeSample>        If Not(image Is Nothing)  AndAlso (TypeOf image Is Bitmap) Then 
            Dim bm As Bitmap = CType(image, Bitmap) 
            Dim newImage As New Bitmap(bm.Width, bm.Height, _ 
                System.Drawing.Imaging.PixelFormat.Format32bppPArgb) 
            Using g As Graphics = Graphics.FromImage(newImage) 
                g.DrawImage(bm, New Rectangle(0, 0, bm.Width, bm.Height)) 
            End Using 
            image = newImage 
        End If 
</PRE>
<H4>使用分页和惰性加载</H4>
<P>在大多数情况下，您应该仅在需要时检索或显示数据。如果您的应用程序需要检索和显示大量信息，则您应该考虑将数据分解到多个页面中，并且一次显示一页数据。这可以使用户界面具有更高的性能，因为它无须显示大量数据。此外，这可以提高应用程序的可用性，因为用户不会同时面对大量数据，并且可以更加容易地导航以查找他或她需要的确切数据。</P>
<P>例如，如果您的应用程序显示来自大型产品目录的产品数据，则您可以按照字母顺序显示这些项，并且将所有以“A”开头的产品显示在一个页面上，将所有以“B”开头的产品显示在下一个页面上。然后，您可以让用户直接导航到适当的页面，以便他或她无须浏览所有页面就可以获得他或她需要的数据。</P>
<P>以这种方式将数据分页还使您可以根据需要获取后台的数据。例如，您可能只需要获取第一页信息以便显示并且让用户与其进行交互。然后，您可以获取后台中的、已经准备好供用户使用的下一页数据。该技术在与数据缓存技术结合使用时可能特别有效。</P>
<P>您还可以通过使用惰性加载技术来提高智能客户端应用程序的性能。您无须立即加载可能在将来某个时刻需要的数据或资源，而是可以根据需要加载它们。您可以在构建大型列表或树结构时使用惰性加载来提高用户界面的性能。在此情况下，您可以在用户需要看到数据时（例如，在用户展开树节点时）加载它。</P>
<H4>优化显示速度</H4>
<P>根据您用于显示用户界面控件和应用程序窗体的技术，您可以用多种不同的方式来优化应用程序的显示速度。</P>
<P>当您的应用程序启动时，您应该考虑尽可能地显示简单的用户界面。这将减少启动时间，并且向用户呈现整洁且易于使用的用户界面。而且，您应该努力避免引用类以及在启动时加载任何不会立刻需要的数据。这将减少应用程序和 .NET Framework 初始化时间，并且提高应用程序的显示速度。</P>
<P>当您需要显示对话框或窗体时，您应该在它们做好显示准备之前使其保持隐藏状态，以便减少需要的绘制工作量。这将有助于确保窗体仅在初始化之后显示。</P>
<P>如果您的应用程序具有的控件含有覆盖整个客户端表面区域的子控件，则您应该考虑将控件背景样式设置为不透明。这可以避免在发生每个绘制事件时重绘控件的背景。您可以通过使用 <B>SetStyle</B> 方法来设置控件的样式。使用 <B>ControlsStyles.Opaque</B> 枚举可以指定不透明控件样式。</P>
<P>您应该避免任何不必要的控件重新绘制操作。一种方法是在设置控件的属性时隐藏控件。在 <B>OnPaint</B> 事件中具有复杂绘图代码的应用程序能够只重绘窗体的无效区域，而不是绘制整个窗体。<B>OnPaint</B> 事件的 <B>PaintEventArgs</B> 参数包含一个 <B>ClipRect</B> 结构，它指示窗口的哪个部分无效。这可以减少用户等待查看完整显示的时间。</P>
<P>使用标准的绘图优化，例如，剪辑、双缓冲和 <B>ClipRectangle</B>。这还将通过防止对不可见或要求重绘的显示部分执行不必要的绘制操作，从而有助于改善智能客户端应用程序的显示性能。有关增强绘图性能的详细信息，请参阅 <I>Painting techniques using Windows Forms for the Microsoft .NET Framework</I>，网址为：<A href="http://windowsforms.net/articles/windowsformspainting.aspx" target=_blank><FONT color=#002c99>http://windowsforms.net/articles/windowsformspainting.aspx</FONT></A>。</P>
<P>如果您的显示包含动画或者经常更改某个显示元素，则您应该使用双缓冲或多缓冲，在绘制当前图像的过程中准备下一个图像。<B>System.Windows.Forms</B> 命名空间中的 <B>ControlStyles</B> 枚举适用于许多控件，并且 <B>DoubleBuffer</B> 成员可以帮助防止闪烁。启用 <B>DoubleBuffer</B> 样式将使您的控件绘制在离屏缓冲中完成，然后同时绘制到屏幕上。尽管这有助于防止闪烁，但它的确为分配的缓冲区使用了更多内存。</P>
<DIV style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 10px"><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter8SCAppPerf.mspx#top"><IMG height=9 alt=返回页首 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_up.gif" width=7 border=0></A><A class=topOfPage href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter8SCAppPerf.mspx#top"><FONT color=#002c99>返回页首</FONT></A></DIV><A name=EEAA></A>
<H2>性能调整和诊断</H2>
<P>在设计和实现阶段处理性能问题是实现应用程序性能目标的最划算的方法。但是，您只有在开发阶段经常且尽早测试应用程序的性能，才能真正有效地优化应用程序的性能。</P>
<P>尽管针对性能进行设计和测试都很重要，但在这些早期阶段优化每个组件和所有代码不是有效的资源用法，因此应该予以避免。所以，应用程序可能存在您在设计阶段未预料到的性能问题。例如，您可能遇到由于两个系统或组件之间的无法预料的交互而产生的性能问题，或者您可能使用原来存在的、未按希望的方式执行的代码。在此情况下，您需要追究性能问题的根源，以便您可以适当地解决该问题。</P>
<P>本节讨论一些将帮助您诊断性能问题以及调整应用程序以获得最佳性能的工具和技术。</P>
<H3>制定性能目标</H3>
<P>当您设计和规划智能客户端应用程序时，您应该仔细考虑性能方面的要求，并且定义合适的性能目标。在定义这些目标时，请考虑您将如何度量应用程序的实际性能。您的性能度量标准应该明确体现应用程序的重要性能特征。请努力避免无法准确度量的模糊或不完整的目标，例如，“应用程序必须快速运行”或“应用程序必须快速加载”。您需要了解应用程序的性能和可伸缩性目标，以便您可以设法满足这些目标并且围绕它们来规划您的测试。请确保您的目标是可度量的和可验证的。</P>
<P>定义良好的性能度量标准使您可以准确跟踪应用程序的性能，以便您可以确定应用程序是否能够满足它的性能目标。这些度量标准应该包括在应用程序测试计划中，以便可以在应用程序的测试阶段度量它们。</P>
<P>本节重点讨论与智能客户端应用程序相关的特定性能目标的定义。如果您还要设计和生成客户端应用程序将消耗的网络服务，则您还需要为这些服务定义适当的性能目标。在此情况下，您应该确保考虑整个系统的性能要求，以及应用程序各个部分的性能与其他部分以及整个系统之间存在怎样的关系。</P>
<H4>考虑用户的观点</H4>
<P>当您为智能客户端应用程序确定合适的性能目标时，您应该仔细考虑用户的观点。对于智能客户端应用程序而言，性能与可用性和用户感受有关。例如，只要用户能够继续工作并且获得有关操作进度的足够反馈，用户就可以接受漫长的操作。</P>
<P>在确定要求时，将应用程序的功能分解为多个使用情景或使用案例通常是有用的。您应该识别对于实现特定性能目标而言关键且必需的使用案例和情景。应该将许多使用案例所共有且经常执行的任务设计得具有较高性能。同样，如果任务要求用户全神贯注并且不允许用户从其切换以执行其他任务，则需要提供优化的且有效的用户体验。如果任务不太经常使用且不会阻止用户执行其他任务，则可能无须进行大量调整。</P>
<P>对于您识别的每个性能敏感型任务，您都应该精确地定义用户的操作以及应用程序的响应方式。您还应该确定每个任务使用的网络和客户端资源或组件。该信息将影响性能目标，并且将驱动对性能进行度量的测试。</P>
<P>可用性研究提供了非常有价值的信息源，并且可能大大影响性能目标的定义。正式的可用性研究在确定用户如何执行他们的工作、哪些使用情景是共有的以及哪些不是共有的、用户经常执行哪些任务以及从性能观点看来应用程序的哪些特征是重要的等方面可能非常有用。如果您要生成新的应用程序，您应该考虑提供应用程序的原型或模型，以便可以执行基本的可用性测试。</P>
<H4>考虑应用程序操作环境</H4>
<P>对应用程序的操作环境进行评估是很重要的，因为这可能对应用程序施加必须在您制定的性能目标中予以反映的约束。</P>
<P>位于网络上的服务可能对您的应用程序施加性能约束。例如，您可能需要与您无法控制的 Web 服务进行交互。在这种情况下，需要确定该服务的性能，并且确定这是否将对客户端应用程序的性能产生影响。</P>
<P>您还应该确定任何相关服务和组件的性能如何随着时间的变化而变化。某些系统会经受相当稳定的使用，而其他系统则会在一天或一周的特定时间经受变动极大的使用。这些区别可能在关键时间对应用程序的性能造成不利影响。例如，提供应用程序部署和更新服务的服务可能会在星期一早上 9 点缓慢响应，因为所有用户都在此时升级到应用程序的最新版本。</P>
<P>另外，还需要准确地对所有相关系统和组件的性能进行建模，以便可以在严格模拟应用程序的实际部署环境的环境中测试您的应用程序。对于每个系统，您都应该确定性能概况以及最低、平均和最高性能特征。然后，您可以在定义应用程序的性能要求时根据需要使用该数据。</P>
<P>您还应该仔细考虑用于运行应用程序的硬件。您将需要确定在处理器、内存、图形功能等方面的目标硬件配置，或者至少确定一个如果得不到满足则无法保证性能的最低配置。</P>
<P>通常，应用程序的业务操作环境将规定一些更为苛刻的性能要求。例如，执行实时股票交易的应用程序将需要执行这些交易并及时显示所有相关数据。</P>
<H3>性能调整过程</H3>
<P>对应用程序进行性能调整是一个迭代过程。该过程由一些重复执行直至应用程序满足其性能目标的阶段组成。（请参见<B>图</B><B> 8.2</B>。）</P>
<DIV style="WIDTH: 356px"><IMG height=238 alt=chapter8_f02 src="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/art/chapter8_f02.gif" width=356 border=0><BR>
<P class=figureCaption><B>图</B><B> 8.2</B><B>：性能调整过程</B></P>
<DIV class=figureRule></DIV></DIV>
<P>正如<B>图</B><B> 8.2</B> 所阐明的，性能调整要求您完成下列过程： </P>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>建立基准</B>。在您开始针对性能调整应用程序时，您必须具有与性能目标、目标和度量标准有关的定义良好的基准。这可能包括应用程序工作集大小、加载数据（例如，目录）的时间、事务持续时间等等。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>收集数据</B>。您将需要通过针对您已经定义的性能目标度量应用程序的性能，来对应用程序性能进行评价。性能目标应该体现特定的且可度量的度量标准，以使您可以在任何时刻量化应用程序的性能。要使您可以收集性能数据，您可能必须对应用程序进行规范，以便可以发布和收集必需的性能数据。下一节将详细讨论您可以用来完成这一工作的一些选项。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>分析结果</B>。在收集应用程序的性能数据之后，您将能够通过确定哪些应用程序功能要求最多的关注，来区分性能调整工作的轻重缓急。此外，您可以使用该数据来确定任何性能瓶颈的位置。通常，您将只能够通过收集更详细的性能数据来确定瓶颈的确切位置：例如，通过使用应用程序规范。性能分析工具可能帮助您识别瓶颈。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>调整应用程序</B>。在已经识别瓶颈之后，您可能需要修改应用程序或其配置，以便尝试解决问题。您应该致力于将更改降低至最低限度，以便可以确定更改对应用程序性能的影响。如果您同时进行多项更改，可能难以确定每项更改对应用程序的总体性能的影响。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>测试和度量</B>。在更改应用程序或其配置之后，您应该再次测试它以确定更改具有的效果，并且使新的性能数据得以收集。性能工作通常要求进行体系结构或其他具有较高影响的更改，因此彻底的测试是很关键的。您的应用程序测试计划应该针对预料到的所有情况，在配置了适当硬件和软件的客户计算机上演习应用程序所实现的完整范围的功能。如果您的应用程序使用网络资源，则应该加载这些资源，以便您可以获得有关应用程序在此类环境中所具有的性能的准确度量。 </P></TD></TR></TBODY></TABLE>
<P>上述过程将使您可以通过针对特定目标度量应用程序的总体性能，来重点解决特定的性能问题。</P>
<H3>性能工具</H3>
<P>您可以使用许多工具来帮助您收集和分析应用程序的性能数据。本节中介绍的每种工具都具有不同的功能，您可以使用这些功能来度量、分析和查找应用程序中的性能瓶颈。</P>
<P><B>注</B>除了这里介绍的工具以外，您还可以使用其他一些选项和第三方工具。有关其他日志记录和异常管理选项的说明，请参阅<B></B><I>Exception Management Architecture Guide</I>，网址为：</P>
<P><A href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnbda/html/exceptdotnet.asp" target=_blank><FONT color=#002c99>http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnbda/html/exceptdotnet.asp</FONT></A></P>
<P>在决定哪些工具最适合您的需要之前，您应该仔细考虑您的确切要求。</P>
<H3>使用性能日志和警报</H3>
<P>性能日志和警报是作为 Windows 操作系统的一部分发行的一种管理性能监控工具。它依靠由各种 Windows 组件、子系统和应用程序发布的性能计数器，使您可以跟踪资源使用情况以及针对时间以图形方式绘制它们。</P>
<P>您可以使用 Performance Logs and Alerts 来监控标准的性能计数器（例如，内存使用情况或处理器使用情况），或者您可以定义您自己的自定义计数器来监控应用程序特定的活动。</P>
<P>.NET CLR 提供了许多有用的性能计数器，它们使您可以洞察应用程序性能的好坏。关系比较大的一些性能对象是： </P>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>.NET CLR </B><B>内存</B>。提供有关托管 .NET 应用程序内存使用情况的数据，包括应用程序正在使用的内存数量以及对未使用的对象进行垃圾回收所花费的时间。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>.NET CLR </B><B>加载</B>。提供有关应用程序正在使用的类和应用程序域的数量的数据，并且提供有关它们的加载和卸载速率的数据。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>.NET CLR </B><B>锁和线程</B>。提供与应用程序内使用的线程有关的性能数据，包括线程个数以及试图同时对受保护的资源进行访问的线程之间的争用率。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>.NET CLR </B><B>网络</B>。提供与通过网络发送和接收数据有关的性能计数器，包括每秒发送和接收的字节数以及活动连接的个数。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>.NET CLR </B><B>异常</B>。提供有关应用程序所引发和捕获的异常个数的报告。 </P></TD></TR></TBODY></TABLE>
<P>有关上述计数器、它们的阈值、要度量的内容以及如何度量它们的详细信息，请参阅 <I>Improving .NET Application Performance and Scalability</I> 的第 15 章“Measuring .NET Application Performance”中的“CLR and Managed Code”部分，网址为：<A href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnpag/html/scalenetchapt15.asp" target=_blank><FONT color=#002c99>http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnpag/html/scalenetchapt15.asp</FONT></A>。</P>
<P>您的应用程序还可以提供您可以通过使用性能日志和警报轻松监控的、应用程序特定的性能计数器。您可以像以下示例所显示的那样，定义自定义性能计数器：</P>
<P><B>[C#]</B></P><PRE class=codeSample>PerformanceCounter counter = new PerformanceCounter( "Category", 
            "CounterName", false ); 
</PRE>
<P><B>[Visual Basic .NET]</B></P><PRE class=codeSample>Dim counter As New PerformanceCounter("Category", "CounterName", False) 
</PRE>
<P>在创建性能计数器对象之后，您可以为您的自定义性能计数器指定类别，并将所有相关计数器保存在一起。<B>PerformanceCounter</B> 类在 <B>System.Diagnostics</B> 命名空间中定义，该命名空间中还定义了其他一些可用于读取和定义性能计数器和类别的类。有关创建自定义性能计数器的详细信息，请参阅知识库中编号为 317679 的文章“How to create and make changes to a custom counter for the Windows Performance Monitor by using Visual Basic .NET”，网址为：<A href="http://support.microsoft.com/default.aspx?scid=kb;en-us;317679" target=_blank><FONT color=#002c99>http://support.microsoft.com/default.aspx?scid=kb;en-us;317679</FONT></A>。</P>
<P><B>注</B>要注册性能计数器，您必须首先注册该类别。您必须具有足够的权限才能注册性能计数器类别（它可能影响您部署应用程序的方式）。</P>
<H3>规范</H3>
<P>您可以使用许多工具和技术来帮助您对应用程序进行规范，并且生成度量应用程序性能所需的信息。这些工具和技术包括： </P>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>Event Tracing for Windows (ETW)</B>。该 ETW 子系统提供了一种系统开销较低（与性能日志和警报相比）的手段，用以监控具有负载的系统的性能。这主要用于必须频繁记录事件、错误、警告或审核的服务器应用程序。有关详细信息，请参阅 Microsoft Platform SDK 中的“Event Tracing”，网址为：<A href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/perfmon/base/event_tracing.asp" target=_blank><FONT color=#002c99>http://msdn.microsoft.com/library/default.asp?url=/library/en-us/perfmon/base/event_tracing.asp</FONT></A>。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>Enterprise</B><B> Instrumentation Framework (EIF)</B>。EIF 是一种可扩展且可配置的框架，您可以使用它来对智能客户端应用程序进行规划。它提供了一种可扩展的事件架构和统一的 API — 它使用 Windows 中内置的现有事件、日志记录和跟踪机制，包括 Windows Management Instrumentation (WMI)、Windows Event Log 和 Windows Event Tracing。它大大简化了发布应用程序事件所需的编码。如果您计划使用 EIF，则需要通过使用 EIF .msi 在客户计算机上安装 EIF。如果您要在智能客户端应用程序中使用 EIF，则需要在决定应用程序的部署方式时考虑这一要求。有关详细信息，请参阅“How To:Use EIF”，网址为：<A href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnpag/html/scalenethowto14.asp" target=_blank><FONT color=#002c99>http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnpag/html/scalenethowto14.asp</FONT></A>。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>Logging Application Block</B>。Logging Application Block 提供了可扩展且可重用的代码组件，以帮助您生成规范化的应用程序。它建立在 EIF 的功能基础之上，以提供某些功能，例如，针对事件架构的增强功能、多个日志级别、附加的事件接收等等。有关详细信息，请参阅“Logging Application Block”，网址为“<A href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnpag/html/Logging.asp" target=_blank><FONT color=#002c99>http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnpag/html/Logging.asp</FONT></A>。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>Windows Management Instrumentation (WMI)</B>。WMI 组件是 Windows 操作系统的一部分，并且提供了用于访问企业中的管理信息和控件的编程接口。系统管理员常用它来自动完成管理任务（通过使用调用 WMI 组件的脚本）。有关详细信息，请参阅 Windows Management Information，网址为：<A href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/wmisdk/wmi/wmi_start_page.asp" target=_blank><FONT color=#002c99>http://msdn.microsoft.com/library/default.asp?url=/library/en-us/wmisdk/wmi/wmi_start_page.asp</FONT></A>。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>调试和跟踪类</B>。.NET Framework 在 <B>System.Diagnosis</B> 下提供了 <B>Debug</B> 和 <B>Trace</B> 类来对代码进行规范。<B>Debug</B> 类主要用于打印调试信息以及检查是否有断言。<B>Trace</B> 类使您可以对发布版本进行规范，以便在运行时监控应用程序的完好状况。在 Visual Studio .NET 中，默认情况下启用跟踪。在使用命令行版本时，您必须为编译器添加 <B>/d:Trace</B> 标志，或者在 Visual C# .NET 源代码中添加 <B>#define TRACE</B>，以便启用跟踪。对于 Visual Basic .NET 源代码，您必须为命令行编译器添加 <B>/d:TRACE=True</B>。有关详细信息，请参阅知识库中编号为 815788 的文章“HOW TO:Trace and Debug in Visual C# .NET”，网址为：<A href="http://support.microsoft.com/default.aspx?scid=kb;en-us;815788" target=_blank><FONT color=#002c99>http://support.microsoft.com/default.aspx?scid=kb;en-us;815788</FONT></A>。 </P></TD></TR></TBODY></TABLE>
<H3>CLR Profiler</H3>
<P>CLR Profiler 是 Microsoft 提供的一种内存分析工具，并且可以从 MSDN 下载。它使您能够查看应用程序进程的托管堆以及调查垃圾回收器的行为。使用该工具，您可以获取有关应用程序的执行、内存分配和内存消耗的有用信息。这些信息可以帮助您了解应用程序的内存使用方式以及如何优化应用程序的内存使用情况。</P>
<P>CLR Profiler 可从 <A href="http://msdn.microsoft.com/netframework/downloads/tools/default.aspx" target=_blank><FONT color=#002c99>http://msdn.microsoft.com/netframework/downloads/tools/default.aspx</FONT></A> 获得。有关如何使用 CLR Profiler 工具的详细信息，另请参阅“How to use CLR Profiler”，网址为：<A href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnpag/html/scalenethowto13.asp?frame=true" target=_blank><FONT color=#002c99>http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnpag/html/scalenethowto13.asp?frame=true</FONT></A>。</P>
<P>CLR Profiler 在日志文件中记录内存消耗和垃圾回收器行为信息。然后，您可以使用一些不同的图形视图，通过 CLR Profiler 来分析该数据。一些比较重要的视图是： </P>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>Allocation Graph</B>。显示有关对象分配方式的调用堆栈。您可以使用该视图来查看方法进行的每个分配的系统开销，隔离您不希望发生的分配，以及查看方法可能进行的过度分配。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>Assembly, Module, Function, and Class Graph</B>。显示哪些方法造成了哪些程序集、函数、模块或类的加载。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>Call Graph</B>。使您可以查看哪些方法调用了其他哪些方法以及相应的调用频率。您可以使用该图表来确定库调用的系统开销，以及调用了哪些方法或对特定方法进行了多少个调用。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>Time Line</B>。提供了有关应用程序执行的基于文本的、按时间顺序的层次结构视图。使用该视图可以查看分配了哪些类型以及这些类型的大小。您还可以使用该视图查看方法调用使得哪些程序集被加载，并且分析您不希望发生的分配。您可以分析完成器的使用情况，并且识别尚未实现或调用 <B>Close</B> 或 <B>Dispose</B> 从而导致瓶颈的方法。 </P></TD></TR></TBODY></TABLE>
<P>您可以使用 CLR Profiler.exe 来识别和隔离与垃圾回收有关的问题。这包括内存消耗问题（例如，过度或未知的分配、内存泄漏、生存期很长的对象）以及在执行垃圾回收时花费的时间的百分比。</P>
<P><B>注</B>有关如何使用 CLR Profiler 工具的详细信息，请参阅“Improving .NET Application Performance and Scalability”，网址为：<A href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnpag/html/scalenethowto13.asp?frame=true" target=_blank><FONT color=#002c99>http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnpag/html/scalenethowto13.asp?frame=true</FONT></A>。</P>
<DIV style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 10px"><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter8SCAppPerf.mspx#top"><IMG height=9 alt=返回页首 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_up.gif" width=7 border=0></A><A class=topOfPage href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter8SCAppPerf.mspx#top"><FONT color=#002c99>返回页首</FONT></A></DIV><A name=EDAA></A>
<H2>小结</H2>
<P>要完全实现智能客户端应用程序的潜能，您需要在应用程序的设计阶段仔细考虑性能问题。通过在早期阶段解决这些性能问题，您可以在应用程序设计过程中控制成本，并减小在开发周期的后期陷入性能问题的可能性。</P>
<P>本章分析了许多不同的技术，您可以在规划和设计智能客户端应用程序时使用这些技术，以确保优化它们的性能。本章还考察了您可以用来确定智能客户端应用程序内的性能问题的一些工具和技术。</P>
<DIV style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 10px"><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter8SCAppPerf.mspx#top"><IMG height=9 alt=返回页首 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_up.gif" width=7 border=0></A><A class=topOfPage href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter8SCAppPerf.mspx#top"><FONT color=#002c99>返回页首</FONT></A></DIV><A name=ECAA></A>
<H2>参考资料</H2>
<P>有关详细信息，请参阅以下内容： </P>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><A href="http://msdn.microsoft.com/perf" target=_blank><FONT color=#002c99>http://msdn.microsoft.com/perf </FONT></A></P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><A href="http://www.windowsforms.net/Default.aspx" target=_blank><FONT color=#002c99>http://www.windowsforms.net/Default.aspx </FONT></A></P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><A href="http://msdn.microsoft.com/vstudio/using/understand/perf/" target=_blank><FONT color=#002c99>http://msdn.microsoft.com/vstudio/using/understand/perf/ </FONT></A></P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><A href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnnetcomp/html/netcfimproveformloadperf.asp" target=_blank><FONT color=#002c99>http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnnetcomp/html/netcfimproveformloadperf.asp </FONT></A></P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><A href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dndotnet/html/highperfmanagedapps.asp" target=_blank><FONT color=#002c99>http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dndotnet/html/highperfmanagedapps.asp </FONT></A></P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><A href="http://msdn.microsoft.com/msdnmag/issues/02/08/AdvancedBasics/default.aspx" target=_blank><FONT color=#002c99>http://msdn.microsoft.com/msdnmag/issues/02/08/AdvancedBasics/default.aspx </FONT></A></P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><A href="http://msdn.microsoft.com/library/default.asp?url=/msdnmag/issues/04/01/NET/toc.asp?frame=true" target=_blank><FONT color=#002c99>http://msdn.microsoft.com/library/default.asp?url=/msdnmag/issues/04/01/NET/toc.asp?frame=true </FONT></A></P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><A href="http://msdn.microsoft.com/library/default.asp?url=/msdnmag/issues/03/02/Multithreading/toc.asp?frame=true" target=_blank><FONT color=#002c99>http://msdn.microsoft.com/library/default.asp?url=/msdnmag/issues/03/02/Multithreading/toc.asp?frame=true </FONT></A></P></TD></TR></TBODY></TABLE>
<P><A href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnpag/html/SCAG-CH08.asp" target=_blank><FONT color=#002c99>转到原英文页面</FONT></A></P><img src ="http://www.blogjava.net/TrampEagle/aggbug/30147.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/TrampEagle/" target="_blank">TrampEagle</a> 2006-02-10 14:58 <a href="http://www.blogjava.net/TrampEagle/articles/30147.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>智能客户端体系结构与设计指南 第 7 章 — 部署和更新智能客户端应用程序</title><link>http://www.blogjava.net/TrampEagle/articles/30146.html</link><dc:creator>TrampEagle</dc:creator><author>TrampEagle</author><pubDate>Fri, 10 Feb 2006 06:57:00 GMT</pubDate><guid>http://www.blogjava.net/TrampEagle/articles/30146.html</guid><wfw:comment>http://www.blogjava.net/TrampEagle/comments/30146.html</wfw:comment><comments>http://www.blogjava.net/TrampEagle/articles/30146.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/TrampEagle/comments/commentRss/30146.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/TrampEagle/services/trackbacks/30146.html</trackback:ping><description><![CDATA[原文引自：<A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter1Introduction.mspx">http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter1Introduction.mspx</A><BR><BR>
<H1>第 7 章 — 部署和更新智能客户端应用程序</H1>
<H2 class=subtitle></H2>
<DIV class=date>发布日期： 08/20/2004<SPAN class=datePipe> | </SPAN>更新日期： 08/20/2004</DIV>
<DIV class=overview>
<DIV style="WIDTH: 250px"><IMG height=68 alt=pponline src="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/art/pponline.gif" width=250 border=0><BR>
<P class=figureCaption><A href="http://www.microsoft.com/resources/practices/default.mspx" target=_blank></A></P>
<DIV class=figureRule></DIV></DIV>
<P><B>智能客户端体系结构与设计指南</B></P>
<P>David Hill, Brenton Webster, Edward A. Jezierski, Srinath Vasireddy and Mohammad Al-Sabt, Microsoft Corporation; Blaine Wastell, Ascentium Corporation; Jonathan Rasmusson and Paul Gale, ThoughtWorks; and Paul Slater, Wadeware LLC</P>
<P><B>相关链接</B></P>
<P>Microsoft® <I>patterns &amp; practices</I> 库 (<A href="http://www.microsoft.com/resources/practices/default.mspx" target=_blank><FONT color=#002c99>http://www.microsoft.com/resources/practices/default.mspx</FONT></A>)</P>
<P><I>.NET </I><I>的应用程序体系结构：设计应用程序和服务</I> (<A href="http://msdn.microsoft.com/library/en-us/dnbda/html/distapp.asp" target=_blank><FONT color=#002c99>http://msdn.microsoft.com/library/en-us/dnbda/html/distapp.asp</FONT></A>) </P>
<P><B>摘要</B>：本章介绍如何在智能客户端和 Windows 平台中最好地使用 .NET Framework 提供的部署功能，并且提供有关如何取舍可用的部署和更新机制的指导。</P></DIV>
<CENTER><IMG title="" height=6 alt=* src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/3squares.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/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter7DeployUpdSCApp.mspx#EGAA"><IMG height=9 alt="部署 .NET Framework" hspace=4 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_down.gif" width=7 vspace=2 border=0></A></TD>
<TD class=onThisPage><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter7DeployUpdSCApp.mspx#EGAA"><FONT color=#002c99>部署 .NET Framework</FONT></A></TD></TR>
<TR vAlign=top>
<TD><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter7DeployUpdSCApp.mspx#EFAA"><FONT color=#002c99><IMG height=9 alt="预先安装 .NET Framework" hspace=4 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_down.gif" width=7 vspace=2 border=0></FONT></A></TD>
<TD class=onThisPage><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter7DeployUpdSCApp.mspx#EFAA"><FONT color=#002c99>预先安装 .NET Framework</FONT></A></TD></TR>
<TR vAlign=top>
<TD><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter7DeployUpdSCApp.mspx#EEAA"><FONT color=#002c99><IMG height=9 alt=部署智能客户端应用程序 hspace=4 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_down.gif" width=7 vspace=2 border=0></FONT></A></TD>
<TD class=onThisPage><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter7DeployUpdSCApp.mspx#EEAA"><FONT color=#002c99>部署智能客户端应用程序</FONT></A></TD></TR>
<TR vAlign=top>
<TD><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter7DeployUpdSCApp.mspx#EDAA"><FONT color=#002c99><IMG height=9 alt=部署智能客户端应用程序 hspace=4 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_down.gif" width=7 vspace=2 border=0></FONT></A></TD>
<TD class=onThisPage><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter7DeployUpdSCApp.mspx#EDAA"><FONT color=#002c99>部署智能客户端应用程序</FONT></A></TD></TR>
<TR vAlign=top>
<TD><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter7DeployUpdSCApp.mspx#ECAA"><FONT color=#002c99><IMG height=9 alt=选择正确的更新方法 hspace=4 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_down.gif" width=7 vspace=2 border=0></FONT></A></TD>
<TD class=onThisPage><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter7DeployUpdSCApp.mspx#ECAA"><FONT color=#002c99>选择正确的更新方法</FONT></A></TD></TR></TBODY></TABLE>
<P>智能客户端应用程序在客户端计算机上执行本地处理，因此需要将它们部署到这些计算机上。过去，在客户端计算机上长期部署、更新、维护和卸载应用程序非常困难而且存在很多问题。由于 COM 的缘故，几个问题使得向客户端计算机部署应用程序变得非常困难，包括： </P>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>与注册表紧密耦合的应用程序</B>。安装 COM 应用程序要求在注册表中注册类和类型库。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>非独立的应用程序</B>。除了必须在注册表中注册类和类型以外，应用程序通常还包括位于硬盘上的共享文件以及注册表中包含的配置设置。应用程序不是独立的；相反，它的构成部分分布在计算机中的不同区域。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>无法并列部署的组件</B>。无法将同一 DLL 的两个不同版本部署到同一目录中。 </P></TD></TR></TBODY></TABLE>
<P>这些问题构成了有效部署和维护客户端应用程序的巨大障碍。</P>
<P>Microsoft ® .NET Framework 具有一些能够简化部署 .NET Framework 应用程序的过程的功能。这些功能包括： </P>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>自我描述的程序集</B>。.NET Framework 程序集包含元数据，以描述引用的所有程序集的版本信息、类型、资源和详细信息（以及其他内容）。这意味着它们不依赖于注册表。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>版本控制和并列支持</B>。.NET Framework 对于版本控制具有大量的支持，允许您安装多个版本的应用程序和多个版本的 .NET Framework，以便它们能够并列运行。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>相互隔离的应用程序</B>。可以将 .NET Framework 程序集部署到应用程序目录，以供该特定应用程序使用，并且默认情况下将其与其他应用程序单独保存并隔离。这意味着不需要将程序集部署到 Windows 目录或者将其显式注册到注册表中，从而降低了在安装其他应用程序时改写或删除这些程序集的可能性。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>全局程序集缓存</B>。如果您希望在同一台计算机上的不同应用程序之间共享代码，则可以将组件部署到全局程序集缓存。全局程序集缓存允许同一程序集的不同版本共存。在引用全局程序集缓存中的程序集时，必须指定程序集的完全限定名，包括公钥标记和版本号。这有助于防止无意中使用组件的不同版本。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>针对具有强名称的程序集的编译时程序集的默认运行时绑定</B>。默认情况下，如果程序集具有强名称，则 .NET Framework 会将其绑定到其从属程序集的确切版本。这会降低应用程序脆弱性，因为 .NET Framework 加载的是构建和测试该应用程序时的确切的程序集版本。如果需要，可以显式覆盖该行为。 </P></TD></TR></TBODY></TABLE>
<P>总而言之，上述更改有助于解决许多过去曾经困扰胖客户端应用程序的部署和维护的基础问题。有关 .NET Framework 如何简化部署的详细信息，请参阅“Simplifying Deployment and Solving DLL Hell with the .NET Framework”，网址为：<A href="http://msdn.microsoft.com/library/en-us/dndotnet/html/dplywithnet.asp" target=_blank><FONT color=#002c99>http://msdn.microsoft.com/library/en-us/dndotnet/html/dplywithnet.asp</FONT></A>。</P>
<P>本章介绍用于部署 .NET Framework 本身的选择，然后分析如何部署基于 .NET Framework 的智能客户端应用程序。有许多用于部署应用程序的选择，本章讨论了每个选择，并随后讨论了如何选择最适合您的环境的方法。最后，本章比较详细地分析了用于部署应用程序更新的选择。</P><A name=EGAA></A>
<H2>部署 .NET Framework</H2>
<P>.NET 智能客户端应用程序依靠 .NET Framework 工作，因此要求在客户端计算机上部署 .NET Framework。.NET Framework 是使用 .NET Framework Redistributable Package 部署的，后者可从 Microsoft MSDN? 或 Windows Update Web 站点获得。 </P>
<P>您还可以从产品 CD 或 DVD 获得该 Redistributable Package。该软件包在 .NET Framework SDK 和 Microsoft Visual Studio? .NET 2003 DVD 上提供。</P>
<P>.NET Framework Redistributable Package 实际上是一个 Windows 安装程序软件包，它被包装到一个名为 Dotnetfx.exe 的自解压缩可执行文件中。Dotnetfx.exe 可执行文件启动 Install.exe，后者执行平台检查，根据需要安装 Windows 安装程序 2.0 版，然后启动 Windows 安装程序软件包（.msi 文件）。</P>
<P>有关使用 Dotnetfx.exe 的详细信息，请参阅“.NET Framework Redistributable Package 1.1 Technical Reference”，网址为：<A href="http://msdn.microsoft.com/library/en-us/dnnetdep/html/dotnetfxref1_1.asp" target=_blank><FONT color=#002c99>http://msdn.microsoft.com/library/en-us/dnnetdep/html/dotnetfxref1_1.asp</FONT></A>。</P>
<DIV style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 10px"><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter7DeployUpdSCApp.mspx#top"><IMG height=9 alt=返回页首 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_up.gif" width=7 border=0></A><A class=topOfPage href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter7DeployUpdSCApp.mspx#top"><FONT color=#002c99>返回页首</FONT></A></DIV><A name=EFAA></A>
<H2>预先安装 .NET Framework</H2>
<P>如今，许多企业都选择将 .NET Framework 作为其标准操作环境的一部分进行部署。您可以用两种方法在整个企业中部署 .NET Framework： </P>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>使用将软件</B><B>“</B><B>推</B><B>”</B><B>到客户端计算机的技术</B>，如 Microsoft Active Directory? 目录服务的“组策略”功能或 Microsoft Systems Management Server (SMS)。使用“组策略”软件部署通过网络安装软件包，您可以确保用提升的特权安装该软件包。同样，使用企业“推”技术（如 SMS），您可以用必需的权限安装 .NET Framework。要使用“组策略”或 SMS 安装 .NET Framework，首先需要从 dotnetfx.exe 中解压缩 Windows 安装程序文件。有关如何完成该任务的详细信息，请参阅“Redistributing the .NET Framework”，网址为：<A href="http://msdn.microsoft.com/library/en-us/dnnetdep/html/redistdeploy.asp" target=_blank><FONT color=#002c99>http://msdn.microsoft.com/library/en-us/dnnetdep/html/redistdeploy.asp</FONT></A>。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>要求最终用户自己部署</B><B> .NET Framework</B>，方法是使用 Windows Update，或者从网络共享、内部 Web 站点或 Microsoft Web 站点下载 .NET Framework 。最终用户将需要在其计算机上具有管理特权才能部署 .NET Framework，这是因为 .NET Framework Redistributable Package 安装程序要求有管理特权才能安装。 </P></TD></TR></TBODY></TABLE>
<H3>随应用程序一起安装 .NET Framework</H3>
<P>如果您无法确定哪些计算机预先安装了 .NET Framework，您可以选择仅在需要 .NET Framework 时（换句话说，就是在安装 .NET Framework 应用程序时）才安装它。当您不知道您将向其进行部署的计算机的确切软件配置（因此不知道是否预先安装了 .NET Framework）时，该方法尤其有用。例如，如果您是独立软件供应商 (ISV)，并且开发和打包智能客户端应用程序以便销售给形形色色的客户，则您可能不知道您的客户的计算机是否安装了 .NET Framework。</P>
<P>要确保 .NET Framework 随您的应用程序一起安装，可以使用 setup.exe 引导程序示例。该示例会检查是否已经安装了 .NET Framework，如果尚未安装，则会在安装应用程序之前安装 .NET Framework。 </P>
<P>有关使用 setup.exe 引导程序示例的详细信息，请参阅 Deploying .NET Framework-based Applications 的第 3 章，网址为：<A href="http://www.microsoft.com/downloads/details.aspx?FamilyId=5B7C6E2D-D03F-4B19-9025-6B87E6AE0DA6&amp;displaylang=en" target=_blank><FONT color=#002c99> http://www.microsoft.com/downloads/details.aspx?FamilyId=5B7C6E2D-D03F-4B19-9025-6B87E6AE0DA6&amp;displaylang=en </FONT></A>。 </P>
<DIV style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 10px"><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter7DeployUpdSCApp.mspx#top"><IMG height=9 alt=返回页首 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_up.gif" width=7 border=0></A><A class=topOfPage href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter7DeployUpdSCApp.mspx#top"><FONT color=#002c99>返回页首</FONT></A></DIV><A name=EEAA></A>
<H2>部署智能客户端应用程序</H2>
<P>当您设计智能客户端应用程序时，应该考虑将如何部署这些应用程序。只要有可能，您都应该努力将任何安装的系统影响降至最低限度。这样做使您可以更紧密地跟踪对应用程序进行的任何更改，并且减轻更新和卸载应用程序的问题。但是，有时您将需要执行更为复杂的安装，例如，当您重用非托管代码组件时，或者当您需要在注册表中安全地存储敏感数据时。 </P>
<P>当您部署智能客户端应用程序时，可以使用多种选择。这些选择包括： </P>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>无接触部署</B>。使用该方法时，您可以将文件复制到 Web 服务器，然后当用户单击相应的链接时，.NET Framework 会自动将应用程序及其从属程序集下载到客户端。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>带有应用程序更新存根的无接触部署</B>。使用该方法时，您可以使用无接触部署下载应用程序存根，该存根随后会将应用程序的其余部分下载到本地硬盘。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>从文件共享运行代码</B>。使用该方法时，您可以将文件复制到文件共享，然后从该共享中运行应用程序。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>Xcopy</B>。使用该方法时，您可以将文件直接复制到客户端。.NET Framework 允许应用程序及其所有从属程序集位于单个目录结构中，因此您无须在客户端上注册任何组件。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>Windows </B><B>安装程序软件包</B>。使用该方法时，您可以将应用程序的文件打包到 Windows 安装程序软件包中，然后将该软件包安装到客户端上。 </P></TD></TR></TBODY></TABLE>
<P>每种方法都有其自己的优点和缺点。要帮助确定最适合您的环境的部署方法，您应该更加详细地分析每种方法。 </P>
<H3>无接触部署</H3>
<P>无接触部署使用户可以通过使用指向应用程序的 URL 链接来访问您的位于 Web 服务器上的应用程序。要使用无接触部署来部署应用程序，您只需要将适当的文件复制到 Web 服务器。当用户通过使用 URL 链接浏览到应用程序的位置时，Microsoft Internet Explorer 将下载并运行该应用程序。应用程序及其从属程序集将被使用 HTTP 下载到客户端，并且被存储在名为程序集下载缓存的特殊位置。当 .NET Framework 确定是否需要下载 Web 服务器上的程序集时，将只检查该文件上的日期-时间戳，而不会检查程序集版本号。如果服务器上的程序集所具有的日期-时间戳不比客户端上的晚，则不会下载这些程序集。</P>
<P>如果您使用无接触部署来部署您的智能客户端应用程序，则需要向用户提供指向 Web 服务器上的应用程序位置的 URL。使用该方法时，客户端计算机上不需要任何安装程序 — 所有代码都将根据需要下载。每当 Web 服务器上发生更改时，都会自动更新您的应用程序。如果文件已经更改，则会根据需要下载较新的版本，就像使用普通的 Web 浏览一样。</P>
<P>无接触部署依靠 .NET Framework 的能力与 Internet Explorer 5.01 或更高版本交互，以检查是否有所请求的 .NET 程序集。在请求期间，会将可执行文件下载到下载缓存中。然后，一个名为 <B>IEExec</B> 的进程将在 .NET Framework 的代码访问安全基础结构提供的安全隔离环境中启动应用程序。</P>
<P><B>注</B>客户端只有在同时安装了 .NET Framework 以及 Internet Explorer 版本 5.01 或更高版本时，才会尝试运行该应用程序。</P>
<P>如果您决定利用无接触部署来部署使用应用程序配置文件的应用程序，则可能需要配置 Web 服务器目录以便允许下载该应用程序的配置文件，因为默认情况下未启用该功能。请确保仅启用从您的应用程序所在的目录下载配置文件的功能；否则，您可能启用下载专用配置文件的功能并且引入安全风险。</P>
<P><B>注</B>在使用无接触部署时，实际上会下载配置文件两次：第一次是在检查有无绑定信息时（例如，为了控制应用程序使用的组件的确切版本），第二次是在查找用户特定的配置信息时。</P>
<P>您可以从已经部署的应用程序内使用无接触部署，通过 <B>Assembly.LoadFrom()</B> 方法下载并运行代码。该技术可用于下载频繁更改的代码（如频繁更改的业务规则），或者提供其他某种功能的按需安装。 </P>
<P>您可以通过无接触部署运行应用程序的本地化版本。客户端计算机的当前区域性用于自动下载所需的适当资源程序集，以便提供应用程序的本地化版本。</P>
<P>您可以使用 Web 服务器提供的安全性机制来确保无接触部署应用程序的安全。例如，要将对应用程序的访问权限制到 Intranet 上的授权用户，您可以在 Web 服务器中的应用程序目录上启用 Windows 集成安全性。要允许所有用户访问该应用程序，您可以启用对应用程序的目录的匿名访问。</P>
<P><B>注</B>如果您的 Web 服务器不允许匿名访问或者使用 Windows 集成安全性对客户端进行身份验证，则您的应用程序可能无法下载配置文件。</P>
<H4>无接触部署的局限性</H4>
<P>对于部署简单的应用程序或者部署更为复杂的应用程序的组成部分而言，无接触部署可能很有用。但是，对于更为复杂的智能客户端应用程序的完整安装而言，它并不是适当的部署方法，原因有以下几点： </P>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>受限制的默认安全设置</B></P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>不可靠的脱机功能</B></P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>没有事务性安装</B></P></TD></TR></TBODY></TABLE>
<P><B>注</B>.NET Framework 版本 2.0 中的 ClickOnce 技术将无需在安装和运行应用程序之前手动对客户端进行安全策略更改。ClickOnce 将提供一种可配置的机制，以便在从 Web 服务器首次安装应用程序时，自动进行安全策略更改。ClickOnce 还将向智能客户端应用程序提供可靠的脱机功能，并且将使它们可以与 Windows 外壳程序完全集成。</P>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>本节详细分析无接触部署的限制。 </P></TD></TR></TBODY></TABLE>
<H4>受限制的默认安全设置</H4>
<P>代码访问安全根据应用程序提供的证据向应用程序授权。默认情况下，使用应用程序的位置（用于启动它的 URL）来确定授予它的权限。除非更改了客户端计算机上的本地安全策略，否则只在某种程度上信任无接触部署应用程序，这意味着只会将有限数量的权限授予它们。</P>
<P>默认情况下，使用无接触部署进行部署的智能客户端应用程序将无法执行下列操作： </P>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>向硬盘写（独立存储除外） </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>将程序集部署到全局程序集缓存 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>部署或使用非托管代码 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>部署要求注册或者进行其他注册表更改的组件 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>与 Windows 外壳程序（具体说来，就是 <B>Start</B> 菜单上的安装图标以及 Control Panel 中的 <B>Add or Remove Programs</B> 项目）集成 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>访问数据库 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>与任何其他客户端应用程序（如 Microsoft Office 应用程序）交互 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>访问 Web 服务或其他位于网络上但不位于部署应用程序的同一服务器上的资源 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>执行在与部署位置相关联的区域中定义的安全操作以外的其他安全操作 </P></TD></TR></TBODY></TABLE>
<P>如果您的应用程序要求比默认权限集更多的权限，并且您希望使用无接触部署，则您将必须修改客户端上的安全策略，以便授予应用程序正常工作所需的权限。在部署您的应用程序之前，需要将此类安全策略更改传播到客户端计算机（例如，使用“组策略”、Windows 安装程序软件包或批处理文件）。这些要求减少了无接触部署方法的一些好处。有关部署安全策略的详细信息，请参阅“.NET Framework Enterprise Security Policy Administration and Deployment”，网址为：<A href="http://msdn.microsoft.com/library/en-us/dnnetsec/html/entsecpoladmin.asp" target=_blank><FONT color=#002c99>http://msdn.microsoft.com/library/en-us/dnnetsec/html/entsecpoladmin.asp</FONT></A>。</P>
<P>当您设计应用程序时，应该确定您是否能够满足您的智能客户端应用程序的设计规范，并且遵守无接触部署应用程序的不完全信任要求。通常，无接触部署以及从文件共享运行代码提供了易于部署的解决方案，但是可能在某种程度上限制应用程序的功能，以至于它们对于许多智能客户端应用程序而言并不实用。不过，如果您的应用程序不要求任何附加权限，则无接触部署可能是应用程序的理想部署机制。</P>
<P>有关完全受信任的应用程序和不完全受信任的应用程序的详细信息，请参阅<A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter5SecuCons.mspx"><FONT color=#002c99>第 5 章：安全性考虑事项</FONT></A>。</P>
<H4>不可靠的脱机功能</H4>
<P>使用无接触部署来部署智能客户端应用程序的另一个问题是它们不能可靠地脱机工作。这一问题是由许多因素造成的： </P>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>程序集的延迟下载</B>。程序集被按需下载并存储在程序集下载缓存（它被作为 Internet Explorer 缓存的一部分进行管理）中。在某些情况下，当您联机运行应用程序时，您可能没有下载该应用程序的所有部分，这将影响应用程序在脱机时完全发挥作用的能力。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>程序集可能被删除</B>。因为程序集驻留在由 Internet Explorer 缓存管理的区域中，所以如果该缓存由于某种原因而被清除，则您的应用程序文件将被删除。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>应用程序依赖于</B><B> Internet Explorer </B><B>脱机设置</B>。在尝试脱机运行应用程序时，您必须将 Internet Explorer 设置为在脱机模式下运行，即使您的应用程序不是在 Internet Explorer 内部运行。而且，如果您确实具有连接，但 Internet Explorer 被无意中设置为脱机模式，则不会对服务器进行更新检查。 </P></TD></TR></TBODY></TABLE>
<H4>没有事务性安装</H4>
<P>在使用无接触部署时，根据需要将程序集下载到随时可能被清除的缓存中。因此，无法在任何时候都确保本地硬盘上装有所有必要的代码。对于许多组织而言，这种不确定性可能是业务线应用程序所不能接受的。</P>
<H4>带有应用程序更新存根的无接触部署</H4>
<P>使用无接触部署的主要问题之一是：默认情况下，应用程序从程序集下载缓存中运行，并且只受到不完全信任，除非修改了本地安全策略。这可能限制智能客户端应用程序的功能，包括它的在脱机环境下可靠工作的能力。一种避免该问题的方法是最初使用无接触部署来部署应用程序存根，后者接着自动将该应用程序的其余部分下载并安装到本地硬盘上。该存根将应用程序部署到硬盘上的指定位置，如“C:\Program Files”，因此不会受到 Internet Explorer 缓存的限制。当该应用程序运行时，因为它是从本地硬盘运行的，所以它将被授予完全信任权限，并且在工作时不会受到与不完全信任应用程序相关联的限制。应用程序更新存根还可用于确保在服务器上发生更改时可靠而自动地更新该应用程序。</P>
<P>如果您使用该方法部署您的应用程序，则需要确保修改客户端计算机的 .NET Framework 安全策略，使应用程序存根本身可以用足够的权限运行，以便将应用程序构件下载并存储到本地硬盘上。</P>
<P>设计应用程序更新存根可能非常复杂。为了帮助您，Microsoft 已经创建了 Updater Application Block，您可以将其用作设计您自己的自动更新解决方案的基础。设计 Updater Application Block 的目的是： </P>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>为 .NET Framework 应用程序实现基于“拉”机制的更新解决方案。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>使用加密验证技术，在使用应用程序更新之前验证它们的真实性。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>在没有用户干预的情况下执行后部署配置任务。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>帮助您编写能够自动将其本身更新为可用的最新版本的应用程序。 </P></TD></TR></TBODY></TABLE>
<P><B>图</B><B> 7.1</B> 显示了 Updater Application Block 的体系结构。</P>
<DIV style="WIDTH: 450px"><IMG height=351 alt=apupf01 src="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/art/apupf01.gif" width=450 border=0><BR>
<P class=figureCaption><B>图</B><B> 7.1 Updater Application Block </B><B>体系结构</B></P>
<DIV class=figureRule></DIV></DIV>
<P>有关 Updater Application Block 的详细信息，请参阅“Updater Application Block for .NET”，网址为：<A href="http://msdn.microsoft.com/library/en-us/dnbda/html/updater.asp" target=_blank><FONT color=#002c99>http://msdn.microsoft.com/library/en-us/dnbda/html/updater.asp</FONT></A>。</P>
<P>带有应用程序更新存根的无接触部署支持应用程序的事务性安装。Updater Application Block 可帮助确保应用程序成功地完整安装。要执行事务性安装，您将需要包含相应的代码，以便除了执行自动更新以外，还检查是否已经在本地硬盘上安装了所有代码。这些代码的形式可以是一个清单文件以及用于确定该清单中的每个文件都在本地硬盘上的代码。</P>
<P>通过将无接触部署与应用程序更新存根结合起来，您可以得到许多好处，如简化的部署和更新，以及在完全受信任的环境中运行您的应用程序的能力。上述好处使得这一混合式方法成为部署许多智能客户端应用程序的有用选择。但是，它并非在所有情况下都是理想的选择。您仍然需要向应用程序存根授予足够的权限，以使该存根能够下载应用程序的其余部分。而且，使用该方法安装的应用程序不提供 Windows 外壳程序集成（具体说来，就是与 <B>Start</B> 菜单或 Control Panel 中的 <B>Add or Remove Programs</B> 项目的集成），除非您将该功能构建到应用程序存根中。最后，部署和更新将仅发生在用户的安全上下文中。如果您的应用程序需要向注册表写入，或者需要向文件系统的某个您禁止用户访问的部分写入，则这一限制可能导致问题。</P>
<H4>从文件共享运行代码</H4>
<P>从文件共享运行代码类似于无接触部署，不同之处在于您向用户提供文件共享而不是 URL，以便其部署和运行应用程序。从文件共享运行的代码按需下载，并且在适当的时候执行。因为该代码是从网络运行的，所以它将作为不完全受信任的应用程序运行，而且，除非您更改了客户端上的安全策略，否则它通常从本地 Intranet 运行并且会获得本地 Intranet 权限集。</P>
<P>从文件共享运行代码具有无接触部署的许多优点和缺点，尽管代码不像无接触部署那样缓存在客户端上。因为从文件共享运行代码具有一些安全限制，所以该方法通常并不适合于部署智能客户端应用程序。</P>
<P><B>注</B>像无接触部署一样，您可以采用将从文件共享运行代码与自动更新存根结合起来的混合式方法。有关详细信息，请参阅本章前面的“带有应用程序更新存根的无接触部署”。</P>
<H4>Xcopy 部署</H4>
<P>Xcopy 部署要求将应用程序包含的所有文件复制到客户端计算机，以便运行该应用程序。智能客户端应用程序通常只包含位于目录层次结构中的一个或多个可执行文件、一个或多个 DLL 以及一个或多个配置文件。通过将上述所有文件复制到另一台计算机，您就实质上安装了该应用程序。要卸载该应用程序，只需将所有文件从计算机中删除即可。</P>
<P>如果为安装应用程序只需要修改文件系统，则 Xcopy 方法可能是最佳选择。但是，因为您无法对安装过程进行编程控制，所以 Xcopy 方法<I>不</I> 允许您执行下列操作： </P>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>将程序集部署到全局程序集缓存（以及维护引用） </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>部署 COM 对象 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>部署要求注册或者进行其他注册表更改的组件 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>与 Windows 外壳程序集成 </P></TD></TR></TBODY></TABLE>
<P>如果您的应用程序要求附加的安装步骤，则您或许会在复制文件之后手动执行上述步骤。例如，如果您需要修改注册表，则可以在目标计算机上编辑注册表或者导入 *.reg 文件，以确保使适当的设置就绪。如果您需要将程序集部署到全局程序集缓存，则可以使用带 <B>/ir</B> 开关的 Gacutil.exe 实用工具，以便将程序集安装到带有跟踪引用的全局程序集缓存中。可以通过使用 <B>/ur</B> 开关在卸载程序集时删除这些引用。</P>
<P><B>注</B>您还可以在 Windows 资源管理器中使用拖放操作将共享程序集移动到全局程序集缓存文件夹中。但是，您应该避免使用该方法，因为该方法没有实现引用计数。没有引用计数，其他应用程序的卸载例程可能导致您的应用程序所需的程序集被从全局程序集缓存中删除。</P>
<P>Xcopy 部署适合于某些智能客户端应用程序，但是，在许多情况下，因为需要执行一些附加步骤才能使应用程序正常工作，所以使得这一看起来简单的方法变得过于费力。</P>
<H4>Windows 安装程序软件包</H4>
<P>您可以将要安装的应用程序打包为 Windows 安装程序软件包。该方法使您能够不受限制地在目标计算机上安装任何应用程序，尽管应用程序在运行时会受到安装它的最终用户的安全上下文的限制。</P>
<P>Windows 安装程序软件包十分灵活和强大，因此您可以使用它们安装那些对客户端进行大量配置更改的非常复杂的应用程序。然而，它们也适合于那些具有简单得多的安装要求的应用程序。即使您已经将应用程序设计为在安装时对客户端具有最低限度的影响，您也应该考虑使用 Windows 安装程序软件包，因为它们通过在 <B>Start</B> 菜单和桌面上添加图标以及向 Control Panel 中的 <B>Add or Remove Programs</B> 项目中添加应用程序，与 Windows 外壳程序集成。这一集成使您能够有效地控制安装以及在需要时卸载应用程序。</P>
<P>您可以将下列组件中的任意组件或全部组件添加到 Windows 安装程序软件包： </P>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>项目输出组 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>服务文件和文件夹 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>程序集 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>应用程序资源 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>合并模块 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>CAB 文件 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>依赖项 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>注册表设置 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>项目属性 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>自定义操作 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>用户界面设计设置</P></TD></TR></TBODY></TABLE>
<P>在您创建 Windows 安装程序软件包之后，您会具有多种用于将它分发到客户端计算机的选择，包括： </P>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>使用企业“推”技术，如 SMS。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>使用 Active Directory 的“组策略”功能发布或分配该软件包。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>允许用户从媒体、文件共享或 URL 安装该软件包。 </P></TD></TR></TBODY></TABLE>
<P>通过使用“推”技术来安装您的 Windows 安装程序软件包，您可以对安装何时发生以及在何处发生拥有某种集中式控制。它还使您能够控制企业内的哪些组应该拥有该应用程序或该应用程序的特定版本。例如，您可以确保安装针对特定的用户组在一天中的特定时间发生。但是，请记住，您可能需要大量的硬件和网络带宽（具体取决于应用程序的大小）来确保大规模部署能够高效进行。</P>
<P>Windows 安装程序软件包的最重要的优点之一是：如果您使用“组策略”或 SMS，则不需要用户具有管理权限就可以安装应用程序。Windows 安装程序软件包还自动支持事务性安装。只有两种可能：Windows 安装程序软件包将安装所有文件和配置更改；或者，如果出现了什么问题，则 Windows 安装程序将回滚整个安装。</P>
<P>Windows 安装程序软件包的灵活性意味着它适合于具有任何复杂性的安装：从简单地写文件系统以及与 Control Panel 中的 <B>Add or Remove Programs</B> 项目集成的应用程序，到那些对客户端进行许多重大配置更改的应用程序。</P>
<P><B>注如果您使用</B><B> Windows </B><B>安装程序软件包来部署您的应用程序，则无须使用同一方法来部署更新。</B><B>在许多情况下，将您的应用程序设计为在安装后自动更新自身是比较好的做法。有关如何配置应用程序以便自动进行更新的详细信息，请参阅本章后面的</B><B>“</B><B>自动更新</B><B>”</B><B>。</B></P>
<H3>选择正确的部署方法</H3>
<P>既然有如此之多可用于智能客户端应用程序的部署选择，那么确定适合于您的环境的正确选择可能非常困难。但是，您的应用程序的要求以及您的用户的需要通常将决定最佳的方法。</P>
<P>下表概述了每种部署方法的特性。</P>
<P><B>表</B><B> 7.1 </B><B>智能客户端应用程序的部署方法</B></P>
<TABLE class=dataTable id=EBAEAA cellSpacing=0 cellPadding=0>
<THEAD>
<TR class=stdHeader vAlign=top>
<TD id=colEFBBAEAA></TD>
<TD id=colEEBBAEAA>无接触部署</TD>
<TD id=colEDBBAEAA>带有应用程序更新存根的无接触部署</TD>
<TD id=colECBBAEAA>从文件共享运行代码</TD>
<TD id=colEBBBAEAA>Xcopy 部署</TD>
<TD id=colEABBAEAA style="BORDER-RIGHT: #cccccc 1px solid">Windows 安装程序软件包</TD></TR></THEAD>
<TBODY>
<TR class=record vAlign=top>
<TD>
<P class=lastInCell><B>可靠的脱机访问</B></P></TD>
<TD>
<P class=lastInCell>否</P></TD>
<TD>
<P class=lastInCell>是</P></TD>
<TD>
<P class=lastInCell>否</P></TD>
<TD>
<P class=lastInCell>是</P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>是</P></TD></TR>
<TR class=evenRecord vAlign=top>
<TD>
<P class=lastInCell><B>完全信任应用程序功能</B></P></TD>
<TD>
<P class=lastInCell>要求客户端安全策略更改</P></TD>
<TD>
<P class=lastInCell>是</P></TD>
<TD>
<P class=lastInCell>要求客户端安全策略更改</P></TD>
<TD>
<P class=lastInCell>是</P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>是</P></TD></TR>
<TR class=record vAlign=top>
<TD>
<P class=lastInCell><B>非超级用户安装</B></P></TD>
<TD>
<P class=lastInCell>是</P></TD>
<TD>
<P class=lastInCell>取决于应用程序的要求</P></TD>
<TD>
<P class=lastInCell>是</P></TD>
<TD>是</TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>取决于应用程序的要求和应用程序分发机制</P></TD></TR>
<TR class=evenRecord vAlign=top>
<TD>
<P class=lastInCell><B>对系统影响小</B></P></TD>
<TD>
<P class=lastInCell>是</P></TD>
<TD>
<P class=lastInCell>取决于应用程序的要求</P></TD>
<TD>
<P class=lastInCell>是</P></TD>
<TD>
<P class=lastInCell>是</P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>取决于应用程序的要求</P></TD></TR>
<TR class=record vAlign=top>
<TD>
<P class=lastInCell><B>Windows </B><B>外壳程序集成</B></P></TD>
<TD>
<P class=lastInCell>否</P></TD>
<TD>
<P class=lastInCell>否</P></TD>
<TD>
<P class=lastInCell>否</P></TD>
<TD>
<P class=lastInCell>否</P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>是</P></TD></TR>
<TR class=evenRecord vAlign=top>
<TD>
<P class=lastInCell><B>无限制的安装</B></P></TD>
<TD>
<P class=lastInCell>否</P></TD>
<TD>
<P class=lastInCell>否</P></TD>
<TD>
<P class=lastInCell>否</P></TD>
<TD>
<P class=lastInCell>否</P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>是</P></TD></TR>
<TR class=record vAlign=top>
<TD>
<P class=lastInCell><B>事务性安装</B></P></TD>
<TD>
<P class=lastInCell>否</P></TD>
<TD>
<P class=lastInCell>是</P></TD>
<TD>
<P class=lastInCell>否</P></TD>
<TD>
<P class=lastInCell>否</P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>是</P></TD></TR>
<TR class=evenRecord vAlign=top>
<TD>
<P class=lastInCell><B>需要修改客户端的</B><B> .NET Framework </B><B>安全策略</B></P></TD>
<TD>
<P class=lastInCell>是 — 如果客户端需要在提升的权限下运行</P></TD>
<TD>
<P class=lastInCell>是 — 仅限于应用程序存根。</P></TD>
<TD>
<P class=lastInCell>是 — 如果客户端需要在提升的权限下运行</P></TD>
<TD>
<P class=lastInCell>否</P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>否</P></TD></TR></TBODY></TABLE>
<DIV class=dataTableBottomMargin></DIV>
<P>在许多情况下，最简单的方法是使用 Windows 安装程序软件包将您的应用程序打包。Windows 安装程序软件包具有高度的灵活性，使您可以安装具有任何复杂性的应用程序。如果您使用企业“推”技术（如“组策略”或 SMS）来部署您的 Windows 安装程序软件包，则还可以在管理安全上下文中安装该应用程序，而无需考虑用户的安全上下文。当您希望使用户可以通过单击 URL 来安装他们的应用程序时，则带有自动更新存根的无接触部署也是一个可行的选择，但是您将必须对目标计算机的本地安全策略进行更改，以确保您的应用程序存根应用程序可以在完全信任权限下运行。</P>
<DIV style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 10px"><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter7DeployUpdSCApp.mspx#top"><IMG height=9 alt=返回页首 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_up.gif" width=7 border=0></A><A class=topOfPage href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter7DeployUpdSCApp.mspx#top"><FONT color=#002c99>返回页首</FONT></A></DIV><A name=EDAA></A>
<H2>部署智能客户端应用程序</H2>
<P>在最初部署您的智能客户端应用程序之后，您的工作尚未完成。过一段时间之后，随着您升级应用程序功能并且修复缺陷或解决安全漏洞，应用程序将需要更新。</P>
<P>根据具体情况的不同，您可以使用也可以不使用与部署智能客户端应用程序的方法相同的方法来更新它。例如，如果您最初使用 Windows 安装程序软件包来部署应用程序，则可以使用自动更新来部署更新。您的环境的具体情况通常将决定哪种更新方法最为适合。</P>
<P>部署更新时的一个常见要求是能够联合更新基础结构，以便多个更新不用在由单个实体控制的单个服务器或服务器场上竞争。例如，如果某个 ISV 已经创建了一个在客户的整个企业中部署的智能客户端应用程序，并且该 ISV 发布了该应用程序的一个更新，则该企业可能希望首先在他们的标准操作环境中下载并测试该更新，然后再将其传播到整个企业中运行的所有计算机。通过联合更新基础结构，可以使这样做变得可能。例如，更新服务器可能位于负责从该 ISV 获取更新的客户站点上。在该企业内部运行的客户端可以从本地更新服务器获取更新，但前提是 IT 管理员予以批准。使用该方法，还可以通过减轻单个服务器或服务器场的负载，提高更新基础结构的性能和可伸缩性。</P>
<P>在部署应用程序的更新时，您具有下列选择： </P>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>无接触部署</B>。更新的程序集被添加到 Web 服务器，以供客户端自动下载。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>自动更新</B>。应用程序被配置为自动从服务器下载并安装更新。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>从文件共享获取更新</B>。更新的程序集被添加到网络共享，以供客户端自动下载。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>Xcopy </B><B>更新</B>。更新被直接复制到客户端。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>Windows </B><B>安装程序软件包部署</B>。更新了 Windows 安装程序软件包，创建了新的软件包，或者使用修补软件包更新客户端。 </P></TD></TR></TBODY></TABLE>
<P>对每个选择进行详细分析，以便您能够确定哪个选择最适合于您的环境，将是很有用的。</P>
<H3>无接触部署更新</H3>
<P>如果您已经使用无接触部署方法部署了简单应用程序或更为复杂的应用程序的组成部分，则通过在 Web 服务器上放置新文件即可更新这些程序集。在应用程序加载程序集之前，.NET Framework 会自动在本地以及在 Web 服务器上检查该程序集的时间戳，以便确定是否需要重新下载该程序集，或者是否可以只是从用户的程序集下载缓存中运行该程序集。</P>
<P><B>注无接触部署具有许多限制，使其不适合于部署大多数智能客户端应用程序。有关详细信息，请参阅本章前面的</B><B>“</B><B>无接触部署</B><B>”</B><B>。</B></P>
<P>尽管使用无接触部署方法发布更新通常非常简单，但您的客户端有可能在升级过程中由于缺少对事务性安装的支持而出现问题。如果您在客户端使用应用程序的过程中更新目录，则客户端最初可能下载旧代码，然后尝试下载自那时起已经更新的其他代码。这可能导致不可预知的结果，并且可能导致您的应用程序失败。该问题最简单的解决方案是将任何重要的更新都部署到 Web 服务器上的单独目录中，然后在部署完成后，将所有链接更改到这一新位置。</P>
<P><B>注如果您选择使用带有自动更新存根的无接触部署方法来部署您的应用程序，则请参阅下一节</B><B>“</B><B>自动更新</B><B>”</B><B>。</B></P>
<H3>自动更新</H3>
<P>在大多数情况下，修补、重新打包和更新应用程序的最佳方法是将更新基础结构构建到应用程序本身内。在此情况下，可以将客户端应用程序设计为自动从服务器下载并安装更新，并且由 IT 管理员将这些更新发布到服务器以供客户端获取。要达到此目的，您可以在应用程序中包含相应的代码以便该应用程序能够执行下列操作： </P>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>自动检查是否有更新。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>如果有更新则进行下载。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>通过应用这些更新来升级其自身。 </P></TD></TR></TBODY></TABLE>
<P>当您配置您的应用程序以自动更新时，确保将所有已更新的文件下载到客户端十分重要。当您更新具有强名称的程序集时，这一点尤其重要。调用具有强名称的程序集的程序集必须指定具有强名称的程序集的版本，因此如果您更新具有强名称的程序集，您还必须更新任何调用它们的程序集。</P>
<P>在配置事务性更新时，您可以使用代码来检查是否在本地安装了这些更新，并根据清单来验证它们。通常，您将决定在单独的目录中安装更新，然后或者在成功安装之后删除原始目录，或者保留原始目录不动以提供后备应用程序。</P>
<P>有关自动更新以及 Updater Application Block 的用法的详细信息，请参阅本章前面的“带有应用程序更新存根的无接触部署”。</P>
<P><B>注自动更新将通过</B><B> .NET Framework </B><B>版本</B><B> 2.0 </B><B>中的</B><B> ClickOnce </B><B>功能进行简化。作为部署清单的一部分，您将能够指定应用程序是否检查更新以及何时应该检查是否有更新，并且能够指定备用的更新位置。</B></P>
<H3>从文件共享获取更新</H3>
<P>您将程序集复制到文件共享时，则应用程序每次运行时，这些程序集都会被下载到客户端并且不会缓存。像无接触部署一样，要更新原来通过从文件共享运行代码部署的应用程序，只需将新代码添加到文件共享。然后，客户端在下次运行时将下载新代码。</P>
<H3>Xcopy 更新</H3>
<P>如果您原来使用文件复制技术分发您的应用程序，则您可能希望以相同的方式部署更新。无论原来的部署机制如何，当更新比较简单（如对配置文件进行的修改）时，文件复制仍然可能是更新应用程序的最有效方法之一。在这样的情况下，在部署更新时，只需复制新文件，并且删除任何不再需要的旧文件。</P>
<P>通常，您只需通过复制新版本的程序集以覆盖旧版本，更新专用程序集。然而，尽管您可以使用简单的复制操作对具有强名称的程序集进行初始部署，但您无法以这种方式更新具有强名称的程序集并且让您的应用程序（或其他程序集）自动使用该程序集。程序集的强名称存储在引用它的任何程序集的清单中，并且公共语言运行库 (CLR) 将某个具有强名称的程序集的不同版本视为完全不同的程序集。除非您另外指定，否则 CLR 会加载原来构建应用程序时所依据的具有强名称的程序集的同一版本。</P>
<H3>Windows 安装程序更新</H3>
<P>Windows 安装程序提供了一种用于更新 .NET Framework 应用程序的全面解决方案。它的几项功能经过专门设计，以便解决应用程序更新问题。</P>
<P>Windows 安装程序软件包内置了对版本控制的支持。如果您正确地对应用程序进行版本控制，则 Windows 安装程序软件包能够自动确保更新正确发生，并且您可以指定在安装新的应用程序时是否要删除以前版本的应用程序。</P>
<P>如果您要使用“推”技术（如 SMS）来部署这些更新，则您还可以控制哪些用户将获得更新以及他们何时获得这些更新。如果您要在更广泛地部署更新之前通过特定的用户组测试这些更新，则该功能尤其有用。</P>
<P>如果您计划使用 Windows 安装程序技术升级您的应用程序，则您具有三个用于实现该升级的选择： </P>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>生成修补软件包 (.msp) 并将其应用于当前安装的应用程序。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>更新现有的 Windows 安装程序文件。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>创建全新的 Windows 安装程序文件。 </P></TD></TR></TBODY></TABLE>
<P>通常，如果您要使用 Windows 安装程序来部署更新，则应该使用修补软件包或者更新现有的 Windows 安装程序文件。如果您创建全新的 Windows 安装程序文件，则 Microsoft Windows? 不会将该软件包识别为更新，并且 Windows 的升级管理功能将无法正常工作。但是，在某些情况下，更改是如此广泛，以至于您可能选择放弃该功能并创建新的 Windows 安装程序文件。</P>
<P><B>注有关使用</B><B> Windows </B><B>安装程序部署更新的详细信息，请参阅</B><B></B><I>Deploying .NET-Framework-Based Applications</I>，网址为：<A href="http://www.microsoft.com/downloads/details.aspx?FamilyId=5B7C6E2D-D03F-4B19-9025-6B87E6AE0DA6&amp;displaylang=en" target=_blank><FONT color=#002c99>http://www.microsoft.com/downloads/details.aspx?FamilyId=5B7C6E2D-D03F-4B19-9025-6B87E6AE0DA6&amp;displaylang=en</FONT></A>。</P>
<DIV style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 10px"><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter7DeployUpdSCApp.mspx#top"><IMG height=9 alt=返回页首 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_up.gif" width=7 border=0></A><A class=topOfPage href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter7DeployUpdSCApp.mspx#top"><FONT color=#002c99>返回页首</FONT></A></DIV><A name=ECAA></A>
<H2>选择正确的更新方法</H2>
<P>在某些情况下，您选择的更新方法由您为应用程序选择的部署方法限定。但是，最适当的方法通常由您要部署的更新的性质决定。例如，您可能只是复制新文件以覆盖旧文件，或者您可能希望更新的应用程序与旧应用程序并列运行。更新可能涉及到将新的程序集添加到全局程序集缓存，或者更改注册表中的配置信息。如果您要部署对具有强名称的程序集的更新，则更新将变得更为复杂，因为调用具有强名称的程序集的每个程序集都将在调用中使用版本号。</P>
<P>表 7.2 概述了可用于更新应用程序的选择以及每个选择所支持的功能。</P>
<P><B>表</B><B> 7.2 </B><B>智能客户端应用程序的更新方法</B></P>
<TABLE class=dataTable id=ECCAA cellSpacing=0 cellPadding=0>
<THEAD>
<TR class=stdHeader vAlign=top>
<TD id=colEFBCCAA></TD>
<TD id=colEEBCCAA>无接触部署更新</TD>
<TD id=colEDBCCAA>带有应用程序更新存根的自动更新</TD>
<TD id=colECBCCAA>从文件共享获取更新</TD>
<TD id=colEBBCCAA>Xcopy 更新</TD>
<TD id=colEABCCAA style="BORDER-RIGHT: #cccccc 1px solid">Windows 安装程序更新</TD></TR></THEAD>
<TBODY>
<TR class=record vAlign=top>
<TD>
<P class=lastInCell><B>非超级用户更新</B></P></TD>
<TD>
<P class=lastInCell>是</P></TD>
<TD>
<P class=lastInCell>取决于应用程序的要求</P></TD>
<TD>
<P class=lastInCell>是</P></TD>
<TD>
<P class=lastInCell>否</P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>取决于应用程序的要求和应用程序分发机制</P></TD></TR>
<TR class=evenRecord vAlign=top>
<TD>
<P class=lastInCell><B>集中式更新管理</B></P></TD>
<TD>
<P class=lastInCell>是</P></TD>
<TD>
<P class=lastInCell>是</P></TD>
<TD>
<P class=lastInCell>是</P></TD>
<TD>
<P class=lastInCell>否</P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>取决于应用程序分发机制</P></TD></TR>
<TR class=record vAlign=top>
<TD>
<P class=lastInCell><B>在运行应用程序时下载更新</B></P></TD>
<TD>
<P class=lastInCell>是</P></TD>
<TD>
<P class=lastInCell>是</P></TD>
<TD>
<P class=lastInCell>否</P></TD>
<TD>
<P class=lastInCell>否</P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>否</P></TD></TR>
<TR class=evenRecord vAlign=top>
<TD>
<P class=lastInCell><B>联合的更新基础结构</B></P></TD>
<TD>
<P class=lastInCell>否</P></TD>
<TD>
<P class=lastInCell>是</P></TD>
<TD>
<P class=lastInCell>否</P></TD>
<TD>
<P class=lastInCell>否</P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>是</P></TD></TR>
<TR class=record vAlign=top>
<TD>
<P class=lastInCell><B>逐个用户</B><B>/</B><B>组进行更新</B></P></TD>
<TD>
<P class=lastInCell>是</P></TD>
<TD>
<P class=lastInCell>是</P></TD>
<TD>
<P class=lastInCell>否</P></TD>
<TD>
<P class=lastInCell>否</P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>取决于应用程序分发机制</P></TD></TR>
<TR class=evenRecord vAlign=top>
<TD>
<P class=lastInCell><B>事务性更新</B></P></TD>
<TD>
<P class=lastInCell>否</P></TD>
<TD>
<P class=lastInCell>是</P></TD>
<TD>
<P class=lastInCell>否</P></TD>
<TD>
<P class=lastInCell>否</P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>是</P></TD></TR>
<TR class=record vAlign=top>
<TD>
<P class=lastInCell><B>内置的版本控制支持</B></P></TD>
<TD>
<P class=lastInCell>否</P></TD>
<TD>
<P class=lastInCell>否</P></TD>
<TD>
<P class=lastInCell>否</P></TD>
<TD>
<P class=lastInCell>否</P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>是</P></TD></TR></TBODY></TABLE>
<DIV class=dataTableBottomMargin></DIV>
<P>在许多情况下，自动更新是部署应用程序更新的最有效的方法。但是，在部署重大更新或涉及到对客户端进行复杂配置更改的更新时，您可能需要使用 Windows 安装程序（它也具有自动版本控制支持的好处）。</P>
<H3>小结</H3>
<P>部署智能客户端应用程序要比过去部署胖客户端应用程序容易得多，这要归功于 .NET Framework 所具有的功能。但是，要成功完成部署，您需要进行大量重要的选择，包括如何设计您的应用程序以便于部署，以及您为应用程序和 .NET Framework 本身选择哪种部署方法这两个方面。</P>
<P>在大多数情况下，部署应用程序的最佳选择是使用 Windows 安装程序软件包，或者使用无接触部署和应用程序更新存根的组合。您将需要考虑在部署应用程序之后如何有效地维护该应用程序以及部署更新。同样，在大多数情况下，最佳选择很可能是 Windows 安装程序或由应用程序本身控制的自动更新。</P>
<P><A href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnpag/html/SCAG-CH07.asp" target=_blank><FONT color=#002c99>转到原英文页面</FONT></A></P><img src ="http://www.blogjava.net/TrampEagle/aggbug/30146.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/TrampEagle/" target="_blank">TrampEagle</a> 2006-02-10 14:57 <a href="http://www.blogjava.net/TrampEagle/articles/30146.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>智能客户端体系结构与设计指南 第 6 章 — 使用多线程</title><link>http://www.blogjava.net/TrampEagle/articles/30145.html</link><dc:creator>TrampEagle</dc:creator><author>TrampEagle</author><pubDate>Fri, 10 Feb 2006 06:56:00 GMT</pubDate><guid>http://www.blogjava.net/TrampEagle/articles/30145.html</guid><wfw:comment>http://www.blogjava.net/TrampEagle/comments/30145.html</wfw:comment><comments>http://www.blogjava.net/TrampEagle/articles/30145.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/TrampEagle/comments/commentRss/30145.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/TrampEagle/services/trackbacks/30145.html</trackback:ping><description><![CDATA[<H1><FONT size=4>原文引自：<A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter1Introduction.mspx">http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter1Introduction.mspx</A></FONT></H1>
<H1>&nbsp;</H1>
<H1>第 6 章 — 使用多线程</H1>
<H2 class=subtitle></H2>
<DIV class=date>发布日期： 08/20/2004<SPAN class=datePipe> | </SPAN>更新日期： 08/20/2004</DIV>
<DIV class=overview>
<DIV style="WIDTH: 250px"><IMG height=68 alt=pponline src="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/art/pponline.gif" width=250 border=0><BR>
<P class=figureCaption><A href="http://www.microsoft.com/resources/practices/default.mspx" target=_blank></A></P>
<DIV class=figureRule></DIV></DIV>
<P><B>智能客户端体系结构与设计指南</B></P>
<P>David Hill、Brenton Webster、Edward A. Jezierski、Srinath Vasireddy 和 Mohammad Al-Sabt，Microsoft Corporation；Blaine Wastell，Ascentium Corporation；Jonathan Rasmusson 和 Paul Gale，ThoughtWorks；以及 Paul Slater，Wadeware LLC</P>
<P><B>相关链接</B></P>
<P>Microsoft® <I>patterns &amp; practices</I> 库： <A href="http://www.microsoft.com/resources/practices/default.mspx" target=_blank><FONT color=#002c99>http://www.microsoft.com/resources/practices/default.mspx</FONT></A></P>
<P><I>.NET </I><I>的应用程序体系结构：设计应用程序和服务</I><A href="http://msdn.microsoft.com/library/en-us/dnbda/html/distapp.asp" target=_blank><FONT color=#002c99>http://msdn.microsoft.com/library/en-us/dnbda/html/distapp.asp</FONT></A></P>
<P><B>摘要</B>：本章讨论与智能客户端应用程序中多线程的使用有关的问题。为了最大限度地提高智能客户端应用程序的响应能力，需要仔细考虑如何和何时使用多线程。线程可以大大提高应用程序的可用性和性能，但是当您确定它们将如何与用户界面交互时，需要对其进行非常仔细的考虑。</P></DIV>
<CENTER><IMG title="" height=6 alt=* src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/3squares.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/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter6UsingMultThr.mspx#EHAA"><IMG height=9 alt=".NET Framework 中的多线程处理" hspace=4 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_down.gif" width=7 vspace=2 border=0></A></TD>
<TD class=onThisPage><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter6UsingMultThr.mspx#EHAA"><FONT color=#002c99>.NET Framework 中的多线程处理</FONT></A></TD></TR>
<TR vAlign=top>
<TD><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter6UsingMultThr.mspx#EGAA"><FONT color=#002c99><IMG height=9 alt=何时使用多线程 hspace=4 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_down.gif" width=7 vspace=2 border=0></FONT></A></TD>
<TD class=onThisPage><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter6UsingMultThr.mspx#EGAA"><FONT color=#002c99>何时使用多线程</FONT></A></TD></TR>
<TR vAlign=top>
<TD><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter6UsingMultThr.mspx#EFAA"><FONT color=#002c99><IMG height=9 alt=创建和使用线程 hspace=4 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_down.gif" width=7 vspace=2 border=0></FONT></A></TD>
<TD class=onThisPage><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter6UsingMultThr.mspx#EFAA"><FONT color=#002c99>创建和使用线程</FONT></A></TD></TR>
<TR vAlign=top>
<TD><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter6UsingMultThr.mspx#EEAA"><FONT color=#002c99><IMG height=9 alt="使用任务处理 UI 线程和其他线程之间的交互" hspace=4 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_down.gif" width=7 vspace=2 border=0></FONT></A></TD>
<TD class=onThisPage><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter6UsingMultThr.mspx#EEAA"><FONT color=#002c99>使用任务处理 UI 线程和其他线程之间的交互</FONT></A></TD></TR>
<TR vAlign=top>
<TD><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter6UsingMultThr.mspx#EDAA"><FONT color=#002c99><IMG height=9 alt=小结 hspace=4 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_down.gif" width=7 vspace=2 border=0></FONT></A></TD>
<TD class=onThisPage><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter6UsingMultThr.mspx#EDAA"><FONT color=#002c99>小结</FONT></A></TD></TR></TBODY></TABLE>
<P>线程是基本执行单元。单线程执行一系列应用程序指令，并且在应用程序中从头到尾都经由单一的逻辑路径。所有的应用程序都至少有一个线程，但是您可以将它们设计成使用多线程，并且每个线程执行一个单独的逻辑。在应用程序中使用多线程，可以将冗长的或非常耗时的任务放在后台处理。即使在只有单处理器的计算机上，使用多线程也可以非常显著地提高应用程序的响应能力和可用性。</P>
<P>使用多线程来开发应用程序可能非常复杂，特别是当您没有仔细考虑锁定和同步问题时。当开发智能客户端应用程序时，需要仔细地评估应该在何处使用多线程和如何使用多线程，这样就可以获得最大的好处，而无需创建不必要的复杂并难于调试的应用程序。</P>
<P>本章研究对于开发多线程智能客户端应用程序最重要的一些概念。它介绍了一些值得推荐的在智能客户端应用程序中使用多线程的方法，并且描述了如何实现这些功能。</P><A name=EHAA></A>
<H2>.NET Framework 中的多线程处理</H2>
<P>所有的 .NET Framework 应用程序都是使用单线程创建的，单线程用于执行该应用程序。在智能客户端应用程序中，这样的线程创建并管理用户界面 (UI)，因而称为 UI 线程。</P>
<P>可以将 UI 线程用于所有的处理，其中包括 Web 服务调用、远程对象调用和数据库调用。然而，以这种方式使用 UI 线程通常并<I>不是</I> 一个好主意。在大多数情况下，您不能预测调用 Web 服务、远程对象或数据库会持续多久，而且在 UI 线程等待响应时，您可能会导致 UI 冻结。</P>
<P>通过创建附加线程，应用程序可以在不使用 UI 线程的情况下执行额外的处理。当应用程序调用 Web 服务时，可以使用多线程来防止 UI 冻结或并行执行某些本地任务，以整体提高应用程序的效率。在大多数情况下，您应该坚持在单独的线程上执行任何与 UI 无关的任务。</P>
<H3>同步和异步调用之间的选择</H3>
<P>应用程序既可以进行同步调用，也可以进行异步调用。<I>同步</I> 调用在继续之前等待响应或返回值。如果不允许调用继续，就说调用被<I>阻塞</I> 了。</P>
<P><I>异步</I> 或<I>非阻塞</I> 调用不等待响应。异步调用是通过使用单独的线程执行的。原始线程启动异步调用，异步调用使用另一个线程执行请求，而与此同时原始的线程继续处理。</P>
<P>对于智能客户端应用程序，将 UI 线程中的同步调用减到最少非常重要。在设计智能客户端应用程序时，应该考虑应用程序将进行的每个调用，并确定同步调用是否会对应用程序的响应和性能产生负面影响。</P>
<P>仅在下列情况下，使用 UI 线程中的同步调用： </P>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>执行操纵 UI 的操作。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>执行不会产生导致 UI 冻结的风险的小的、定义完善的操作。 </P></TD></TR></TBODY></TABLE>
<P>在下列情况下，使用 UI 线程中的异步调用： </P>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>执行不影响 UI 的后台操作。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>调用位于网络的其他系统或资源。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>执行可能花费很长时间才能完成的操作。 </P></TD></TR></TBODY></TABLE>
<H3>前台线程和后台线程之间的选择</H3>
<P>.NET Framework 中的所有线程都被指定为前台线程或后台线程。这两种线程唯一的区别是 — 后台线程不会阻止进程终止。在属于一个进程的所有前台线程终止之后，公共语言运行库 (CLR) 就会结束进程，从而终止仍在运行的任何后台线程。</P>
<P>在默认情况下，通过创建并启动新的 <B>Thread</B> 对象生成的所有线程都是前台线程，而从非托管代码进入托管执行环境中的所有线程都标记为后台线程。然而，通过修改 <B>Thread.IsBackground</B> 属性，可以指定一个线程是前台线程还是后台线程。通过将 <B>Thread.IsBackground</B> 设置为 <B>true</B>，可以将一个线程指定为后台线程；通过将 <B>Thread.IsBackground</B> 设置为 <B>false</B>，可以将一个线程指定为前台线程。</P>
<P><B>注</B>有关 Thread 对象的详细信息，请参阅本章后面的“使用 Thread 类”部分<B>。</B></P>
<P>在大多数应用程序中，您会选择将不同的线程设置成前台线程或后台线程。通常，应该将被动侦听活动的线程设置为后台线程，而将负责发送数据的线程设置为前台线程，这样，在所有的数据发送完毕之前该线程不会被终止。</P>
<P>只有在确认线程被系统随意终止没有不利影响时，才应该使用后台线程。如果线程正在执行必须完成的敏感操作或事务操作，或者需要控制关闭线程的方式以便释放重要资源，则使用前台线程。</P>
<H3>处理锁定和同步</H3>
<P>有时在构建应用程序时，创建的多个线程都需要同时使用关键资源（例如数据或应用程序组件）。如果不仔细，一个线程就可能更改另一个线程正在使用的资源。其结果可能就是该资源处于一种不确定的状态并且呈现为不可用。这称为 <I>争用情形</I>。在没有仔细考虑共享资源使用的情况下使用多线程的其他不利影响包括：死锁、线程饥饿和线程关系问题。</P>
<P>为了防止这些影响，当从两个或多个线程访问一个资源时，需要使用锁定和同步技术来协调这些尝试访问此资源的线程。</P>
<P>使用锁定和同步来管理线程访问共享资源是一项复杂的任务，只要有可能，就应该通过在线程之间传送数据而不是提供对单个实例的共享访问来避免这样做。</P>
<P>假如不能排除线程之间的资源共享，则应该： </P>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>使用 Microsoft Visual C# 中的 <B>lock</B> 语句和 Microsoft Visual Basic .NET 中的 <B>SyncLock</B> 语句来创建临界区，但要小心地从临界区内调用方法来防止死锁。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>使用 <B>Synchronized</B> 方法获得线程安全的 .NET 集合。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>使用 <B>ThreadStatic</B> 属性创建逐线程成员。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>使用重新检查 (double-check) 锁或 <B>Interlocked.CompareExchange</B> 方法来防止不必要的锁定。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>确保静态声明是线程安全的。 </P></TD></TR></TBODY></TABLE>
<P>有关锁定和同步技术的详细信息，请参阅 <A href="http://msdn.microsoft.com/library/en-us/cpgenref/html/cpconthreadingdesignguidelines.asp" target=_blank><FONT color=#002c99>http://msdn.microsoft.com/library/en-us/cpgenref/html/cpconthreadingdesignguidelines.asp</FONT></A> 上的 <I>.NET Framework General Reference</I> 中的“Threading Design Guidelines”。</P>
<H3>使用计时器</H3>
<P>在某些情况下，可能不需要使用单独的线程。如果应用程序需要定期执行简单的与 UI 有关的操作，则应该考虑使用进程计时器。有时，在智能客户端应用程序中使用进程计时器，以达到下列目： </P>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>按计划定期执行操作。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>在使用图形时保持一致的动画速度（而不管处理器的速度）。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>监视服务器和其他的应用程序以确认它们在线并且正在运行。 </P></TD></TR></TBODY></TABLE>
<P>.NET Framework 提供三种进程计时器： </P>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>System.Window.Forms.Timer</B></P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>System.Timers.Timer</B></P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>System.Threading.Timer</B></P></TD></TR></TBODY></TABLE>
<P>如果想要在 Windows 窗体应用程序中引发事件，<B>System.Window.Forms.Timer</B> 就非常有用。它经过了专门的优化以便与 Windows 窗体一起使用，并且必须用在 Windows 窗体中。它设计成能用于单线程环境，并且可以在 UI 线程上同步操作。这就意味着该计时器从来不会抢占应用程序代码的执行（假定没有调用 <B>Application.DoEvents</B>），并且对与 UI 交互是安全的。</P>
<P><B>System.Timers.Timer</B> 被设计并优化成能用于多线程环境。与 <B>System.Window.Forms.Timer</B> 不同，此计时器调用从 CLR 线程池中获得的辅助线程上的事件处理程序。在这种情况下，应该确保事件处理程序不与 UI 交互。<B>System.Timers.Timer</B> 公开了可以模拟 <B>System.Windows.Forms.Timer</B> 中的行为的 <B>SynchronizingObject</B> 属性，但是除非需要对事件的时间安排进行更精确的控制，否则还是应该改为使用 <B>System.Windows.Forms.Timer</B>。</P>
<P><B>System.Threading.Timer</B> 是一个简单的轻量级服务器端计时器。它并不是内在线程安全的，并且使用起来比其他计时器更麻烦。此计时器通常不适合 Windows 窗体环境。</P>
<P>表 6.1 列出了每个计时器的各种属性。</P>
<P><B>表</B><B> 6.1 </B><B>进程计时器属性</B></P>
<TABLE class=dataTable id=EAAHAA cellSpacing=0 cellPadding=0>
<THEAD>
<TR class=stdHeader vAlign=top>
<TD id=colEDBAAHAA>属性</TD>
<TD id=colECBAAHAA>System.Windows.Forms</TD>
<TD id=colEBBAAHAA>System.Timers</TD>
<TD id=colEABAAHAA style="BORDER-RIGHT: #cccccc 1px solid">System.Threading</TD></TR></THEAD>
<TBODY>
<TR class=record vAlign=top>
<TD>
<P class=lastInCell>计时器事件运行在什么线程中？</P></TD>
<TD>
<P class=lastInCell>UI 线程</P></TD>
<TD>
<P class=lastInCell>UI 线程或辅助线程</P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>辅助线程</P></TD></TR>
<TR class=evenRecord vAlign=top>
<TD>
<P class=lastInCell>实例是线程安全的吗？</P></TD>
<TD>
<P class=lastInCell>否</P></TD>
<TD>
<P class=lastInCell>是</P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>否</P></TD></TR>
<TR class=record vAlign=top>
<TD>
<P class=lastInCell>需要 Windows 窗体吗？</P></TD>
<TD>
<P class=lastInCell>是</P></TD>
<TD>
<P class=lastInCell>否</P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>否</P></TD></TR>
<TR class=evenRecord vAlign=top>
<TD>
<P class=lastInCell>最初的计时器事件可以调度吗？</P></TD>
<TD>
<P class=lastInCell>否</P></TD>
<TD>
<P class=lastInCell>否</P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>是</P></TD></TR></TBODY></TABLE>
<DIV class=dataTableBottomMargin></DIV>
<DIV style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 10px"><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter6UsingMultThr.mspx#top"><IMG height=9 alt=返回页首 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_up.gif" width=7 border=0></A><A class=topOfPage href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter6UsingMultThr.mspx#top"><FONT color=#002c99>返回页首</FONT></A></DIV><A name=EGAA></A>
<H2>何时使用多线程</H2>
<P>在许多常见的情况下，可以使用多线程处理来显著提高应用程序的响应能力和可用性。</P>
<P>应该慎重考虑使用多线程来： </P>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>通过网络（例如，与 Web 服务器、数据库或远程对象）进行通信。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>执行需要较长时间因而可能导致 UI 冻结的本地操作。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>区分各种优先级的任务。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>提高应用程序启动和初始化的性能。 </P></TD></TR></TBODY></TABLE>
<P>非常详细地分析这些使用情况是非常有用的。</P>
<H3>通过网络进行通信</H3>
<P>智能客户端可以采用许多方式通过网络进行通信，其中包括： </P>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>远程对象调用，例如，DCOM、RPC 或 .NET 远程处理 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>基于消息的通信，例如，Web 服务调用和 HTTP 请求。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>分布式事务处理。 </P></TD></TR></TBODY></TABLE>
<P>许多因素决定了网络服务对应用程序请求的响应速度，其中包括请求的性质、网络滞后时间、连接的可靠性和带宽、单个服务或多个服务的繁忙程度。</P>
<P>这种不可预测性可能会引起单线程应用程序的响应问题，而多线程处理常常是一种好的解决方案。应该为网络上的所有通信创建针对 UI 线程的单独线程，然后在接收到响应时将数据传送回 UI 线程。</P>
<P>为网络通信创建单独的线程并不总是必要的。如果应用程序通过网络进行异步通信，例如使用 Microsoft Windows 消息队列（也称为 MSMQ），则在继续执行之前，它不会等待响应。然而，即使在这种情况下，您仍然应该使用单独的线程来侦听响应，并且在响应到达时对其进行处理。</P>
<H3>执行本地操作</H3>
<P>即使在处理发生在本地的情况下，有些操作也可能花费很长时间，足以对应用程序的响应产生负面影响。这样的操作包括： </P>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>图像呈现。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>数据操纵。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>数据排序。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>搜索。 </P></TD></TR></TBODY></TABLE>
<P>不应该在 UI 线程上执行诸如此类的操作，因为这样做会引起应用程序中的性能问题。相反，应该使用额外的线程来异步执行这些操作，防止 UI 线程阻塞。</P>
<P>在许多情况下，也应该这样设计应用程序，让它报告正在进行的后台操作的进程和成功或失败。可能还会考虑允许用户取消后台操作以提高可用性。</P>
<H3>区分各种优先级的任务</H3>
<P>并不是应用程序必须执行的所有任务都具有相同的优先级。一些任务对时间要求很急，而一些则不是。在其他的情况中，您或许会发现一个线程依赖于另一个线程上的处理结果。</P>
<P>应该创建不同优先级的线程以反映正在执行的任务的优先级。例如，应该使用高优先级线程管理对时间要求很急的任务，而使用低优先级线程执行被动任务或者对时间不敏感的任务。</P>
<H3>应用程序启动</H3>
<P>应用程序在第一次运行时常常必须执行许多操作。例如，它可能需要初始化自己的状态，检索或更新数据，打开本地资源的连接。应该考虑使用单独的线程来初始化应用程序，从而使得用户能够尽快地开始使用该应用程序。使用单独的线程进行初始化可以增强应用程序的响应能力和可用性。</P>
<P>如果确实在单独的线程中执行初始化，则应该通过在初始化完成之后，更新 UI 菜单和工具栏按钮的状态来防止用户启动依赖于初始化尚未完成的操作。还应该提供清楚的反馈消息来通知用户初始化的进度。</P>
<DIV style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 10px"><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter6UsingMultThr.mspx#top"><IMG height=9 alt=返回页首 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_up.gif" width=7 border=0></A><A class=topOfPage href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter6UsingMultThr.mspx#top"><FONT color=#002c99>返回页首</FONT></A></DIV><A name=EFAA></A>
<H2>创建和使用线程</H2>
<P>在 .NET Framework 中有几种方法可以创建和使用后台线程。可以使用 <B>ThreadPool</B> 类访问由 .NET Framework 管理的给定进程的线程池，也可以使用 <B>Thread</B> 类显式地创建和管理线程。另外，还可以选择使用委托对象或者 Web 服务代理来使非 UI 线程上发生特定处理。本节将依次分析各种不同的方法，并推荐每种方法应该在何时使用。</P>
<H3>使用 ThreadPool 类</H3>
<P>到现在为止，您可能会认识到许多应用程序都会从多线程处理中受益。然而，线程管理并不仅仅是每次想要执行一个不同的任务就创建一个新线程的问题。有太多的线程可能会使得应用程序耗费一些不必要的系统资源，特别是，如果有大量短期运行的操作，而所有这些操作都运行在单独线程上。另外，显式地管理大量的线程可能是非常复杂的。</P>
<P>线程池化技术通过给应用程序提供由系统管理的辅助线程池解决了这些问题，从而使得您可以将注意力集中在应用程序任务上而不是线程管理上。 </P>
<P>在需要时，可以由应用程序将线程添加到线程池中。当 CLR 最初启动时，线程池没有包含额外的线程。然而，当应用程序请求线程时，它们就会被动态创建并存储在该池中。如果线程在一段时间内没有使用，这些线程就可能会被处置，因此线程池是根据应用程序的要求缩小或扩大的。</P>
<P><B>注</B>每个进程都创建一个线程池，因此，如果您在同一个进程内运行几个应用程序域，则一个应用程序域中的错误可能会影响相同进程内的其他应用程序域，因为它们都使用相同的线程池。</P>
<P>线程池由两种类型的线程组成： </P>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>辅助线程</B>。辅助线程是标准系统池的一部分。它们是由 .NET Framework 管理的标准线程，大多数功能都在它们上面执行。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>完成端口线程</B>.这种线程用于异步 I/O 操作（通过使用 IOCompletionPorts API）。 </P></TD></TR></TBODY></TABLE>
<P><B>注</B>，如果应用程序尝试在没有 IOCompletionPorts 功能的计算机上执行 I/O 操作，它就会还原到使用辅助线程。</P>
<P>对于每个计算机处理器，线程池都默认包含 25 个线程。如果所有的 25 个线程都在被使用，则附加的请求将排入队列，直到有一个线程变得可用为止。每个线程都使用默认堆栈大小，并按默认的优先级运行。</P>
<P>下面代码示例说明了线程池的使用。</P><PRE class=codeSample>private void ThreadPoolExample() 
{ 
    WaitCallback callback = new WaitCallback( ThreadProc ); 
    ThreadPool.QueueUserWorkItem( callback ); 
} 
</PRE>
<P>在前面的代码中，首先创建一个委托来引用您想要在辅助线程中执行的代码。.NET Framework 定义了 <B>WaitCallback</B> 委托，该委托引用的方法接受一个对象参数并且没有返回值。下面的方法实现您想要执行的代码。</P><PRE class=codeSample>private void ThreadProc( Object stateInfo ) 
{ 
    // Do something on worker thread. 
} 
</PRE>
<P>可以将单个对象参数传递给 <B>ThreadProc</B> 方法，方法是将其指定为 <B>QueueUserWorkItem</B> 方法调用中的第二个参数。在前面的示例中，没有给 <B>ThreadProc</B> 方法传递参数，因此 <B>stateInfo</B> 参数为空。</P>
<P>在下面的情况下，使用 <B>ThreadPool</B> 类： </P>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>有大量小的独立任务要在后台执行。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>不需要对用来执行任务的线程进行精细控制。 </P></TD></TR></TBODY></TABLE>
<H3>使用 Thread 类</H3>
<P>使用 <B>Thread</B> 类可以显式管理线程。这包括 CLR 创建的线程和进入托管环境执行代码的 CLR 以外创建的线程。CLR 监视其进程中曾经在 .NET Framework 内执行代码的所有线程，并且使用 <B>Thread</B> 类的实例来管理它们。</P>
<P>只要有可能，就应该使用 <B>ThreadPool</B> 类来创建线程。然而，在一些情况下，您还是需要创建并管理您自己的线程，而不是使用 <B>ThreadPool</B> 类。</P>
<P>在下面的情况下，使用 <B>Thread</B> 对象： </P>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>需要具有特定优先级的任务。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>有可能运行很长时间的任务（这样可能阻塞其他任务）。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>需要确保只有一个线程可以访问特定的程序集。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>需要有与线程相关的稳定标识。 </P></TD></TR></TBODY></TABLE>
<P><B>Thread</B> 对象包括许多属性和方法，它们可以帮助控制线程。可以设置线程的优先级，查询当前的线程状态，中止线程，临时阻塞线程，并且执行许多其他的线程管理任务。 </P>
<P>下面的代码示例演示了如何使用 <B>Thread</B> 对象创建并启动一个线程。 </P><PRE class=codeSample>static void Main()  
{ 
    System.Threading.Thread workerThread = 
        new System.Threading.Thread( SomeDelegate ); 
    workerThread.Start(); 
} 
public static void SomeDelegate () { Console.WriteLine( "Do some work." ); } 
</PRE>
<P>在这个示例中，SomeDelegate 是一个 <B>ThreadStart</B> 委托 — 指向将要在新线程中执行的代码的引用。<B>Thread.Start </B>向操作系统提交请求以启动线程。</P>
<P>如果采用这种方式实例化一个新线程，就不可能向 <B>ThreadStart</B> 委托传递任何参数。如果需要将一个参数传递给要在另一个线程中执行的方法，应该用所需的方法签名创建一个自定义委托并异步调用它。</P>
<P>有关自定义委托的详细信息，请参阅本章后面的“使用委托”部分。</P>
<P>如果需要从单独的线程中获得更新或结果，可以使用回调方法 — 一个委托，引用在线程完成工作之后将要调用的代码 — 这就使得线程可以与 UI 交互。有关详细信息，请参阅本章后面的“使用任务处理 UI 线程和其他线程之间的交互”部分。</P>
<H3>使用委托</H3>
<P>委托是指向方法的引用（或指针）。在定义委托时，可以指定确切的方法签名，如果其他的方法想要代表该委托，就必须与该签名相匹配。所有委托都可以同步和异步调用。</P>
<P>下面的代码示例展示了如何声明委托。这个示例展示了如何将一个长期运行的计算实现为一个类中的方法。</P><PRE class=codeSample>delegate string LongCalculationDelegate( int count ); 
</PRE>
<P>如果 .NET Framework 遇到像上面一样的委托声明，就隐式声明了一个从 <B>MultiCastDelegate</B> 类继承的隐藏类，正如下面的代码示例中所示。</P><PRE class=codeSample>Class LongCalculationDelegate : MutlicastDelegate 
{ 
    public string Invoke( count ); 
    public void BeginInvoke( int count, AsyncCallback callback, 
        object asyncState ); 
    public string EndInvoke( IAsyncResult result ); 
} 
</PRE>
<P>委托类型 <B>LongCalculationDelegate</B> 用于引用接受单个整型参数并返回一个字符串的方法。下面的代码示例举例说明了一个这种类型的委托，它引用带有相关签名的特定方法。</P><PRE class=codeSample>LongCalculationDelegate longCalcDelegate = 
            new LongCalculationDelegate( calculationMethod ); 
</PRE>
<P>在本示例中，<B>calculationMethod</B> 是实现您想要在单独线程上执行的计算的方法的名称。</P>
<P>可以同步或异步调用委托实例所引用的方法。为了同步调用它，可以使用下面的代码。</P><PRE class=codeSample>string result = longCalcDelegate( 10000 ); 
</PRE>
<P>该代码在内部使用上面的委托类型中定义的 <B>Invoke</B> 方法。因为 <B>Invoke</B> 方法是同步调用，所以此方法只在调用方法返回之后才返回。返回值是调用方法的结果。</P>
<P>更常见的情况是，为了防止调用线程阻塞，您将选择通过使用 <B>BeginInvoke</B> 和 B&gt;EndInvoke 方法来异步调用委托。异步委托使用 .NET Framework 中的线程池化功能来进行线程管理。.NET Framework 实现的标准<I>异步调用</I> 模式提供 <B>BeginInvoke</B> 方法来启动线程上所需的操作，并且它提供 <B>EndInvoke</B> 方法来允许完成异步操作以及将任何得到的数据传送回调用线程。在后台处理完成之后，可以调用回调方法，其中，可以调用 <B>EndInvoke</B> 来获取异步操作的结果。</P>
<P>当调用 <B>BeginInvoke</B> 方法时，它不会等待调用完成；相反，它会立即返回一个 <B>IAsyncResult </B>对象，该对象可以用来监视该调用的进度。可以使用 <B>IAsyncResult</B> 对象的 <B>WaitHandle</B> 成员来等待异步调用完成，或使用 <B>IsComplete</B> 成员轮询是否完成。如果在调用完成之前调用 <B>EndInvoke</B> 方法，它就会阻塞，并且只在调用完成之后才返回。然而，您应该慎重，不要使用这些技术来等待调用完成，因为它们可能阻塞 UI 线程。一般来说，回调机制是通知调用已经完成的最好方式。 </P>
<P>异步执行委托引用的方法</P>
<TABLE class=numberedList cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR vAlign=top>
<TD class=listNumber noWrap align=right>
<P>1.</P></TD>
<TD>
<P>定义代表长期运行的异步操作的委托，如下面的示例所示： </P><PRE class=codeSample>delegate string LongCalculationDelegate( int count ); 
</PRE></TD></TR>
<TR vAlign=top>
<TD class=listNumber noWrap align=right>
<P>2.</P></TD>
<TD>
<P>定义一个与委托签名相匹配的方法。下面的示例中的方法模拟需要消耗较多时间的操作，方法是使线程返回之前睡眠 <B>count</B> 毫秒。 </P><PRE class=codeSample>private string LongCalculation( int count ) 
{ 
    Thread.Sleep( count ); 
    return count.ToString(); 
} 
</PRE></TD></TR>
<TR vAlign=top>
<TD class=listNumber noWrap align=right>
<P>3.</P></TD>
<TD>
<P>定义与 .NET Framework 定义的 <B>AsyncCallback</B> 委托相对应的回调方法，如下面的示例所示。 </P><PRE class=codeSample>private void CallbackMethod( IAsyncResult ar ) 
{ 
    // Retrieve the invoking delegate. 
    LongCalculationDelegate dlgt = (LongCalculationDelegate)ar.AsyncState; 
    // Call EndInvoke to retrieve the results. 
    string results = dlgt.EndInvoke(ar); 
} 
</PRE></TD></TR>
<TR vAlign=top>
<TD class=listNumber noWrap align=right>
<P>4.</P></TD>
<TD>
<P>创建一个委托实例，它引用您想要异步调用的方法，并且创建一个 <B>AsyncCallback</B> 委托来引用回调方法，如下面的代码示例所示。 </P><PRE class=codeSample>    LongCalculationDelegate longCalcDelegate = 
            new LongCalculationDelegate( calculationMethod ); 
    AsyncCallback callback = new AsyncCallback( CallbackMethod ); 
</PRE></TD></TR>
<TR vAlign=top>
<TD class=listNumber noWrap align=right>
<P>5.</P></TD>
<TD>
<P>从调用线程中开始异步调用，方法是调用引用您想要异步执行的代码的委托中的 <B>BeginInvoke</B> 方法。 </P><PRE class=codeSample>    longCalcDelegate.BeginInvoke( count, callback, longCalcDelegate ); 
</PRE>
<P>方法 <B>LongCalculation</B> 是在辅助线程上调用的。当它完成时，就调用 <B>CallbackMethod</B> 方法，并且获取计算的结果。 </P>
<P><B>注</B>回调方法是在非 UI 线程上执行的。要修改 UI，需要使用某些技术来从该线程切换到 UI 线程。有关详细信息，请参阅本章后面的“使用任务处理 UI 线程和其他线程之间的交互”部分<B>。</B></P></TD></TR></TBODY></TABLE>
<P>可以使用自定义委托来将任意参数传送给要在单独的线程上执行的方法（有时当您直接使用 <B>Thread</B> 对象或线程池创建线程时，您无法这样做）。</P>
<P>当需要在应用程序 UI 中调用长期运行的操作时，异步调用委托非常有用。如果用户在 UI 中执行预期要花很长时间才能完成的操作，您肯定并不希望该 UI 冻结，也不希望它不能刷新自己。使用异步委托，可以将控制权返回给主 UI 线程以执行其他操作。</P>
<P>在以下情况中，您应该使用委托来异步调用方法： </P>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>需要将任意参数传递给您想要异步执行的方法。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>您想要使用 .NET Framework 提供的<I>异步调用</I> 模式。 </P></TD></TR></TBODY></TABLE>
<P><B>注</B>有关如何使用 BeginInvoke 和 EndInvoke 进行异步调用的详细信息，请参阅<A href="http://msdn.microsoft.com/library/en-us/cpguide/html/cpovrasynchronousprogrammingoverview.asp" target=_blank><B><FONT color=#002c99>http://msdn.microsoft.com/library/en-us/cpguide/html/cpovrasynchronousprogrammingoverview.asp</FONT></B></A> 上的 <I>.NET Framework Developer's Guide</I> 中的“Asynchronous Programming Overview”。</P>
<H3>异步调用 Web 服务</H3>
<P>应用程序常常使用 Web 服务与网络资源进行通信。一般来说，不应该从 UI 线程同步调用 Web 服务，这是因为 Web 服务调用的响应时间变化很大，正如网络上所有交互的响应时间的情况一样。相反，应该从客户端异步调用所有的 Web 服务。</P>
<P>要了解如何异步调用 Web 服务，可以考虑使用下面简单的 Web 服务，它睡眠一段时间，然后返回一个字符串，指示它已经完成了它的操作。</P><PRE class=codeSample>[WebMethod] 
public string ReturnMessageAfterDelay( int delay ) 
{ 
    System.Threading.Thread.Sleep(delay); 
    return "Message Received"; 
} 
</PRE>
<P>当在 Microsoft Visual Studio .NET 开发系统中引用 Web 服务时，它会自动生成一个代理。代理是一个类，它允许使用 .NET Framework 实现的<I>异步调用</I> 模式异步调用 Web 服务。如果您分析一下生成的代理，您就会看到下面三个方法。</P><PRE class=codeSample>public string ReturnMessageAfterDelay( int delay ) 
{ 
    object[] results = this.Invoke( "ReturnMessageAfterDelay", 
                                    new object[] {delay} ); 
    return ((string)(results[0])); 
} 
public System.IAsyncResult BeginReturnMessageAfterDelay( int delay, 
                          System.AsyncCallback callback, object asyncState ) 
{ 
    return this.BeginInvoke( "ReturnMessageAfterDelay", 
                             new object[] {delay}, callback, asyncState ); 
} 
public string EndReturnMessageAfterDelay( System.IAsyncResult asyncResult ) 
{ 
      object[] results = this.EndInvoke( asyncResult ); 
      return ((string)(results[0])); 
} 
</PRE>
<P>第一个方法是调用 Web 服务的同步方法。第二个和第三个方法是异步方法。可以如下所示异步调用 Web 服务。</P><PRE class=codeSample>private void CallWebService() 
{ 
    localhost.LongRunningService serviceProxy = 
                    new localhost.LongRunningService(); 
    AsyncCallback callback = new AsyncCallback( Completed ); 
    serviceProxy.BeginReturnMessageAfterDelay( callback, serviceProxy, null ); 
} 
</PRE>
<P>这个示例非常类似于使用自定义委托的异步回调示例。当 Web 服务返回时，用将要调用的方法定义一个 <B>AsyncCallback</B> 对象。用指定回调和代理本身的方法调用异步 Web 服务，正如下面的代码示例所示：</P><PRE class=codeSample>void Completed( IAsyncResult ar ) 
{ 
    localhost.LongRunningService serviceProxy = 
        (localhost.LongRunningService)ar.AsyncState; 
    string message = serviceProxy.EndReturnMessageAfterDelay( ar ); 
} 
</PRE>
<P>当 Web 服务完成时，就调用完成的回调方法。然后，可以通过调用代理上的 <B>EndReturnMessageAfterDelay</B> 来获取异步结果。</P>
<DIV style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 10px"><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter6UsingMultThr.mspx#top"><IMG height=9 alt=返回页首 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_up.gif" width=7 border=0></A><A class=topOfPage href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter6UsingMultThr.mspx#top"><FONT color=#002c99>返回页首</FONT></A></DIV><A name=EEAA></A>
<H2>使用任务处理 UI 线程和其他线程之间的交互</H2>
<P>设计多线程应用程序最复杂的一个方面是处理 UI 线程和其他线程之间的关系。用于应用程序的后台线程并不直接与应用程序 UI 交互，这一点相当关键。如果后台线程试图修改应用程序的 UI 中的控件，该控件就可能会处于一种未知的状态。这可能会在应用程序中引起较大的问题，并难于诊断。例如，当另一个线程正在给动态生成的位图传送新数据时，它或许不能显示。或者，当数据集正在刷新时，绑定到数据集的组件可能会显示冲突信息。</P>
<P>为了避免这些问题，应该从不允许 UI 线程以外的线程更改 UI 控件或绑定到 UI 的数据对象。您应该始终尽力维护 UI 代码和后台处理代码之间的严格分离。</P>
<P>将 UI 线程与其他线程分离是一个良好的做法，但是您仍然需要在这些线程之间来回传递信息。多线程应用程序通常需要具有下列功能： </P>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>从后台线程获得结果并更新 UI。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>当后台线程执行它的处理时向 UI 报告进度。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>从 UI 控制后台线程，例如让用户取消后台处理。 </P></TD></TR></TBODY></TABLE>
<P>从处理后台线程的代码中分离 UI 代码的有效方法是，根据任务构造应用程序，并且使用封装所有任务细节的对象代表每个任务。</P>
<P>任务是用户期望能够在应用程序内完成的一个工作单元。在多线程处理的环境中，<B>Task</B> 对象封装了所有的线程细节，这样它们就可以从 UI 中清晰地分离出来。</P>
<P>通过使用 <I>Task</I> 模式，在使用多线程时可以简化代码。<I>Task</I> 模式将线程管理代码从 UI 代码中清晰地分离出来。UI 使用 <I>Task</I> 对象提供的属性和方法来执行行动，比如启动和停止任务、以及查询它们的状态。<I>Task</I> 对象也可以提供许多事件，从而允许将状态信息传送回 UI。这些事件都应该在 UI 线程内激发，这样，UI 就不需要了解后台线程。</P>
<P>使用 <B>Task</B> 对象可以充分简化线程交互，<B>Task</B> 对象虽然负责控制和管理后台线程，但是激发 UI 可以使用并且保证在 UI 线程上的事件。<B>Task</B> 对象可以在应用程序的各个部分中重用，甚至也可以在其他的应用程序中重用。 </P>
<P><B>图</B><B> 6.1 </B>说明了使用 <B>Task</B> 模式时代码的整体结构。</P>
<DIV style="WIDTH: 437px"><IMG height=206 alt=multif01 src="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/art/multif01.gif" width=437 border=0><BR>
<P class=figureCaption><B>图</B><B> 6.1 </B><B>使用</B><B> Task </B><B>模式时的代码结构</B></P>
<DIV class=figureRule></DIV></DIV>
<P><B>注</B><I>Task</I> 模式可以用来在单独的线程上执行本地后台处理任务，或者与网络上的远程服务异步交互。在后者的情况下，Task 对象常常称为服务代理。服务代理可以使用与 Task 对象相同的模式，并且可以支持使其与 UI 交互更容易的属性和事件。</P>
<P>因为 <I>Task</I> 对象封装了任务的状态，所以可以用它来更新 UI。要这样做，无论何时发生更改，都可以让 <I>Task</I> 对象针对主 UI 线程激发 <B>PropertyChanged</B> 事件。这些事件提供一种标准而一致的方法来传递属性值更改。 </P>
<P>可以使用任务来通知主 UI 线程进度或其他状态改变。例如，当任务变得可用时，可以将其设置为已启用的标志，该标志可用于启用相应的菜单项和工具栏按钮。相反，当任务变得不可用（例如，因为它还在进行中），可以将已启用标志设置为 false，这会导致主 UI 线程中的事件处理程序禁用适当的菜单项和工具栏按钮。</P>
<P>也可以使用任务来更新绑定到 UI 的数据对象。应该确保数据绑定到 UI 控件的任何数据对象在 UI 线程上更新。例如，如果将 <B>DataSet</B> 对象绑定到 UI 并从 Web 服务检索更新信息，就可以将新数据传递给 UI 代码。然后，UI 代码将新数据合并到 UI 线程上绑定的 <B>DataSet</B> 对象中。</P>
<P>可以使用 <B>Task</B> 对象实现后台处理和线程控制逻辑。因为 <B>Task</B> 对象封装了必要的状态和数据，所以它可以协调在一个或更多线程中完成任务所需的工作，并且在需要时传递更改和通知到应用程序的 UI。可以实现所有必需的锁定和同步并将其封装在 <B>Task</B> 对象中，这样 UI 线程就不必处理这些问题。</P>
<H3>定义 Task 类</H3>
<P>下面的代码示例显示了管理长期计算任务的类定义。</P>
<P><B>注</B>虽然该示例比较简单，但是它可以很容易地扩展为支持在应用程序的 UI 中集成的复杂后台任务。</P><PRE class=codeSample>public class CalculationTask 
{ 
    // Class Members…    public CalculationTask(); 
    public void StartCalculation( int count ); 
    public void StopCalculation(); 
    private void FireStatusChangedEvent( CalculationStatus status ); 
    private void FireProgressChangedEvent( int progress ); 
    private string Calculate( int count ); 
    private void EndCalculate( IAsyncResult ar ); 
} 
</PRE>
<P><B>CalculationTask</B> 类定义一个默认的构造函数和两个公共方法来启动和停止计算。它还定义了帮助器方法来帮助 <B>Task</B> 对象激发针对 UI 的事件。<B>Calculate</B> 方法实现计算逻辑，并且运行在后台线程上。<B>EndCalculate</B> 方法实现回调方法，它是在后台计算线程完成之后调用的。</P>
<P>类成员如下：</P><PRE class=codeSample>private CalculationStatus _calcState; 
private delegate string CalculationDelegate( int count ); 
public delegate void CalculationStatusEventHandler( 
                object sender, CalculationEventArgs e ); 
public delegate void CalculationProgressEventHandler( 
                object sender, CalculationEventArgs e ); 
public event CalculationStatusEventHandler CalculationStatusChanged; 
public event CalculationProgressEventHandler CalculationProgressChanged; 
</PRE>
<P><B>CalculationStatus</B> 成员是一个枚举，它定义了在任何一个时刻计算可能处于的三个状态。</P><PRE class=codeSample>public enum CalculationStatus 
{ 
    NotCalculating, 
    Calculating, 
    CancelPending 
} 
</PRE>
<P><B>Task</B> 类提供两个事件：一个通知 UI 有关计算状态的事件，另一个通知 UI 有关计算进度的事件。委托签名与事件本身都要定义。</P>
<P>这两个事件是在帮助器方法中激发的。这些方法检查目标的类型，如果目标类型是从 <B>Control</B> 类派生的，它们就使用 Control 类中的 <B>Invoke</B> 方法来激发事件。因此，对于 UI 事件接收器，可以保证事件是在 UI 线程上调用的。下面的示例展示了激发事件的代码。</P><PRE class=codeSample>private void FireStatusChangedEvent( CalculationStatus status ) 
{ 
    if( CalculationStatusChanged != null ) 
    { 
        CalculationEventArgs args = new CalculationEventArgs( status ); 
        if ( CalculationStatusChanged.Target is 
                System.Windows.Forms.Control ) 
        { 
            Control targetForm = CalculationStatusChanged.Target 
                    as System.Windows.Forms.Control; 
            targetForm.Invoke( CalculationStatusChanged, 
                    new object[] { this, args } ); 
        } 
        else 
        { 
            CalculationStatusChanged( this, args ); 
        } 
    } 
} 
</PRE>
<P>这段代码首先检查事件接收器是否已经注册，如果它已经注册，就检查目标的类型。如果目标类型是从 <B>Control</B> 类派生的，就使用 <B>Invoke</B> 方法激发该事件以确保在 UI 线程上处理它。如果目标类型不是从 <B>Control</B> 类派生的，就正常激发事件。在 <B>FireProgressChangedEvent</B> 方法中，以相同的方式激发事件以向 UI 报告计算进度，如下列的示例所示。</P><PRE class=codeSample>private void FireProgressChangedEvent( int progress ) 
    { 
        if( CalculationProgressChanged != null ) 
        { 
            CalculationEventArgs args = 
                new CalculationEventArgs( progress ); 
            if ( CalculationStatusChanged.Target is 
                    System.Windows.Forms.Control ) 
            { 
                Control targetForm = CalculationStatusChanged.Target 
                        as System.Windows.Forms.Control; 
                targetForm.Invoke( CalculationProgressChanged, 
                        new object[] { this, args } ); 
            } 
            else 
            { 
                CalculationProgressChanged( this, args ); 
            } 
        } 
} 
</PRE>
<P><B>CalculationEventArgs</B> 类定义了两个事件的事件参数，并且包含计算状态和进度参数，以便将它们发送给 UI。<B>CalculationEventArgs</B> 类的定义如下所示。</P><PRE class=codeSample>public class CalculationEventArgs : EventArgs 
    { 
        public string            Result; 
        public int               Progress; 
        public CalculationStatus Status; 
        public CalculationEventArgs( int progress ) 
        { 
            this.Progress = progress; 
            this.Status   = CalculationStatus.Calculating; 
        } 
        public CalculationEventArgs( CalculationStatus status ) 
        { 
            this.Status = status; 
        } 
}     
</PRE>
<P><B>StartCalculation</B> 方法负责启动后台线程上的计算。委托 <B>CalculationDelegate</B> 允许使用<I>委托异步调用</I><I> (Delegate Asynchronous Call)</I> 模式在后台线程上调用 <B>Calculation</B> 方法，如下面的示例所示。</P><PRE class=codeSample>public void StartCalculation( int count ) 
{ 
    lock( this ) 
    { 
        if( _calcState == CalculationStatus.NotCalculating ) 
        { 
            // Create a delegate to the calculation method. 
            CalculationDelegate calc = 
                    new CalculationDelegate( Calculation ); 
            // Start the calculation. 
            calc.BeginInvoke( count, 
                    new AsyncCallback( EndCalculate ), calc ); 
            // Update the calculation status. 
            _calcState = CalculationStatus.Calculating; 
            // Fire a status changed event. 
            FireStatusChangedEvent( _calcState ); 
        } 
    } 
} 
</PRE>
<P><B>StopCalculation</B> 方法负责取消计算，如下面的代码示例所示。</P><PRE class=codeSample>public void StopCalculation() 
{ 
    lock( this ) 
    { 
        if( _calcState == CalculationStatus.Calculating ) 
        { 
            // Update the calculation status. 
            _calcState = CalculationStatus.CancelPending; 
            // Fire a status changed event. 
            FireStatusChangedEvent( _calcState ); 
        } 
    } 
} 
</PRE>
<P>当调用 <B>StopCalculation</B> 时，计算状态被设置为 <B>CancelPending</B>，以通知后台停止计算。向 UI 激发一个事件，以通知已经接收到取消请求。</P>
<P>这两个方法都使用 <B>lock</B> 关键字来确保对计算状态变量的更改是原子的，这样应用程序就不会遇到争用情形。这两个方法都激发状态改变事件来通知 UI 计算正在启动或停止。</P>
<P>计算方法定义如下。</P><PRE class=codeSample>private string Calculation( int count ) 
{ 
    string result = ""; 
    for ( int i = 0 ; i &lt; count ; i++ ) 
    { 
        // Long calculation…        // Check for cancel. 
        if ( _calcState == CalculationStatus.CancelPending ) break; 
        // Update Progress 
        FireProgressChangedEvent( count, i ); 
    } 
    return result; 
} 
</PRE>
<P><B>注为清楚起见，计算的细节已经忽略。</B></P>
<P>每次传递都是通过循环进行的，这样就可以检查计算状态成员，以查看用户是否已经取消了计算。如果这样，循环就退出，从而完成计算方法。如果计算继续进行，就使用 <B>FireProgressChanged</B> 帮助器方法来激发事件，以向 UI 报告进度。</P>
<P>在计算完成之后，就调用 <B>EndCalculate</B> 方法，以便通过调用 <B>EndInvoke</B> 方法来完成异步调用，如下面的示例所示。</P><PRE class=codeSample>private void EndCalculate( IAsyncResult ar ) 
{ 
    CalculationDelegate del = (CalculationDelegate)ar.AsyncState; 
    string result = del.EndInvoke( ar ); 
    lock( this ) 
    { 
        _calcState = CalculationStatus.NotCalculating; 
        FireStatusChangedEvent( _calcState ); 
    } 
} 
</PRE>
<P><B>EndCalculate</B> 将计算状态重置为 <B>NotCalculating</B>，准备开始下一次计算。同时，它激发一个状态改变事件，这样就可以通知 UI 计算已经完成。</P>
<H3>使用 Task 类</H3>
<P><B>Task</B> 类负责管理后台线程。要使用 <B>Task</B> 类，必须做的事情就是创建一个 <B>Task</B> 对象，注册它激发的事件，并且实现这些事件的处理。因为事件是在 UI 线程上激发的，所以您根本不必担心代码中的线程处理问题。</P>
<P>下面的示例展示了如何创建 <B>Task</B> 对象。在这个示例中，UI 有两个按钮，一个用于启动计算，一个用于停止计算，还有一个进度栏显示当前的计算进度。</P><PRE class=codeSample>// Create new task object to manage the calculation. 
_calculationTask = new CalculationTask(); 
// Subscribe to the calculation status event. 
_ calculationTask.CalculationStatusChanged += new 
  CalculationTask.CalculationStatusEventHandler( OnCalculationStatusChanged ); 
// Subscribe to the calculation progress event. 
_ calculationTask.CalculationProgressChanged += new 
  CalculationTask.CalculationProgressEventHandler(  
OnCalculationProgressChanged ); 
</PRE>
<P>用于计算状态和计算进度事件的事件处理程序相应地更新 UI，例如通过更新状态栏控件。 </P><PRE class=codeSample>private void CalculationProgressChanged( object sender,  
CalculationEventArgs e ) 
{ 
    _progressBar.Value = e.Progress; 
} 
</PRE>
<P>下面的代码展示的 <B>CalculationStatusChanged</B> 事件处理程序更新进度栏的值以反映当前的计算进度。假定进度栏的最小值和最大值已经初始化。</P><PRE class=codeSample>private void CalculationStatusChanged( object sender, CalculationEventArgs e ) 
{ 
    switch ( e.Status ) 
    { 
        case CalculationStatus.Calculating: 
            button1.Enabled = false; 
            button2.Enabled = true; 
            break; 
        case CalculationStatus.NotCalculating: 
            button1.Enabled = true; 
            button2.Enabled = false; 
            break; 
        case CalculationStatus.CancelPending: 
            button1.Enabled = false; 
            button2.Enabled = false; 
            break; 
    } 
} 
</PRE>
<P>在这个示例中，<B>CalculationStatusChanged</B> 事件处理程序根据计算状态启用和禁用启动和停止按钮。这可以防止用户尝试启动一个已经在进行的计算，并且向用户提供有关计算状态的反馈。</P>
<P>通过使用 <B>Task</B> 对象中的公共方法，UI 为每个按钮单击实现了窗体事件处理程序，以便启动和停止计算。例如，启动按钮事件处理程序调用 <B>StartCalculation</B> 方法，如下所示。</P><PRE class=codeSample>private void startButton_Click( object sender, System.EventArgs e ) 
{ 
    calculationTask.StartCalculation( 1000 ); 
} 
</PRE>
<P>类似地，停止计算按钮通过调用 <B>StopCalculation</B> 方法来停止计算，如下所示。</P><PRE class=codeSample>private void stopButton_Click( object sender, System.EventArgs e ) 
{ 
    calculationTask.StopCalculation(); 
} 
</PRE>
<DIV style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 10px"><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter6UsingMultThr.mspx#top"><IMG height=9 alt=返回页首 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_up.gif" width=7 border=0></A><A class=topOfPage href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter6UsingMultThr.mspx#top"><FONT color=#002c99>返回页首</FONT></A></DIV><A name=EDAA></A>
<H2>小结</H2>
<P>多线程处理是创建可以响应的智能客户端应用程序的重要部分。应该分析多线程适合于应用程序的什么地方，并且注意在单独线程上进行不直接涉及 UI 的所有处理。在大多数情况下，可以使用 <B>ThreadPool</B> 类创建线程。然而，在某些情况下，必须使用 <B>Thread</B> 类来作为代替，在另外一些情况下，需要使用委托对象或 Web 服务代理来使特定的处理在非 UI 线程上进行。</P>
<P>在多线程应用程序中，必须确保 UI 线程负责所有与 UI 有关的任务，这样就可以有效地管理 UI 线程和其他线程之间的通信。<I>Task</I> 模式可以帮助大大简化这种交互。</P><A href="http://www.microsoft.com/practices"></A>
<P><A href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnpag/html/SCAG-CH06.asp" target=_blank><FONT color=#002c99>转到原英文页面</FONT></A></P><img src ="http://www.blogjava.net/TrampEagle/aggbug/30145.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/TrampEagle/" target="_blank">TrampEagle</a> 2006-02-10 14:56 <a href="http://www.blogjava.net/TrampEagle/articles/30145.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>智能客户端体系结构与设计指南 第 5 章 — 安全性考虑事项</title><link>http://www.blogjava.net/TrampEagle/articles/30144.html</link><dc:creator>TrampEagle</dc:creator><author>TrampEagle</author><pubDate>Fri, 10 Feb 2006 06:54:00 GMT</pubDate><guid>http://www.blogjava.net/TrampEagle/articles/30144.html</guid><wfw:comment>http://www.blogjava.net/TrampEagle/comments/30144.html</wfw:comment><comments>http://www.blogjava.net/TrampEagle/articles/30144.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/TrampEagle/comments/commentRss/30144.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/TrampEagle/services/trackbacks/30144.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 原文引自：http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter1Introduction.mspx第 5 章 — 安全性考虑事项发布日期： 8/20/2004 | 更新日期： 8/20/2004智能客户...&nbsp;&nbsp;<a href='http://www.blogjava.net/TrampEagle/articles/30144.html'>阅读全文</a><img src ="http://www.blogjava.net/TrampEagle/aggbug/30144.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/TrampEagle/" target="_blank">TrampEagle</a> 2006-02-10 14:54 <a href="http://www.blogjava.net/TrampEagle/articles/30144.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>智能客户端体系结构与设计指南 第 4 章 — 偶尔连接的智能客户端</title><link>http://www.blogjava.net/TrampEagle/articles/30143.html</link><dc:creator>TrampEagle</dc:creator><author>TrampEagle</author><pubDate>Fri, 10 Feb 2006 06:53:00 GMT</pubDate><guid>http://www.blogjava.net/TrampEagle/articles/30143.html</guid><wfw:comment>http://www.blogjava.net/TrampEagle/comments/30143.html</wfw:comment><comments>http://www.blogjava.net/TrampEagle/articles/30143.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/TrampEagle/comments/commentRss/30143.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/TrampEagle/services/trackbacks/30143.html</trackback:ping><description><![CDATA[原文引自：<A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter1Introduction.mspx">http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter1Introduction.mspx</A><BR><BR>
<H1>第 4 章 — 偶尔连接的智能客户端</H1>
<H2 class=subtitle></H2>
<DIV class=date>发布日期： 8/20/2004<SPAN class=datePipe> | </SPAN>更新日期： 8/20/2004</DIV>
<DIV class=overview>
<DIV style="WIDTH: 250px"><IMG height=68 alt="" src="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/art/pponline.gif" width=250 border=0><BR>
<P class=figureCaption></P>
<DIV class=figureRule></DIV></DIV>
<P><B>智能客户端体系结构与设计指南</B></P>
<P>David Hill, Brenton Webster, Edward A. Jezierski, Srinath Vasireddy and Mohammad Al-Sabt, Microsoft Corporation; Blaine Wastell, Ascentium Corporation; Jonathan Rasmusson and Paul Gale, ThoughtWorks; and Paul Slater, Wadeware LLC</P>
<P><B>相关链接</B></P>
<P>Microsoft® <I>patterns &amp; practices</I> 库 <A href="http://www.microsoft.com/resources/practices/default.mspx" target=_blank><FONT color=#002c99>http://www.microsoft.com/resources/practices/default.mspx</FONT></A></P>
<P><I>Application Architecture for .NET: Designing Applications and Services </I><A href="http://msdn.microsoft.com/library/en-us/dnbda/html/distapp.asp" target=_blank><FONT color=#002c99>http://msdn.microsoft.com/library/en-us/dnbda/html/distapp.asp</FONT></A></P>
<P><B>摘要</B>：本章讨论了您在设计和生成偶尔连接到网络的智能客户端应用程序时可能面临到的问题。本章讨论了连接性的概念，介绍了两种实现脱机功能的主要方法，并且讨论了您在使应用程序可供脱机使用时需要考虑的一些问题。</P></DIV>
<CENTER><IMG title="" height=6 alt=* src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/3squares.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/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter4OccConnSC.mspx#EHAA"><IMG height=9 alt=常见的偶尔连接情况 hspace=4 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_down.gif" width=7 vspace=2 border=0></A></TD>
<TD class=onThisPage><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter4OccConnSC.mspx#EHAA"><FONT color=#002c99>常见的偶尔连接情况</FONT></A></TD></TR>
<TR vAlign=top>
<TD><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter4OccConnSC.mspx#EGAA"><FONT color=#002c99><IMG height=9 alt=偶尔连接设计策略 hspace=4 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_down.gif" width=7 vspace=2 border=0></FONT></A></TD>
<TD class=onThisPage><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter4OccConnSC.mspx#EGAA"><FONT color=#002c99>偶尔连接设计策略</FONT></A></TD></TR>
<TR vAlign=top>
<TD><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter4OccConnSC.mspx#EFAA"><FONT color=#002c99><IMG height=9 alt=使用面向服务的方法设计偶尔连接的智能客户端应用程序 hspace=4 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_down.gif" width=7 vspace=2 border=0></FONT></A></TD>
<TD class=onThisPage><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter4OccConnSC.mspx#EFAA"><FONT color=#002c99>使用面向服务的方法设计偶尔连接的智能客户端应用程序</FONT></A></TD></TR>
<TR vAlign=top>
<TD><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter4OccConnSC.mspx#EEAA"><FONT color=#002c99><IMG height=9 alt=使用基于任务的方法 hspace=4 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_down.gif" width=7 vspace=2 border=0></FONT></A></TD>
<TD class=onThisPage><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter4OccConnSC.mspx#EEAA"><FONT color=#002c99>使用基于任务的方法</FONT></A></TD></TR>
<TR vAlign=top>
<TD><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter4OccConnSC.mspx#EDAA"><FONT color=#002c99><IMG height=9 alt=处理依赖性 hspace=4 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_down.gif" width=7 vspace=2 border=0></FONT></A></TD>
<TD class=onThisPage><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter4OccConnSC.mspx#EDAA"><FONT color=#002c99>处理依赖性</FONT></A></TD></TR>
<TR vAlign=top>
<TD><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter4OccConnSC.mspx#ECAA"><FONT color=#002c99><IMG height=9 alt=小结 hspace=4 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_down.gif" width=7 vspace=2 border=0></FONT></A></TD>
<TD class=onThisPage><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter4OccConnSC.mspx#ECAA"><FONT color=#002c99>小结</FONT></A></TD></TR></TBODY></TABLE>
<P>我们生活在一个连接程度越来越高的世界里。然而，在许多情况下，我们不能无时无刻都依赖连接。您的用户可能需要旅行，他们可能会暂时失去无线连接，可能存在延迟或带宽问题，或者您可能需要拆卸网络的某些部分进行维护。即使用户的确具有良好的网络连接，您的应用程序也可能无法在所有时间都能访问网络资源。所请求的服务可能繁忙、停止运行或者只是暂时不可用。</P>
<P>如果应用程序有时无法及时地通过网络与服务或数据交互，则为偶尔 连接的应用程序。如果您能让用户在脱机时使用其应用程序富有成效地工作，并且仍然能够为其提供连接的应用程序在连接有效时所具有的好处，则可以提高用户的生产率和工作效率，并且提高应用程序的可用性。</P>
<P>智能客户端相对于基于 Web 的应用程序所具有的主要好处之一是：当应用程序无法连接到网络资源时，它们能够让用户继续工作。偶尔连接的智能客户端能够在未连接到网络资源时工作，然后在以后某个时间在后台更新网络资源。更新可能几乎立即发生，但有时可能发生在数天甚至数周以后。</P>
<P>要给予应用程序完整的偶尔连接功能，您需要提供一种基础结构，使用户能够在没有连接到网络资源时工作。该基础结构应该包括数据缓存，以便可以在客户端使用所有需要的数据；它还应该包括用户工作详细信息的存储，以便用来在用户重新联机时将客户端与网络资源同步。应用程序为支持偶尔连接的操作所需要的确切特性和功能取决于它的连接性、操作环境以及用户在联机和脱机时所期望的功能。但是，所有智能客户端应用程序都应该在未连接到网络时为用户提供某种体验，即使功能极其有限。在设计和生成应用程序时，应该始终避免因为服务器不可用而在客户端生成错误消息。</P>
<P>本章考察您在生成具有脱机功能的应用程序时所面临的问题。它将讨论设计脱机应用程序的不同策略，详细讨论设计注意事项，分析如何组织应用程序以使用任务，并且考察应用程序应该如何处理数据。</P><A name=EHAA></A>
<H2>常见的偶尔连接情况</H2>
<P>偶尔连接的智能客户端在许多常见的情况下都极其有用。许多脱机情况涉及到用户显式断开网络连接并且在没有网络连接的情况下工作，例如： </P>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>保险代理可能需要在离开办公室的时候创建新的保险单。他或她可能需要在无法连接到办公室中系统的情况下，输入所有相关数据，计算保险费，并签发保险单详细信息。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>销售代表可能需要在现场（该销售代表无法在此处连接到服务器）与客户签订大订单。他或她可能需要咨询价格表和目录信息，输入所有订单数据，并提供交货预算和折扣级别，而无须连接。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>维护技术人员在客户端站点处理服务呼叫时可能需要详细的技术信息。应用程序帮助他或她诊断问题，提供技术文档资料和详细信息，并且使该技术人员无须连接就能签下部件订单以及记录他或她的操作。 </P></TD></TR></TBODY></TABLE>
<P>其他脱机情况涉及到间歇性或低质量的连接，例如： </P>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>遍布全球的客户呼叫中心和企业网络之间的连接可能不具有足够高的质量，以供全天候联机使用。应用程序应该提供脱机功能（包括数据缓存），以便维持应用程序的可用性。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>携带 Tablet PC 旅行的医务人员可能在旅行途中经历网络连接中断的情况。当应用程序连接时，它应该在后台同步数据，而不应该等待显式重新连接。 </P></TD></TR></TBODY></TABLE>
<P>应该将偶尔连接的智能客户端设计为最大限度地利用可用连接，确保应用程序和数据尽可能保持最新，并且不会对应用程序的性能造成不利影响。</P>
<DIV style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 10px"><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter4OccConnSC.mspx#top"><IMG height=9 alt=返回页首 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_up.gif" width=7 border=0></A><A class=topOfPage href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter4OccConnSC.mspx#top"><FONT color=#002c99>返回页首</FONT></A></DIV><A name=EGAA></A>
<H2>偶尔连接设计策略</H2>
<P>在设计偶尔连接的智能客户端应用程序的体系结构时，有两种概括性的方法：<I>以数据为中心</I> 和面向服务。 </P>
<P>使用以数据为中心的策略的应用程序具有一个在客户端上本地安装的关系数据库管理系统 (RDBMS)，并且使用该数据库系统的内置功能将本地数据更改传回服务器，处理同步过程，并检测和解决任何数据冲突。</P>
<P>使用面向服务方法的应用程序将信息存储在消息中，并且当客户端脱机时将这些消息排列到队列中。在重新建立连接以后，排队的消息将被发送到服务器进行处理。 </P>
<P>图 4.1 显示了以数据为中心的方法和面向服务的方法。</P>
<DIV style="WIDTH: 450px"><IMG height=422 alt="" src="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/art/chfg0112.gif" width=450 border=0><BR>
<P class=figureCaption><B>图</B><B> 4.1 </B>偶尔连接应用程序设计的面向服务的方法和以数据为中心的方法</P>
<DIV class=figureRule></DIV></DIV>
<P>本节将详细分析这两种方法并解释何时应该使用每种方法。</P>
<H3>以数据为中心的方法</H3>
<P>当您使用以数据为中心的方法时，通常情况下，服务器发布数据，而客户端创建它所需要的数据的预订，以便它可以在脱机之前将相应的数据复制到本地数据存储。当客户端脱机时，它将通过对本地数据存储的调用来对本地数据进行更改。当客户端重新联机后，数据存储会将对客户端上的数据所做的更改传回服务器。对服务器上数据所做的更改也可能被传回客户端。在合并阶段遇到的任何冲突都将按照业务分析师定义的自定义规则，由在服务器或客户端上指定的冲突解决规则处理。</P>
<P>在客户端和服务器之间合并更改的过程称为<I>合并复制</I>。更改可能在客户端和服务器以自治方式发生，因此不使用 ACID（原子、一致、独立、持久）事务。相反，当执行合并时，系统中的所有预订者都将使用发布者拥有的数据值。</P>
<P>以数据为中心的方法主要优点是所有更改跟踪代码都包含在关系数据库中。通常，这包括数据库的列级和行级的冲突检测代码、数据验证代码以及约束。这意味着您无须编写自己的更改跟踪代码或冲突检测与解决代码，尽管您的确需要知道合并-复制方案以便针对数据冲突和数据更新来优化您的应用程序。</P>
<P>在以数据为中心的模型中，数据库系统负责处理同步；因此，您无须自己来实现所有数据同步功能。用户定义哪些表要求数据同步，而数据库系统使基础结构可以跟踪更改并且检测和解决冲突。您可以通过使用 COM 对象或 Transact SQL (TSQL) 存储过程的自定义冲突解决程序来扩展该基础结构，以便提供自定义冲突解决或避免机制。而且，因为在整个系统中只有一个数据储存库，所以当同步完成时，能够保证在服务器和客户端之间执行数据会聚。 </P>
<P>但是，以数据为中心的方法有一些缺点。在客户端上需要本地数据库意味着该方法在下列情况下可能不适合： </P>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>应用程序在小型设备上运行 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>需要轻触部署机制。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>非管理员用户应该能够部署应用程序 </P></TD></TR></TBODY></TABLE>
<P>Microsoft 提供了能够在 Windows® 客户端、Windows Server™ 和 Pocket PC 平台上运行的数据库软件，但它没有为 SmartPhone 设备提供数据库软件。</P>
<P>而且，服务器上的数据库与客户端上的数据库之间的紧耦合性意味着对服务器上数据库架构进行的更改对客户端具有直接影响。这可能使管理对客户端或服务器进行的数据库架构更改变得很困难。</P>
<P>对于大量客户端而言，需要提供一种可管理且可伸缩的方法来部署独特的数据集。合并复制支持动态筛选，这使得管理员能够定义这些脱机数据集并且以可伸缩的方式部署它们。您应该利用数据库提供的筛选机制来减少在客户端和服务器之间发送的数据量，并降低冲突的可能性。</P>
<P>使用本地数据库在本地存储和操作数据有许多好处。您可以使用数据库将本地更改传回服务器并帮助处理同步问题。 </P>
<P>您应该在下列情况下使用以数据为中心的方法： </P>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>您可以在客户端部署数据库实例。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>您的应用程序可以在两层环境中工作。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>您可以通过数据架构定义和通讯协议将客户端紧耦合到服务器。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>您需要内置的更改跟踪和同步。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>您希望依赖数据库来处理数据协调冲突以及尽可能减少需要编写的自定义协调代码的数量。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>您无须与多个截然不同的服务交互。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>Windows 用户能够通过局域网 (LAN) 或虚拟专用网络 (VPN/IPSec) 直接连接到数据库。为 Pocket PC 平台编写的应用程序能够通过 HTTPS 同步 HTTP。 </P>
<P><B>注</B> 本指南没有深入讨论以数据为中心的方法。许多地方对它进行了更为完整的介绍，包括 Microsoft SQL Server 联机图书或 MSDN。有关以数据为中心的方法的详细信息，请参阅“Merge Replication”，网址为：<A href="http://msdn.microsoft.com/library/en-us/replsql/repltypes_6my6.asp" target=_blank><FONT color=#002c99>http://msdn.microsoft.com/library/en-us/replsql/repltypes_6my6.asp</FONT></A>。</P></TD></TR></TBODY></TABLE>
<H3>面向服务的方法</H3>
<P>对于面向服务的方法而言，客户端可以与需要的任何服务交互。而且，客户端将致力于服务请求本身，而不是对本地保存的数据进行直接更改。服务请求可能在客户端或服务器上导致状态更改，但此类更改是服务请求的副产品。</P>
<P>面向服务策略的一个优点是在客户端上不需要本地关系数据库。这意味着可以将该方法应用于许多不同的客户端类型，包括那些具有少量处理能力的客户端，如移动电话。</P>
<P>当您的应用程序必须在 Internet 和 Extranet 环境中工作时，面向服务的方法尤其适合。如果您的客户端在防火墙外部工作并且与企业服务交互，则通过使用面向服务的策略，您可以避免由于某种原因而必须在防火墙中打开特定的端口，例如为了启用直接数据库或 Microsoft 消息队列 (MSMQ) 访问。</P>
<P>松耦合意味着您可以在客户端上使用与服务器上不同的数据架构，并且在客户端传输数据。实际上，客户端和服务器不需要知道对方。您还可以独立地更新客户端和服务器组件。</P>
<P>该方法的主要缺点是您需要编写更多的基础结构代码，以便存储和转发消息以及检测应用程序何时联机或脱机。这可以使您在设计中具有更多的灵活性，但通常意味着在创建脱机客户端时需要完成更多的工作。</P>
<P><B>注</B> 智能客户端脱机应用程序块 (Smart Client Offline Application Block) 为您提供了支持脱机客户端的面向服务策略的代码。您可以使用该块来检测应用程序何时联机或脱机，并且存储消息以及将消息转发给服务器进行处理。有关该应用程序块的概述，请参阅 Smart Client Offline Application Block，网址为：<A href="http://msdn.microsoft.com/library/en-us/dnpag/html/offline.asp" target=_blank><FONT color=#002c99>http://msdn.microsoft.com/library/en-us/dnpag/html/offline.asp</FONT></A>。</P>
<P>面向服务的方法最适合于需要与许多不同服务交互的智能客户端。因为消息的有效负载被封装，所以传输层可以改变，而不会影响消息的内容。例如，原来要发往某个 Web 服务的消息可以同样容易地发往消耗消息队列消息的服务。消息与传输无关这一事实还使应用程序可以根据需要自定义安全性实现。</P>
<P>您应该在下列情况下使用面向服务的方法： </P>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>您希望消除客户端和服务器之间的耦合，以便进行独立的版本控制和部署。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>您需要对数据协调问题拥有更多的控制和灵活性。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>您具有编写更高级应用程序基础结构代码的开发人员技能。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>您要求轻量客户端足迹。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>您能够将应用程序组织为面向服务的体系结构。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>您要求特定的业务功能（例如，自定义业务规则和处理、灵活的协调等等）。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>您需要对客户端上所存储数据的架构进行控制，并且还要控制可能与服务器不同的灵活性。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>您的应用程序与多个服务或截然不同的服务（例如，多个 Web 服务，或者通过消息队列、Web 服务或 RPC 机制提供的服务）交互。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>您需要自定义安全方案。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>您的应用程序在 Internet 或 Extranet 环境中工作。 </P></TD></TR></TBODY></TABLE>
<P>尽管以数据为中心的方法和面向服务的方法都是有效的体系结构方法，但许多智能客户端应用程序无法在客户端上支持完整的关系数据库实例。在这种情况下，您应该采用面向服务的方法，并且确保您拥有适当的基础结构，以便处理诸如数据缓存以及冲突检测和解决等问题。 </P>
<P>因此，本章的其余部分将集中讨论智能客户端开发人员在实现面向服务的方法时需要考虑的问题。</P>
<DIV style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 10px"><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter4OccConnSC.mspx#top"><IMG height=9 alt=返回页首 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_up.gif" width=7 border=0></A><A class=topOfPage href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter4OccConnSC.mspx#top"><FONT color=#002c99>返回页首</FONT></A></DIV><A name=EFAA></A>
<H2>使用面向服务的方法设计偶尔连接的智能客户端应用程序</H2>
<P>当您使用面向服务的方法设计偶尔连接的智能客户端时，需要解决许多问题。这些问题包括： </P>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>优选异步通讯。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>尽可能减少复杂的网络交互。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>添加数据缓存功能。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>管理连接。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>设计存储转发机制。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>管理数据和业务规则冲突。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>与类似于 Create, Read, Update, Delete (CRUD) 的 Web 服务交互。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>使用基于任务的方法。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>处理依赖性。 </P></TD></TR></TBODY></TABLE>
<P>本节将详细讨论这些问题。</P>
<H3>优选异步通讯</H3>
<P>应用程序在与位于网络上的数据和服务交互时，使用下列两种通讯方法之一： </P>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>同步通讯。应用程序被设计为在继续处理之前期待响应（例如，同步 RPC 通讯）。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>异步通讯。应用程序通过使用消息总线或其他某种基于消息的传输来进行通讯，并且期待在请求和任何响应之间存在延迟，或者根本不期待任何响应。 </P>
<P><B>注在本指南中，同步通讯是指在能够继续处理之前期待响应的所有通讯，即使同步调用是在单独的后台线程上执行的。</B></P></TD></TR></TBODY></TABLE>
<P>如果您要设计新的智能客户端应用程序，则应该确保它在与位于网络上的数据和服务交互时主要使用异步通讯。被设计为期待请求和响应之间存在延迟的应用程序非常适合于偶尔连接的用途，前提是该应用程序在等待响应的过程中能够提供重要且有用的功能，并且在响应延迟时不会妨碍用户继续完成他或她的工作。</P>
<P>当该应用程序未连接到网络资源时，您可以在本地存储请求，并且在应用程序重新连接时将这些请求发送到远程服务。无论是脱机还是联机，因为应用程序并不期待请求立即得到响应，所以都不会禁止用户继续使用该应用程序，用户可以继续工作。</P>
<P>使用同步通讯的应用程序（即使是在后台线程上）不太适合于偶尔连接的用途。因此，您应该在智能客户端中尽可能减少对同步通讯的使用。如果您要将使用同步通讯的应用程序重新设计为智能客户端，则应该确保它采用异步程度更高的通讯模型，以便它能够脱机工作。但是，在许多情况下，您可以在异步基础结构之上实现类似于同步的通讯（称为同步-异步模型），以便将应用程序设计更改保持到最低限度。</P>
<P>将应用程序设计为使用异步通讯可以为您带来超越偶尔连接用途的好处。大多数设计为使用异步通讯的应用程序都比那些使用同步通讯的应用程序更加灵活。例如，可以在异步应用程序正在执行任务的过程中将其关闭，并且当它重新启动时不会影响请求或响应的处理。</P>
<P>在大多数情况下，您不需要在应用程序中同时实现同步和异步行为以便在联机和脱机时使用。异步行为同时适合于联机和脱机使用；当应用程序联机时，请求会以接近于实时的速度进行处理。</P>
<H3>尽可能减少复杂的网络交互</H3>
<P>偶尔连接的智能客户端应该尽可能减少或消除与位于网络上的数据和服务的交互。当您的应用程序脱机时，它可能必须存储请求并在应用程序重新连接时发送这些请求，或者它可能需要针对响应等待一会儿。无论采取哪种方式，应用程序都不会立即知道请求是否即将成功或已经成功。</P>
<P>要使应用程序能够在脱机时继续工作，您必须就网络请求的成功或本地数据的更改进行某些假设。跟踪这些假设以及服务请求和数据更改之间的依赖性可能非常复杂。要减轻这一负担，您应该尽可能地围绕简单的网络交互来设计您的智能客户端应用程序。</P>
<P>通常，不返回任何数据的请求（“发后不理”请求）对于偶尔连接的应用程序来说不会成为问题；应用程序可以存储该请求并在重新连接时转发该请求。当应用程序脱机时，它不知道调用是否已经成功；因此，该应用程序必须假设调用已成功。这一假设可能影响后续处理。</P>
<P>如果请求返回应用程序继续工作所需要的数据，则应用程序必须使用暂定值或虚拟值，或者在没有相应数据的情况下工作。在此情况下，您需要将应用程序设计为跟踪暂定数据和已确认的数据，并且将用户界面设计为使用户知道暂定或挂起的数据。这使用户或应用程序能够基于数据的有效性进行明智的决策，并防止以后出现与数据冲突和损坏有关的问题。</P>
<P>如果用户已经在脱机状态下完成了一些不连续的工作单元，则应用程序应该允许各个工作单元单独成功或失败。例如，在允许用户输入订单信息的应用程序中，应用程序可以让用户根据需要输入任意多的订单，但该应用程序必须确保一个订单不必依赖于其他订单的成功。</P>
<P>当应用程序对于每个工作单元只能产生一个服务请求时，确保各个工作单元之间不存在依赖性是相对容易的。这使应用程序可以跟踪挂起的请求，并且可以在联机后处理这些请求。但是，在某些情况下，用户任务更为复杂，必须产生多个服务请求才能完成它们。在这些情况下，应用程序必须确保每个请求都与其他请求一致，以便保持数据一致性。</P>
<H3>添加数据缓存功能</H3>
<P>您的应用程序需要确保在它脱机时，客户端能够提供用户继续工作所需的所有数据。在某些情况下，您的应用程序应该出于性能原因在客户端缓存数据，但很多时候，您的应用程序必须缓存额外的数据以满足偶尔连接的用途。例如，您可能没有为联机使用的应用程序缓存不稳定的数据，但要使同一应用程序能够脱机工作，则需要在本地计算机上缓存这些数据。客户端和服务器端都必须进行相应的设计以考虑数据不稳定性，以便它们可以适当地处理更新和冲突。 </P>
<P>当应用程序脱机时，您可能选择不从应用程序数据缓存中删除过时的数据，而是使用这些过时的数据以使用户能够继续工作。在其他情况下，应用程序可能需要自动从缓存中删除这些数据，以防止用户使用它并在以后产生问题。在后一种情况下，在通过同步过程获取新数据之前，应用程序可能停止提供所需的功能。</P>
<P>根据应用程序风格和功能的不同，缓存中数据的刷新可能以多种方式发生。对于某些应用程序，可以按照下列策略自动刷新缓存的数据：当缓存的数据到期时刷新；按照某个时间表定期刷新；当应用程序执行同步操作时刷新；或者当服务器更改了数据并将相应的更改通知应用程序时刷新。其他应用程序可能允许用户手动选择要缓存的数据，从而使用户能够在脱机状态下分析或使用这些数据。</P>
<P>其他数据缓存注意事项同样适用，如安全性约束和数据处理约束。这些问题并非只能在支持脱机操作的应用程序中遇到，<A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter2HandlingData.mspx"><FONT color=#002c99>第 2 章：处理数据</FONT></A>中对它们进行了更为完整的介绍。</P>
<H3>处理对引用数据的更改</H3>
<P>引用数据是很少发生更改的数据。通常，应用程序包含大量这样的数据。例如，在客户记录中，客户名称很少更改。可以容易地将这种类型的数据缓存在客户端上，但有时您的引用数据将发生更改，而您必须具有某种将这些更改传播到智能客户端的机制。</P>
<P>您具有两种传播这些数据的选项：推模型和拉模型。</P>
<P>在推模型中，服务器抢先通知客户端并试图将数据推出。在以数据为中心的方法中，这由复制客户端数据存储中被刷新的数据的服务器数据组成。在面向服务的方法中，这可能是包含已更新数据的消息。（这要求客户端实现可供服务器连接到的终结点。）</P>
<P>在拉模型中，客户端与服务器联系以检查是否存在更新。客户端可以通过定期检查服务器做到这一点，也可以通过分析带有声明引用数据何时到期的原始数据的元数据来做到这一点。客户端甚至可能趁早从服务器拉数据（例如，价格表），并且仅当它变为有效时才使用它。</P>
<P>在某些情况下，您可能选择采用这样一种模型，即由服务器通知客户端更新可用（例如，通过在客户端连接时发送警报），然后客户端从服务器拉数据。</P>
<H3>管理连接</H3>
<P>当您设计偶尔连接的智能客户端时，应该从以下两个方面考虑应用程序的工作环境：可用的连接性，以及应用程序随着该连接性的更改而需要具有的行为。</P>
<P>应该将某些应用程序设计为能够在没有连接的情况下长时间地（数天，甚至数周）工作。而对于其他应用程序，则应该将其设计为总是期待连接，但能够优雅地处理连接临时断开连接的情况。某些应用程序在脱机时应该只提供功能子集，而其他应用程序则应该提供大部分功能以供脱机使用。</P>
<P>尽管许多偶尔连接方案都涉及到用户从网络显式断开连接，并在没有连接的情况下工作，但有时应用程序在未显式从网络断开连接的情况下脱机。可以将应用程序设计为能够处理其中一种方案或能够处理这两种方案。</P>
<H3>手动连接管理</H3>
<P>可以将应用程序设计为能够在用户决定脱机工作时正常工作。应用程序必须在本地计算机上存储用户可能需要的所有数据。在此情况下，用户在与应用程序交互时知道它处于脱机状态，而应用程序不会试图执行网络操作，直到它被明确告知联机并执行同步操作为止。</P>
<P>您还可以包括相应的支持，以便用户能够在使用连接成本较高的连接或低带宽连接（如商业无线热点、移动电话连接或拨号连接）时通知应用程序。在此情况下，可以将应用程序设计为对请求进行批处理，以便在建立连接时，能够最大限度地利用它。</P>
<H3>自动连接管理</H3>
<P>可以将应用程序设计为能够动态适应意外发生的连接性更改。这些更改可能包括下列情况： </P>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>间歇性的连接</B>。可以将应用程序设计为能够适当地适应或处理那些网络连接临时丢失的情况。某些应用程序可能临时挂起功能，直至应用程序能够重新联机为止，而其他应用程序则必须提供完整的功能。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>不断变化的连接质量</B>。可以将应用程序设计为能够预测网络连接具有低带宽或高延迟的情况，或者可以动态确定这一情况并改变其行为以适应其环境。如果连接质量恶化，则应用程序可以更为积极地缓存数据。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>不断变化的服务可用性</B>。可以将应用程序设计为能够处理它通常与其交互的服务不可用的情况，并切换到它的脱机行为。如果应用程序与多个服务交互，并且其中一个服务变得不可用，则它可能决定将所有服务都视为脱机服务。 </P></TD></TR></TBODY></TABLE>
<P>您可以通过使用 wininet.dll 来检测智能客户端应用程序是否具有连接。该 DLL 与 Microsoft Internet Explorer 用于确定用户是否连接到 Internet 的 DLL 相同。下面的代码示例显示了如何调用 wininet.dll。</P><PRE class=codeSample>   [DllImport("wininet.dll")] 
   private extern static bool InternetGetConnectedState( out int  
connectionDescription, int reservedValue ) ; 
   public bool IsConnected() { 
     int connectionDescription = 0; 
     return InternetGetConnectedState(out connectionDescription, 0); 
} 
</PRE>
<H3>设计存储转发机制</H3>
<P>如果您将应用程序设计为使用面向服务的体系结构，则必须提供存储转发机制。通过存储转发，可以创建、存储消息并最终将其转发到它们各自的目的地。最常见的存储转发实现是消息队列。这就是面向消息的中间件产品（如 Microsoft 消息队列）的工作方式。当创建新的消息时，它们将被放入消息队列，并被转发到它们的目标地址。尽管还有其他存储转发替代机制（如 FTP 或在客户端与服务器之间复制文件），但本指南将专门讨论最常见的实现：消息队列。</P>
<P>智能客户端需要一种在脱机时保留消息的方法。如果您的应用程序需要在脱机时创建新消息，则您的队列必须具有一种保留它们以便以后与服务器进行更新的方法。这里，最显而易见的选择是将其写入磁盘。</P>
<P>您的设计需要包括能够确保将消息成功传递到其目的地的功能。您的设计应该考虑下列情况： </P>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>缺少有关消息已正确发送的确认</B>。通常，您不应该只是因为消息已经离开队列就假设已在服务器收到该消息。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>客户端与服务器之间的连接丢失</B>。在某些情况下，因为客户端与服务器之间的连接丢失，所以您必须从队列返回消息。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>缺少来自服务的确认</B>。在此情况下，您可能需要发送独立的确认以通知客户端信息已收到。 </P></TD></TR></TBODY></TABLE>
<P>存储转发机制还需要支持附加功能，如消息加密、优先级化、锁定和同步。</P>
<P>生成和设计可靠的消息处理体系结构是一项复杂的任务，要求有丰富的经验和技能。因此，您应该认真地考虑一下商业产品，如 Microsoft 消息队列。但是，Microsoft 消息队列要求在客户端上安装软件，这可能并非所有智能客户端的选择。</P>
<P>另一个消息队列管理选择是使用智能客户端脱机应用程序块，它可在 <A href="http://msdn.microsoft.com/library/en-us/dnpag/html/offline-CH01.asp" target=_blank><FONT color=#002c99>http://msdn.microsoft.com/library/en-us/dnpag/html/offline-CH01.asp</FONT></A> 获得。</P>
<P>该应用程序块提供了相关的服务和基础结构，智能客户端可用来向它们的应用程序提供脱机功能。该应用程序块使用消息队列概念支持消息处理的存储转发方法。默认情况下，该应用程序块支持消息队列集成以及其他一些消息保留机制（内存、独立存储和 Microsoft SQL Server™ 桌面引擎 [MSDE]）。</P>
<H3>管理数据和业务规则冲突</H3>
<P>以脱机模式在应用程序中进行的更改必须在某个时刻与服务器同步或协调。这增加了发生冲突或发生应用程序、用户或管理员必须解决的其他问题的可能性。当确实发生冲突时，您必须确保能够检测并解决它们。</P>
<P>与数据冲突不同，发生业务规则冲突的原因不是在两块数据之间存在冲突，而是在某个地方违反了业务规则并且需要纠正。数据冲突和业务规则冲突都可能需要由客户端应用程序或用户处理。</P>
<P>作为业务规则冲突的示例，假设您有一个订单管理应用程序，该应用程序缓存了一个产品目录，以便用户能够在脱机时将订单输入系统。然后，当该应用程序重新联机时，这些订单将被转发给服务器。如果某个订单包含一种位于缓存的产品目录中但在应用程序重新联机时已经停止生产的产品，则当订单数据被转发给服务器时，服务器将检查订单详细信息并且发现该产品已经停止生产。此时，应用程序可以通知用户该订单有一个问题。如果所述产品已被替换或废弃，则系统可以给予用户切换到不同产品的能力。这种情况不是数据冲突（因为数据没有与任何内容冲突），但这仍然是不正确的，并且需要纠正。</P>
<P>尽管业务规则异常和数据冲突是不同类型的异常，但大多数情况下，可以使用相同的基本方法和基础结构来处理它们。本节讨论如何在智能客户端应用程序中处理数据和业务规则冲突。</P>
<H3>数据分区和锁定</H3>
<P>任何允许多个用户访问共享数据的系统都有产生冲突的可能。当您设计智能客户端应用程序时，您必须确定它是否将数据分区以及如何执行锁定，因为这些因素有助于确定在应用程序中发生冲突的可能性有多大。</P>
<H3>数据分区</H3>
<P>当不同用户对数据的不同部分具有控制权时，可以使用数据分区。例如，销售代表可能具有一些只分配给他或她的帐户。在此情况下，您可以将数据分区，以便只有销售代表可以更改这些帐户。以这种方式将数据分区后，可以允许用户对数据进行任意更改，而不必担心遇到数据冲突。</P>
<P>将应用程序设计为使用数据分区通常会受到很多限制，因此在许多情况下不是好的解决方案。但是，如果数据分区对于特定应用程序是可行的，则应该认真地予以考虑，因为它有助于减少应用程序所产生的冲突的数量。</P>
<H3>保守式锁定</H3>
<P>保守式锁定意味着系统使用互相排斥的锁来确保一次只有一个用户对系统数据进行操作。对数据的所有请求都被序列化。例如，在出发之前，推销员可能访问数据库，并且逻辑地为特定地区的客户签出客户帐户。这一签出操作可能要求在办公室里更新一个电子表格，并且向其他人发送电子邮件以更新帐户状态。现在，当该推销员出差时，其余销售人员就会明白该推销员对这些客户文件具有独占的访问权，并且可以随意进行所需的任何修改。当该推销员回到办公室并将新数据与服务器数据进行同步时，应该没有任何冲突。在同步数据之后，该推销员将释放逻辑锁。 </P>
<P>保守式锁定的主要问题是：如果多个用户需要同时对相同数据进行操作，则他们必须等待数据变得可用。对于偶尔连接的智能客户端，数据可能在某个客户端重新联机之前一直保持锁定，而这一时间可能很长。这就使得保守式锁定在数据完整性方面很好（因为没有发生冲突的可能性），但在并发性方面却很差。</P>
<P>实际上，保守式锁定仅适合于几种类型的偶尔连接应用程序。例如，在文档管理系统中，用户在处理文档时可能会有意地将文档签出一段持续很久的时间。但是，随着可伸缩性和复杂性的提高，保守式锁定正在成为一种不太可行的选择。</P>
<H3>开放式锁定</H3>
<P>大多数偶尔连接的智能客户端应用程序都使用开放式锁定，它允许多个用户并发地访问和操作相同的数据，并且假设各个用户对数据进行的更改将不会发生冲突。开放式锁定允许对数据进行高度并发性访问，其代价是数据完整性将会降低。如果发生冲突，则需要一种能够处理这些冲突的策略。</P>
<P>在大多数脱机方案中，您需要使用开放式锁定。因此，您必须预料到会发生数据冲突，并且必须在确实发生数据冲突时对它们进行协调。</P>
<H3>跟踪未确认或暂定的数据</H3>
<P>当您的用户脱机工作时，他们已经更改的任何数据都没有被确认为服务器上的更改。只有在这些数据已经与服务器进行合并而且没有任何冲突之后，才能真正地将这些数据视为已确认的数据。对未确认的数据进行跟踪是很重要的。在已经确认数据之后，可以对其进行标记并适当地使用。</P>
<P>您可能希望在应用程序的用户界面中以不同的颜色或字体显示未确认的数据，以便用户可以知道这些数据是暂定的。通常，在已经确认数据之前，您的应用程序不应该允许在多个任务中使用这些数据。这可以防止未确认的数据溢出到其他需要已确认数据的活动中。使用已确认的数据并不能保证将来不会发生冲突，但应用程序起码将知道数据曾经被确认过并且随后已经被某个用户进行了更改。</P>
<H3>处理陈旧数据</H3>
<P>即使数据尚未更改，它也可能因为不再是最新数据而变得不再正确。这样的数据称为陈旧数据。当您设计智能客户端应用程序时，您需要确定如何处理陈旧数据以及如何防止您的智能客户端使用陈旧数据。这对于偶尔连接的智能客户端而言尤其重要，因为数据在客户端首次脱机时可能是最新的，但在客户端重新联机之前可能变为陈旧数据。此外，在客户端上为最新的数据在到达服务器时可能成为陈旧数据。例如，推销员可能在某个星期五使用有效数据为各种项目创建了一份订单，但是如果他或她没有在下个星期一之前将该订单提交给服务器，则这些项目的费用可能已经更改。</P>
<P><B>注</B> 如果在应用程序重新联机时，对服务请求进行排队并且做好发送准备，则该请求排队的时间越长，它遇到数据冲突或异常的可能性就越大。例如，如果您将某个服务请求（该请求包含许多项目的订单）排队，并且在很长一段时间内没有发送该请求，则您订购的项目可能停止生产或脱销。</P>
<P>您可以使用许多技术来处理陈旧数据。您可以使用元数据来说明数据的有效性并且显示数据的到期时间。这可以防止将陈旧数据传递到客户端。</P>
<P>在服务器上，您可以选择对来自客户端的任何数据进行检查，以确定它是否为陈旧数据，然后再根据情况决定是否允许它与服务器上的数据进行合并。如果该数据是陈旧数据，则可以在将该数据重新提交给服务器之前，确保客户端更新其引用数据。</P>
<P>对于偶尔连接的应用程序而言，陈旧数据的风险大于始终连接的应用程序。因此，智能客户端应用程序通常会执行附加的验证步骤以确保数据是有效的。通过向系统中添加额外的验证，您还可以确保您的服务对陈旧数据具有更高的容错性，并且在某些情况下，您或许能够在服务器上自动处理协调（即，将事务映射到新的帐户）。</P>
<P>有时候，陈旧消息是无法避免的。处理陈旧数据的方式应该以您要建模的业务的规则为基础。在某些情况下，陈旧数据是可以接受的。例如，假设为联机目录中的特定项目提交了一个订单。该项目具有一个目录编号，该编号由于联机目录更改已经陈旧。但是，该项目仍然可用且尚未更改，目录编号更改对系统没有影响，并且将生成正确的订单。 </P>
<P>另一方面，如果您要在两个帐户之间执行财务事务，并且其中一个帐户尚未关闭，则您无法执行该事务。这里，数据的陈旧性确实很重要。</P>
<P>一个很好的一般性规则是让业务对象为您处理陈旧数据的情况。您的业务对象可以验证数据是否是最新的，如果数据是陈旧的，则可以不做任何事情、用等效的最新数据协调陈旧数据、将信息传回到客户端以便更新或者使用业务规则自动执行适当的响应。</P>
<P>陈旧数据的协调可能发生在客户端、服务器或这两者。在服务器处理协调可以使您的应用程序容易地检测到冲突。在客户端处理协调可以免除那些可能需要手动解决任何冲突的用户或管理员的一些职责。</P>
<P>没有处理陈旧数据的最佳方式。您的业务规则可能规定，如果客户端无法解决冲突，则服务器是处理陈旧数据的最佳位置。如果服务器没有足够信息以自动处理这种情况，则您需要要求客户端在与服务器进行同步之前清理它的数据。相反，您可能决定陈旧数据完全适合于您的应用程序，在此情况下您没有什么好担心的。</P>
<H3>协调冲突</H3>
<P>当您分析您所在组织的数据协调要求时，您应该考虑组织的工作方式。在某些情况下，因为不同的用户负责不同的数据元素，所以有可能发生冲突。在其他情况下，冲突将更为频繁地发生，您必须确保您具有相应的机制来处理它们。</P>
<P>无论您采取什么样的预防措施，客户端都有可能向网络服务提交将导致违反业务规则或数据冲突的数据。当冲突确实发生时，远程服务应该尽可能多地提供有关数据冲突的详细信息。在某些情况下，数据冲突可能不是重大问题，并且可以由应用程序或服务器自动处理。例如，设想有一个客户关系管理 (CRM) 系统，并且用户更改了某个客户的电话号码。在服务器上更新该更改时，发现另一个用户也已经更改了该电话号码。您可能选择对您的系统进行相应的设计，以便使最新的更改总是优先，或者您可能希望将冲突发送给管理员。如果管理员知道是谁在什么时候进行了这些更改，则他或她就可以做出有关保留哪一个更改的决策。重要的一点在于服务器和应用程序提供足够的详细信息以便进行自动处理，或者为用户或管理员提供足够的信息以便他或她能够协调冲突。</P>
<P>数据协调可能是一个复杂且与具体情况有关的问题。每个业务和每个应用程序都具有稍微不同的规则、要求和假设。然而，您具有三个一般性的数据协调选项： </P>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>在服务器上自动协调数据 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>在客户端上自定义协调 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>第三方协调 </P></TD></TR></TBODY></TABLE>
<P>依次考察一下上述每个选项是有用的。</P>
<H3>在服务器上自动协调数据</H3>
<P>在某些情况下，您可以对您的应用程序进行相应的设计，以便服务器使用业务规则和自动过程来处理冲突，而不会影响客户端。您可以确保最新的更改总是优先，合并两个数据元素，或者采用更为复杂的业务逻辑。</P>
<P>在服务器上处理冲突对于可用性很有好处，并且使用户免于过多地涉及协调过程或忍受该过程带来的不便。您应该始终通过某种方法使客户端了解所采取的任何协调操作；例如，通过向客户端返回协调报告，并且解释冲突以及它的解决方式。这样就使客户端可以保持其本地数据的一致性并且将协调结果通知用户。</P>
<P>例如，假设应用程序允许用户为本地缓存的目录中的项目输入订单信息。如果用户订购的项目已经停止生产并且由较新但类似的型号取代，则订单服务可以选择用新项目替换原来的项目。然后，将向客户端通知该更改，以便它可以适当地修改它的本地状态。</P>
<H3>在客户端上自定义协调</H3>
<P>在某些情况下，客户端是执行协调的最佳位置，因为它了解有关原始请求的上下文的更多信息。应用程序或许能够自动解决冲突。在其他情况下，用户或管理员必须确定如何解决冲突。</P>
<P>要进行有效的客户端协调，服务应该向客户端发送足够的数据，以使客户端能够就如何解决冲突进行明智的决策。应该将冲突的确切详细信息报告给客户端，以便该客户端、用户或管理员可以确定解决问题的最佳方式。</P>
<H3>第三方协调</H3>
<P>在某些情况下，您可能希望第三方来协调任何数据冲突。例如，可以要求管理员或超级用户协调重要的数据冲突。他们可能是具有确定正确操作过程的职权的唯一用户。在此情况下，需要通知客户端决策已挂起。客户端或许能够通过使用暂定值继续工作，但通常它必须等待至基础冲突已经解决为止。当该冲突解决后，将通知客户端。作为替代方法，客户端还可以定期轮询以确定状态，然后在收到协调值时继续工作。</P>
<H3>与类似于 CRUD 的 Web 服务交互</H3>
<P>许多 Web 服务都是使用类似于 Create, Read, Update, Delete (CRUD) 的接口创建的。本节讨论几种用于创建消耗此类服务的偶尔连接应用程序的策略。</P>
<H3>Create </H3>
<P>在 CRUD Web 服务中，创建记录应该是一项比较简单的任务，前提是您能够正确地管理记录的创建。最重要的事情是唯一标识所创建的每个记录。大多数情况下，您可以通过使用唯一标识符作为记录的主键做到这一点。这样，即使在不同的客户端上创建了两个看上去完全相同的记录，在发生合并复制时也将把这些记录视为不同的记录。 </P>
<P><B>注</B> 在某些情况下，您可能不希望将记录视为唯一的。在这些情况下，您可以在两个记录冲突时生成异常。 </P>
<P>您可以使用多种方法在脱机客户端上创建唯一标识符。这些方法包括： </P>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>将记录作为不带唯一 ID 的数据传输对象 (DTO) 发送，并且让服务器分配 ID。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>使用客户端可以分配的全局唯一标识符 (GUID)，如 <B>System.Guid</B>。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>在客户端上分配临时 ID，然后在服务器上用真正的 ID 替代它。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>向每个客户端分配一组唯一的 ID。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>使用用户的姓名或 ID 作为所有已分配的 ID 和句柄的前缀，并且在客户端上递增它们，以便它们在默认情况下是全局唯一的。 </P></TD></TR></TBODY></TABLE>
<H3>Read</H3>
<P>读取操作没有数据冲突，因为读取操作按照定义是只读的。但是，对于偶尔连接的智能客户端中的读取操作而言，仍有可能发生问题。在客户端脱机之前，您应该在客户端上缓存所有需要读取的数据。该数据在客户端重新联机之前可能变为陈旧数据，从而导致客户端上出现不准确的数据，并且在与服务器进行同步时产生问题。有关处理陈旧数据的详细信息，请参阅本章前面的“处理陈旧数据”。</P>
<H3>Update</H3>
<P>数据更新最有可能导致数据冲突，因为多个用户可能更新相同的数据，从而在合并复制发生时导致冲突。您可以使用多种方法将发生冲突的可能性将至最低，并且在冲突确实发生时解决它们。有关详细信息，请参阅本章前面的“管理数据和业务规则冲突”。</P>
<H3>Delete</H3>
<P>删除记录是非常简单的，因为记录只能删除一次。尝试删除同一记录两次不会对系统产生任何影响。但是，在设计应用程序和 Web 服务以处理删除时，您应该记住几点。首先，您应该在客户端上将相关记录标记为暂时删除，然后在服务器上将删除请求排队。这意味着如果服务器由于某种原因无法删除该记录，则可以在客户端上撤消删除。</P>
<P>与创建记录时一样，您还必须通过使用唯一标识符来确保您引用了相关记录。这可以保证您总是在服务器上删除了正确的记录。</P>
<DIV style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 10px"><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter4OccConnSC.mspx#top"><IMG height=9 alt=返回页首 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_up.gif" width=7 border=0></A><A class=topOfPage href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter4OccConnSC.mspx#top"><FONT color=#002c99>返回页首</FONT></A></DIV><A name=EEAA></A>
<H2>使用基于任务的方法</H2>
<P>基于任务的方法使用对象将工作单元封装为用户任务。<B>Task</B> 对象负责处理用户完成特定任务所需的状态、服务和用户界面交互。当您设计和生成支持脱机操作的智能客户端应用程序时，基于任务的方法尤其有用，因为它使您可以将脱机行为的细节封装在单个位置。这使用户界面可以专门致力于解决与 UI 有关的问题，而不是致力于解决处理逻辑。通常，单个 <B>Task</B> 对象封装（由用户）与单个独立工作单元相关联的功能。任务的粒度和详细信息将取决于确切的应用程序方案。任务的一些示例包括： </P>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>输入订单信息。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>对客户联系人详细信息进行更改。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>撰写并发送电子邮件。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>更新订单状态。 </P></TD></TR></TBODY></TABLE>
<P>对于上述每个任务，都将实例化一个 <B>Task</B> 对象并使用它来指导用户完成该过程，存储所有需要的状态，与用户界面交互，并且与任何需要的服务交互。</P>
<P>当应用程序脱机工作时，它需要将服务请求排队，并且可能使用暂定值或未确认的值进行本地状态更改。在同步过程中，应用程序需要执行实际的服务请求，并且可能进行更多的本地状态更改以确认服务请求的成功。通过将该过程的细节封装在单个 <B>Task</B> 对象（该对象将服务请求放入队列中，并且跟踪暂定的和已确认的状态更改）中，您可以简化应用程序的开发，隔离实现更改，并且能够以标准方式处理所有任务。<B>Task</B> 对象可以通过各种属性和事件提供有关任务状态的详细信息，包括： </P>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>Pending status</B>。指示该任务是挂起的同步。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>Confirmed status</B>。指示该任务已经同步并且确认为成功。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>Conflict status</B>。指示在同步过程中发生了错误。其他属性将产生冲突或错误的详细信息。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>Completed</B>。指示完成的百分比或者将该任务标志为已完成。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>Task availability</B>。当应用程序联机或脱机时，某些任务将不可用；或者，如果任务是某个工作流或用户界面过程的一部分，则在已经完成作为先决条件的任务之前，该任务可能不可用。可以将该属性绑定到菜单项或工具栏按钮的启用标志，以防止用户启动不适当的任务。 </P></TD></TR></TBODY></TABLE>
<P>基于任务的方法的另一项好处是：它使应用程序可以致力于满足用户及其任务的需要，从而可以产生更为直观的应用程序。</P>
<DIV style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 10px"><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter4OccConnSC.mspx#top"><IMG height=9 alt=返回页首 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_up.gif" width=7 border=0></A><A class=topOfPage href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter4OccConnSC.mspx#top"><FONT color=#002c99>返回页首</FONT></A></DIV><A name=EDAA></A>
<H2>处理依赖性</H2>
<P>如果用户任务涉及到一个以上的服务请求，则需要非常小心地处理该任务，以便用户可以在脱机时完成整个任务。困难在于服务请求通常是相互依赖的。例如，假设您具有一个允许为客户预订假期的应用程序。为了预订假期，应用程序使用一些服务，按以下顺序执行整个任务的各个部分： </P>
<TABLE class=numberedList cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR vAlign=top>
<TD class=listNumber noWrap align=right>
<P>1.</P></TD>
<TD>
<P>预订汽车。 </P></TD></TR>
<TR vAlign=top>
<TD class=listNumber noWrap align=right>
<P>2.</P></TD>
<TD>
<P>预订旅馆房间。 </P></TD></TR>
<TR vAlign=top>
<TD class=listNumber noWrap align=right>
<P>3.</P></TD>
<TD>
<P>购买飞机票。 </P></TD></TR>
<TR vAlign=top>
<TD class=listNumber noWrap align=right>
<P>4.</P></TD>
<TD>
<P>发送电子邮件确认。 </P></TD></TR></TBODY></TABLE>
<P>上述每个服务都可能由不同的系统（甚至可能由不同的公司）实现。在理想情况下，每个服务请求每次都能成功，以便您的用户可以成功地预订汽车、旅馆和飞机票，并且应用程序可以发送电子邮件以通知客户端预订了假期。但是，并非所有服务请求都是成功的，因此您的应用程序必须能够解决错误条件，并且管理能够影响它处理整个任务的方式的业务规则。为这种任务编写代码极为困难，因为该任务的各个部分（即，对特定服务的各个服务请求）都依赖于该任务的其他部分。</P>
<P>依赖性本身可能依赖于复杂的业务逻辑，这使影响整个任务的逻辑进一步复杂化。例如，您的假期预订应用程序可能允许在没有汽车的情况下预订假期，前提是成功预订了旅馆和航班。各个服务请求之间的依赖性可以是正向 和反向 依赖性： </P>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>正向依赖性</B>。在同步过程中，如果第一个请求成功，但随后的请求失败，则您可能需要通过补偿事务反向第一个请求。这一要求可能大大增加应用程序的复杂性。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>反向依赖性</B>。如果应用程序正在脱机工作，并且将一个服务请求作为多服务请求任务的一部分提交，则它必须假设该请求将成功完成，以便它可以将后续的请求排队，并且不会妨碍用户完成该任务。在此情况下，所有后续请求都依赖于第一个请求的成功。如果第一个请求在同步过程中失败，则应用程序必须知道所有后续请求都需要删除或忽略。 </P></TD></TR></TBODY></TABLE>
<H3>在服务器处理依赖性</H3>
<P>要降低与服务请求之间的依赖性相关联的复杂性，Web 服务应该为每个用户任务提供单个服务请求。这就使用户可以把将要在同步阶段处理的任务作为对 Web 服务的单个原子请求来完成。单个原子请求消除了跟踪服务请求依赖性（这可能使应用程序的客户端或服务器端实现大大复杂化）的需要。</P>
<P>例如，您可以不像下面这样将服务接口编写为三个单独的步骤：</P><PRE class=codeSample>BookCar() 
BookHotel() 
BookAirlineTickets() 
</PRE>
<P>而是将它们合并为一个步骤：</P><PRE class=codeSample>BookVacation( Car car, Hotel hotel, Tickets airlineTickets ) 
</PRE>
<P>以这种方式合并步骤意味着，就客户端而言，您现在拥有了一个原子交互而不是三个单独的交互。在该示例中，<B>BookVacation</B> Web 服务将负责在构成该服务的元素之间执行必要的协调。</P>
<H3>在客户端处理依赖性</H3>
<P>您还可以在客户端上跟踪服务请求依赖性。该方法提供了相当大的灵活性，并且使客户端可以控制任意数量服务之间的协调。但是，该方法难以开发和测试。基于任务的方法是在客户端上跟踪服务请求依赖性的好方法，并且提供了一种在一个位置封装所需的全部业务逻辑和错误处理的方法，从而简化了开发和测试。（有关基于任务的方法的详细信息，请参阅本章前面的“使用基于任务的方法”。）</P>
<P>例如，用于预订假期的 <B>Task</B> 对象将知道它必须执行三个服务请求。它将实现必要的业务逻辑，以便它可以在遇到错误条件时适当地控制服务请求。如果 <B>BookCar</B> 服务调用失败，它可以通过 <B>BookHotel</B> 和 <B>BookAirlineTickets</B> 服务调用继续工作。如果 <B>BookAirlineTickets</B> 服务调用失败，则它将负责通过向每个服务创建一个补偿事务服务请求来取消任何旅馆或汽车预订。图 4.2 阐明了这一基于任务的方法。</P>
<DIV style="WIDTH: 440px"><IMG height=268 alt="" src="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/art/offlf01.gif" width=440 border=0><BR>
<P class=figureCaption><B>图</B><B> 4.2 </B>带有相互依赖性服务的基于任务的方法</P>
<DIV class=figureRule></DIV></DIV>
<H3>使用编排中间件</H3>
<P>有时候，应用程序中的依赖性和相应的业务规则十分复杂，需要某种形式的编排中间件（如 Microsoft BizTalk? Server）来协调多个 Web 服务和一个客户端应用程序之间的交互。编排中间件位于中间层，并且提供了一个表面 Web 服务来与智能客户端交互。表面 Web 服务向客户端呈现了一个特定于应用程序的适当接口，通过该接口可以只为每个用户任务产生单个 Web 请求。当收到服务请求时，编排服务将通过启动并协调对必要 Web 服务的调用（可能首先对结果进行整合，然后再将其返回到客户端）来处理该请求。该方法提供了一种用于解决多个 Web 服务之间交互的、可伸缩性更高的方法。BizTalk 还提供了重要的服务，如数据转换和业务规则引擎，这些服务可在与截然不同的 Web 服务或旧式系统交互时提供极大的帮助，还可以在复杂的业务情况下提供极大的帮助。另外，该方法还提供了重要的可用性和可靠性保证，从而有助于确保多个服务之间的一致性。图 4.3 阐明了编排中间件的用法。</P>
<DIV style="WIDTH: 450px"><IMG height=274 alt="" src="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/art/offlf02.gif" width=450 border=0><BR>
<P class=figureCaption><B>图</B><B> 4.3 </B>用于协调服务依赖性的编排中间件</P>
<DIV class=figureRule></DIV></DIV>
<DIV style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 10px"><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter4OccConnSC.mspx#top"><IMG height=9 alt=返回页首 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_up.gif" width=7 border=0></A><A class=topOfPage href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter4OccConnSC.mspx#top"><FONT color=#002c99>返回页首</FONT></A></DIV><A name=ECAA></A>
<H2>小结</H2>
<P>智能客户端需要在连接到网络以及从网络断开连接时有效地工作。当您设计智能客户端时，您需要确保它们可以在这两种情况下有效地工作，并且能够在这两种情况之间无缝地转换。 </P>
<P>有两种用于设计智能客户端通讯的概括性的策略：面向服务的策略和以数据为中心的策略。当您已经确定使用哪种策略之后，您需要进行一些基本的设计决策，以便使您的智能客户端能够脱机工作。在大多数情况下，应该将客户端设计为使用异步通讯和简单的网络交互。客户端需要缓存数据以便在脱机时使用，而且您需要一种在客户端重新联机后处理数据和业务规则冲突的方法。在许多情况下，脱机客户端允许用户执行一些相互依赖的任务。您需要处理这些依赖性，以防其中某个任务到达服务器时发生失败。您的智能客户端还可能需要与类似于 CRUD 的 Web 服务交互。</P>
<P>基于任务的方法可以显著简化将应用程序脱机的过程。请考虑在您的智能客户端中实现该方法；该方法还可以为您提供一种（在服务器和在客户端）处理依赖性的有效方法。</P>
<P><A href="http://msdn.microsoft.com/library/en-us/dnpag/html/SCAG-CH04.asp" target=_blank><FONT color=#002c99>转到原英文页面</FONT></A></P><img src ="http://www.blogjava.net/TrampEagle/aggbug/30143.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/TrampEagle/" target="_blank">TrampEagle</a> 2006-02-10 14:53 <a href="http://www.blogjava.net/TrampEagle/articles/30143.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>智能客户端体系结构与设计指南 第 3 章 — 建立连接</title><link>http://www.blogjava.net/TrampEagle/articles/30142.html</link><dc:creator>TrampEagle</dc:creator><author>TrampEagle</author><pubDate>Fri, 10 Feb 2006 06:52:00 GMT</pubDate><guid>http://www.blogjava.net/TrampEagle/articles/30142.html</guid><wfw:comment>http://www.blogjava.net/TrampEagle/comments/30142.html</wfw:comment><comments>http://www.blogjava.net/TrampEagle/articles/30142.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/TrampEagle/comments/commentRss/30142.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/TrampEagle/services/trackbacks/30142.html</trackback:ping><description><![CDATA[原文引自：<A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter1Introduction.mspx">http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter1Introduction.mspx</A><BR><BR>
<H1>第 3 章 — 建立连接</H1>
<H2 class=subtitle></H2>
<DIV class=date>发布日期： 8/20/2004<SPAN class=datePipe> | </SPAN>更新日期： 8/20/2004</DIV>
<DIV class=overview>
<DIV style="WIDTH: 250px"><IMG height=68 alt="" src="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/art/pponline.gif" width=250 border=0><BR>
<P class=figureCaption></P>
<DIV class=figureRule></DIV></DIV>
<P><B>智能客户端体系结构与设计指南</B></P>
<P>David Hill, Brenton Webster, Edward A. Jezierski, Srinath Vasireddy and Mohammad Al-Sabt, Microsoft Corporation; Blaine Wastell, Ascentium Corporation; Jonathan Rasmusson and Paul Gale, ThoughtWorks; and Paul Slater, Wadeware LLC</P>
<P><B>相关链接</B></P>
<P>Microsoft® <I>patterns &amp; practices </I>库 <A href="http://www.microsoft.com/resources/practices/default.mspx" target=_blank><FONT color=#002c99>http://www.microsoft.com/resources/practices/default.mspx</FONT></A></P>
<P><I>Application Architecture for .NET: Designing Applications and Services </I><A href="http://msdn.microsoft.com/library/en-us/dnbda/html/distapp.asp" target=_blank><FONT color=#002c99>http://msdn.microsoft.com/library/en-us/dnbda/html/distapp.asp</FONT></A></P>
<P><B>摘要</B>：本章介绍了应用程序可用来连接和使用网络资源以及客户计算机上组件或进程的许多方法，并且讨论了每种方法的优点和缺点。</P></DIV>
<CENTER><IMG title="" height=6 alt=* src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/3squares.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/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter3GettingConnected.mspx#EGAA"><IMG height=9 alt=松耦合系统和紧耦合系统 hspace=4 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_down.gif" width=7 vspace=2 border=0></A></TD>
<TD class=onThisPage><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter3GettingConnected.mspx#EGAA"><FONT color=#002c99>松耦合系统和紧耦合系统</FONT></A></TD></TR>
<TR vAlign=top>
<TD><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter3GettingConnected.mspx#EFAA"><FONT color=#002c99><IMG height=9 alt=通讯选项 hspace=4 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_down.gif" width=7 vspace=2 border=0></FONT></A></TD>
<TD class=onThisPage><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter3GettingConnected.mspx#EFAA"><FONT color=#002c99>通讯选项</FONT></A></TD></TR>
<TR vAlign=top>
<TD><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter3GettingConnected.mspx#EEAA"><FONT color=#002c99><IMG height=9 alt=选择通讯选项 hspace=4 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_down.gif" width=7 vspace=2 border=0></FONT></A></TD>
<TD class=onThisPage><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter3GettingConnected.mspx#EEAA"><FONT color=#002c99>选择通讯选项</FONT></A></TD></TR>
<TR vAlign=top>
<TD><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter3GettingConnected.mspx#EDAA"><FONT color=#002c99><IMG height=9 alt=设计连接的智能客户端应用程序 hspace=4 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_down.gif" width=7 vspace=2 border=0></FONT></A></TD>
<TD class=onThisPage><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter3GettingConnected.mspx#EDAA"><FONT color=#002c99>设计连接的智能客户端应用程序</FONT></A></TD></TR>
<TR vAlign=top>
<TD><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter3GettingConnected.mspx#ECAA"><FONT color=#002c99><IMG height=9 alt=小结 hspace=4 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_down.gif" width=7 vspace=2 border=0></FONT></A></TD>
<TD class=onThisPage><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter3GettingConnected.mspx#ECAA"><FONT color=#002c99>小结</FONT></A></TD></TR></TBODY></TABLE>
<P>按照定义，智能客户端需要连接到其他资源并与这些资源进行通讯，并且构成分布式应用程序的一部分。这些资源可以是客户端进程或组件，也可以是网络资源，如 Web 服务。 </P>
<P>本章分析智能客户端与其他资源之间的通讯的性质。本章将考察可用来连接和使用其他进程、组件或远程服务中的资源的不同技术，并且讨论如何对它们进行取舍。最后，本章将分析如何最好地设计您的智能客户端以连接到资源。</P><A name=EGAA></A>
<H2>松耦合系统和紧耦合系统</H2>
<P>客户端应用程序可以用不同的方式连接和使用其他进程（包括本地进程和网络进程）中的组件和服务。按照服务与客户端之间存在多少耦合性对不同的方法进行分类是很有用的。</P>
<P>耦合性是指组件（在分布式系统中）互相依赖的程度。客户端与它们同其进行通讯的服务之间耦合的性质可能影响智能客户端设计的许多方面，包括互操作性、脱机功能、网络通讯性能、部署以及维护注意事项。</P>
<P>紧耦合系统通常提供直接的对象到对象通讯，并且客户端上的对象对远程对象具有详细的了解。这种紧耦合性可以防止对客户端或服务器进行单独更新。因为紧耦合系统涉及直接的对象到对象通讯，所以对象通常比在松耦合系统中更为频繁地交互，这样，如果两个对象位于不同的计算机上并且由网络连接分隔，则可能导致性能和延迟问题。</P>
<P>松耦合系统通常是基于消息的系统，此时客户端和远程服务并不知道对方是如何实现的。客户端和服务之间的通讯由消息的架构支配。只要消息符合协商的架构，则客户端或服务的实现就可以根据需要进行更改，而不必担心会破坏对方。</P>
<P>松耦合通讯机制提供了紧耦合机制所没有的许多优点，并且它们有助于降低客户端和远程服务之间的依赖性。但是，紧耦合性通常可以提供性能好处，便于在客户端和服务之间进行更为紧密的集成（这在存在安全性和事务处理要求时，可能是必需的）。</P>
<P>所有与远程服务或组件通讯的分布式客户端都具有某种程度的耦合性。您需要了解各种松耦合方法和紧耦合方法具有的不同特征，以便为您的应用程序选择合适程度的耦合性。</P>
<DIV style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 10px"><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter3GettingConnected.mspx#top"><IMG height=9 alt=返回页首 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_up.gif" width=7 border=0></A><A class=topOfPage href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter3GettingConnected.mspx#top"><FONT color=#002c99>返回页首</FONT></A></DIV><A name=EFAA></A>
<H2>通讯选项</H2>
<P>当您设计智能客户端应用程序时，您可以从多种将其连接到其他资源的方法中进行选择，这些方法包括： </P>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>Microsoft_ .NET Enterprise Services</B><B>。</B></P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>Microsoft .NET remoting</B><B>。</B></P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>Microsoft Windows_ </B><B>消息队列（也称为</B><B> MSMQ</B><B>）。</B></P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>Web </B><B>服务。</B></P></TD></TR></TBODY></TABLE>
<H3>.NET Enterprise Services</H3>
<P>您可以使用 .NET Enterprise Services 提供对托管代码组件和应用程序的 COM+ 服务基础结构的访问。.NET 组件依赖 COM+ 为其提供许多组件服务，如： </P>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>Transaction support. </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>Role-based security. </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>Loosely coupled events. </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>Object pooling. </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>Queued components. </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>Just-in-time activation. </P></TD></TR></TBODY></TABLE>
<P>使用 COM+ 服务的 .NET 组件称为<I>服务组件</I>。因为您的服务组件以 COM+应用程序为宿主，所以它们必须可供该应用程序访问。这就为服务组件带来了一些注册和配置要求： </P>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>程序集必须从 <B>System.EnterpriseServices</B> 命名空间中的 <B>ServicedComponent</B> 类派生。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>程序集必须是强命名的。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>程序集必须在 Microsoft Windows 注册表中注册。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>必须将程序集的类型库定义注册和安装到特定的 COM+应用程序中。 </P></TD></TR></TBODY></TABLE>
<P>如果程序集包含被配置为进程外应用程序的服务组件，则应该将相应的程序集放到全局程序集缓存中。如果程序集包含被配置为进程内库的服务组件，则不必将其放到全局程序集缓存中，除非它们位于与应用程序不同的目录中。如果您以这种方式部署同一版本服务组件的多个副本，则 COM+ 目录包含该组件的所有实例的全局配置；您无法逐个副本地对它们进行配置。</P>
<P>下面的代码示例显示了一个需要事务的组件，并且提供了一种在该事务内向数据库中写入数据的方法。</P><PRE class=codeSample>using System.EnterpriseServices; 
[Transaction( TransactionOption.Required )] 
public class CustomerAccount : ServicedComponent 
{ 
[AutoComplete] 
public bool UpdateCustomerName( int customerID, string customerName ) 
{ 
            // Updates the database, no need to call SetComplete. 
            // Calls SetComplete automatically if no exception is thrown. 
} 
} 
</PRE>
<P>服务组件通常可以在首次运行时动态注册。这种类型的注册称为<I>惰性注册</I>。当托管代码应用程序首次尝试创建服务组件的实例时，公共语言运行库 (CLR) 将注册程序集和类型库，并且配置 COM+ 目录。对于特定版本的程序集，注册只发生一次。通过惰性注册，您可以使用 Xcopy 部署来部署服务组件，并且无须显式注册服务组件就可以在开发周期中使用它们。 </P>
<P>惰性注册是注册服务组件的最容易的方法，但它只在运行这些服务组件的进程具有管理特权时才有效。而且，任何被标记为 COM+ 服务器应用程序的程序集都要求显式注册；对于调用托管服务组件的非托管客户端而言，动态注册不起作用。如果使用服务组件的进程不具有动态注册所需的特权，则您需要使用 .NET 框架随附的 Regsvcs.exe 工具显式注册包含该服务组件的程序集。</P>
<P>惰性注册和 Regsvcs.exe 都需要客户计算机上的管理权限，因此如果您的应用程序包含服务组件，则无法使用非接触式部署来部署它。有关详细信息，请参阅<A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter7DeployUpdSCApp.mspx"><FONT color=#002c99>第 7 章：部署和更新智能客户端</FONT></A>。</P>
<P>可以用多种不同的方式托管和访问服务组件。它们可以由 ASP.NET应用程序托管并通过 HTTP 访问，或者可以通过 SOAP 或 DCOM（默认设置）访问它们。但是，如果 COM+ 服务需要与调用一起流动（例如，如果您需要用户的标识或分布式事务从您的应用程序流动到服务组件），则 DCOM 是唯一可行的解决方案。</P>
<P><B>注</B> 如果您使用 DCOM 与 COM+ 应用程序通讯，则需要将 interop 程序集部署到客户计算机，就像您部署传统 COM 组件那样。</P>
<P>Enterprise Services 具有许多您可以在智能客户端应用程序中使用的强大组件功能。然而，您通常只应该在单个进程内、在单台客户计算机上或者在服务器上的服务边界内使用这些功能。由于 Enterprise Services 所具有的紧耦合性质，它们通常不是在智能客户端应用程序和位于远程系统上的服务之间进行通讯的最佳选择。如果您的智能客户端应用程序需要在本地使用 COM+ 服务（例如，事务支持、对象池或基于角色的安全性），请使用 Enterprise Services。</P>
<P>有关 Enterprise Services 的详细信息，请参阅 .NET Framework Developer's Guide 中的“Writing Serviced Components”，网址为：<A href="http://msdn.microsoft.com/library/en-us/cpguide/html/cpconwritingservicedcomponents.asp?frame=true" target=_blank><FONT color=#002c99>http://msdn.microsoft.com/library/en-us/cpguide/html/cpconwritingservicedcomponents.asp?frame=true</FONT></A>。</P>
<H3>.NET remoting</H3>
<P>.NET remoting 提供了灵活且可扩展的远程过程调用 (RPC) 机制，.NET 组件可以通过该机制进行通讯。通过 .NET remoting，您可以使用多种通讯协议（如 HTTP 或 TCP）、数据编码选项（包括 XML、SOAP 和二进制编码）和各种对象激活模型。它可以提供在对象之间进行通讯的快速而有效的手段。</P>
<P>.NET remoting 使您可以通过使用显示为远程对象的代理对象，像调用本地对象一样调用远程对象。.NET remoting 基础结构通过属性和方法调用处理客户端代码和远程对象之间的交互，在此期间对在它们之间传递的数据进行编码，并且管理远程目标对象的创建和删除。</P>
<P>.NET remoting 基础结构要求客户端具有关于远程对象的公共方法和属性的详细知识，以便提供客户端代理。确保客户端具有该知识的一种方法是向客户端分发该远程对象的完整实现。然而，更为有效的方法是将公共方法和属性包括到接口定义中，并且将这些接口编译为它们自己的程序集。然后，客户端可以使用接口程序集来提供适当的代理，而远程对象可以使用接口程序集来实现必要的功能。而且，使用该技术时，您无须将完整的远程对象重新分发到客户端，就能够更新这些远程对象的实现。</P>
<P>您可以用多种方法来管理远程对象的生存期。可以按照需要创建对象以满足单个请求，或者可以通过使用租用机制更细致地控制它们的生存期 — 此时，客户端维护对远程对象的租用，并且只要客户端希望使用远程对象，就必须使远程对象保持活动。.NET remoting 还可以保证对于所有客户端，只存在一个对象实例。您可以根据您对状态管理和可伸缩性的要求，选择应用程序的生存期。</P>
<P>.NET remoting 的可扩展基础结构使您可以创建自定义信道和接收器。自定义信道使您可以定义通过网络传输数据的方式。例如，您可以定义自定义信道以实现自定义有线协议。自定义接收器使您可以截获在对象之间发送的数据，并对其执行操作。例如，您可以定义自定义接收器在传输前后对数据进行加密或压缩。</P>
<P>.NET remoting 具有用于在对象之间进行通讯的强大且可扩展的机制。但是，由于它的紧耦合性质，在某些情形下它可能并不适合。.NET remoting 在客户端和服务器都需要 .NET 实现的对象；因此，对于要求在不同环境之间具有互操作性的情形，它并不适合。对于在客户端和服务器之间采用紧耦合 RPC 风格交互并不适当的情形，.NET remoting 也是不适合的。默认情况下，.NET remoting 不会提供任何内置的加密机制或用于在对象之间传递用户标识或事务的机制。对于这些情形，应该使用 Enterprise Services。</P>
<P>但是，对于在客户计算机上或服务边界内不同进程中的对象之间的通讯而言，以及对于不同应用程序域中的对象而言，.NET remoting 都是一种很好的选择。</P>
<P>有关使用 .NET remoting 的详细信息，请参阅“An Introduction to Microsoft .NET remoting Framework”，网址为：<A href="http://msdn.microsoft.com/library/en-us/dndotnet/html/introremoting.asp?frame=true" target=_blank><FONT color=#002c99>http://msdn.microsoft.com/library/en-us/dndotnet/html/introremoting.asp?frame=true</FONT></A>。</P>
<P>有关对 Web 服务和远程处理进行取舍的信息，请参阅“ASP.NET Web Services or Remoting: How to Choose”，网址为：<A href="http://msdn.microsoft.com/library/en-us/dnbda/html/bdadotnetarch16.asp?frame=true" target=_blank><FONT color=#002c99>http://msdn.microsoft.com/library/en-us/dnbda/html/bdadotnetarch16.asp?frame=true</FONT></A>。</P>
<H3>消息队列</H3>
<P>借助于 Microsoft Windows 消息队列，您可以很容易地通过发送和接收消息，快速而可靠地与应用程序通讯。消息处理为您提供了有保证的消息传递以及执行许多业务过程的可靠方式。消息队列提供了一种您可以在智能客户端应用程序内使用的松耦合通讯机制。消息队列具有下列功能： </P>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>有保证的消息传递</B>。消息队列通过将消息存储在队列中直到其可以传递，保证了即使远程系统失败或不存在，消息也能够传递。因此，与组件之间的直接调用相比，消息受失败的影响的程度要小得多。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>消息优先级化</B>。更为紧急或重要的消息可以在不太重要的消息之前收到，从而有助于保证为关键应用程序提供足够的响应时间。 </P>
<P><B>注</B> 您只能为非事务性消息设置消息优先级。</P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>脱机功能。如果消息因为客户端脱机而无法传递，则会将它们存储在待发队列中，并且在客户端重新联机时自动传递它们。用户在无法访问目标队列时可以继续执行操作。同时，其他操作可以像消息已被处理一样继续执行，因为当网络连接还原时可以保证传递消息。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>事务性消息处理</B>。您将消息作为事务的一部分发送。这样，您就可以发送多个相关消息，或者将您的应用程序设计为参与分布式事务，并且确保所有消息都按顺序传递并且只传递一次。如果事务内发生任何错误，则整个事务都将被取消，并且不会发送任何消息。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>安全性</B>。MessageQueue 组件所基于的消息队列技术使用 Windows 安全性来确保访问控制的安全、提供审核以及对您的组件发送和接收的消息进行加密和身份验证。可以在网络上对消息队列消息进行加密，以使其不会被包嗅探器截获。您还可以禁止队列接收未加密的消息。 </P></TD></TR></TBODY></TABLE>
<P>使用消息队列的应用程序可以通过使用 <B>System.Messaging </B>命名空间中的类来发送消息以及从队列中读取消息。<B>Message</B> 类用于封装要发送到队列的消息，而 <B>MessageQueue</B> 类提供了对特定队列及其属性的访问。</P>
<P>您需要在任何使用消息队列的计算机上安装和配置它。Windows 桌面操作系统和 Microsoft Windows CE .NET 都可以使用消息队列，从而使您可以在移动设备（如 Pocket PC 设备）上使用它。</P>
<P>要与提供基于消息的访问的服务交互，消息队列是一种很好的选择。您可以使用消息队列与其他装有消息队列的系统通讯。尽管您可以使用连接工具包与其他消息处理系统（如 IBM 的 MQSeries）通讯，但与其他系统之间的互操作性是有限的。</P>
<P>有关使用消息队列的详细信息，请参阅 Microsoft Platform SDK 文档资料中的“Message Queuing (MSMQ)”，网址为：<A href="http://msdn.microsoft.com/library/en-us/msmq/msmq_overview_4ilh.asp?frame=true" target=_blank><FONT color=#002c99>http://msdn.microsoft.com/library/en-us/msmq/msmq_overview_4ilh.asp?frame=true</FONT></A>。</P>
<P>有关 MSMQ-MQSeries 跨接编程的信息，请参阅“Programming Considerations Using MSMQ-MQSeries Bridge Extensions”，网址为：<A href="http://msdn.microsoft.com/library/en-us/his/htm/_sna_programming_considerations_using_msmq_mqseries_bridge_extensions_appl.asp" target=_blank><FONT color=#002c99>http://msdn.microsoft.com/library/en-us/his/htm/_sna_programming_considerations_using_msmq_mqseries_bridge_extensions_appl.asp</FONT></A>。</P>
<H3>Web 服务</H3>
<P>Web 服务是具有下列功能的应用程序组件： </P>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>通过标准的 Web 服务协议向其他 Web 服务和应用程序公开有用的功能。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>提供其接口的详细说明，使您可以生成与它通讯的客户端应用程序。说明是在名为 Web 服务描述语言 (WSDL) 文档的 XML 文档中提供的。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>通过使用 XML 架构说明其消息。 </P></TD></TR></TBODY></TABLE>
<P>Web 服务的基于 SOAP 的 XML 消息可以具有显式（结构化和类型化）部分或宽松定义部分（使用任意 XML）。这意味着 Web 服务既可以是松耦合的，也可以是紧耦合的，并且可用于实现基于消息或 RPC 风格的系统，具体取决于环境的准确性要求。 </P>
<P>您可以使用 Web 服务在异类环境中的组织内部以及组织之间生成模块化的应用程序。这些应用程序可以与种类繁多的实现、平台和设备互操作。任何能够通过 HTTP 发送 XML 的系统都可以使用 Web 服务。因为 Web 服务是基于标准的，所以用不同语言编写以及位于不同平台上的系统可以相互使用对方的服务。这通常被称为面向服务的体系结构。</P>
<P>用于 Web 服务的主要标准是 HTTP、SOAP、UDDI 和 WSDL。Web 服务与传输协议无关。但是，HTTP 是最常见的用于传输 SOAP 消息的机制。因此，Web 服务非常适合于横跨网络和企业防火墙的应用程序，如需要通过 Internet 与服务通讯的智能客户端。</P>
<P>许多 Web 服务标准正在出现，以扩展 Web 服务的功能。Microsoft Web Services Enhancements (WSE) 2.0 支持正在出现的 Web 服务标准，如 WS-Security、WS-SecureConversation、WS-Trust、WS-Policy、WS-Addressing、WS-Referrals、WS-Attachments 和 Direct Internet Message Encapsulation (DIME)。WSE 提供了编程模型，以实现它所支持的各种规范。有关详细信息，请参阅“Web Service Enhancements (WSE)”，网址为：<A href="http://msdn.microsoft.com/webservices/building/wse/default.aspx" target=_blank><FONT color=#002c99>http://msdn.microsoft.com/webservices/building/wse/default.aspx</FONT></A>。</P>
<P>有关 SOAP 的详细信息，请参阅“Understanding SOAP”，网址为：<A href="http://msdn.microsoft.com/library/en-us/dnsoap/html/understandsoap.asp" target=_blank><FONT color=#002c99>http://msdn.microsoft.com/library/en-us/dnsoap/html/understandsoap.asp</FONT></A>。 </P>
<P>有关 WS-Security 的详细信息，请参阅“Web Services Security Specifications Index Page”，网址为：<A href="http://msdn.microsoft.com/library/en-us/dnglobspec/html/wssecurspecindex.asp" target=_blank><FONT color=#002c99>http://msdn.microsoft.com/library/en-us/dnglobspec/html/wssecurspecindex.asp</FONT></A>。</P>
<P>Web 服务通讯可以是粗粒度的、独立的和无状态的。但是，与其他形式的通讯相比，Web 服务通常是非常详细的。</P>
<P>Web 服务是生成大多数智能客户端应用程序的最佳方法。高度的互操作性使 Web 服务能够与各种各样的应用程序通讯。使用广泛采用的标准意味着它们通常只须进行最低限度的额外配置（与要求打开专用端口的其他技术比较），就可以通过网络基础结构和防火墙，Microsoft Visual Studio® 开发系统中对于 Web 服务的强大支持意味着您可以在单个开发环境中使用它们。</P>
<P>在性能极高的应用程序中，Web 服务可能并不适当，因为它们太详细，并且与其他消息处理技术（如 .NET remoting 和消息队列）相比，它们包含相当繁重的消息有效负载。 </P>
<P>有关使用和生成 Web 服务的详细信息，请参阅 <I>.NET Framework Developer's Guide</I> 中的“XML Web Services Created Using ASP.NET and XML Web Service Clients”，网址为：<A href="http://msdn.microsoft.com/library/en-us/cpguide/html/cpconaspnetbuildingwebservicesaspnetwebserviceclients.asp?frame=true" target=_blank><FONT color=#002c99>http://msdn.microsoft.com/library/en-us/cpguide/html/cpconaspnetbuildingwebservicesaspnetwebserviceclients.asp?frame=true</FONT></A>。</P>
<DIV style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 10px"><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter3GettingConnected.mspx#top"><IMG height=9 alt=返回页首 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_up.gif" width=7 border=0></A><A class=topOfPage href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter3GettingConnected.mspx#top"><FONT color=#002c99>返回页首</FONT></A></DIV><A name=EEAA></A>
<H2>选择通讯选项</H2>
<P>不同的通讯选项适用于不同的场合。表 3.1 概括了用于建立连接的不同选项。</P>
<P><B>表</B><B> 3.1 </B><B>智能客户端选项</B></P>
<TABLE class=dataTable id=ECEAA cellSpacing=0 cellPadding=0>
<THEAD>
<TR class=stdHeader vAlign=top>
<TD id=colECBCEAA>选项</TD>
<TD id=colEBBCEAA>优点</TD>
<TD id=colEABCEAA style="BORDER-RIGHT: #cccccc 1px solid">缺点</TD></TR></THEAD>
<TBODY>
<TR class=record vAlign=top>
<TD>
<P class=lastInCell>Enterprise Services</P></TD>
<TD>
<P>提供对 COM+ 服务的访问</P>
<P>使标识能够与调用一起流动</P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P>要求在客户端安装服务组件</P>
<P>仅适合于同一进程或计算机</P></TD></TR>
<TR class=evenRecord vAlign=top>
<TD>
<P class=lastInCell>.NET remoting </P></TD>
<TD>
<P>快速</P>
<P>可插接</P>
<P>支持自定义协议</P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P>要求 .NET 框架运行</P>
<P>专用</P>
<P>无法在不打开 RPC 端口的情况下通过防火墙</P>
<P>无安全性基础结构</P></TD></TR>
<TR class=record vAlign=top>
<TD>
<P class=lastInCell>Message Queuing</P></TD>
<TD>
<P>对于与消息处理系统通讯很有用</P>
<P>安全</P>
<P>有保证的消息传递</P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P>要求在客户端上配置消息队列</P>
<P>不能容易地与其他系统集成</P></TD></TR>
<TR class=evenRecord vAlign=top>
<TD>
<P class=lastInCell>Web services</P></TD>
<TD>
<P>支持集成</P>
<P>可扩展</P>
<P>强大的行业支持</P>
<P>定义明确的标准</P>
<P>供应商/语言无关</P>
<P>安全</P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P>详细</P>
<P>性能比 .NET remoting 低</P></TD></TR></TBODY></TABLE>
<DIV class=dataTableBottomMargin></DIV>
<P>如表 3.1 所示，在某些情况下，Enterprise Services、NET remoting 和消息队列可能是用于在智能客户端和连接的资源之间进行通讯的适当技术。但是，在大多数情况下，Web 服务是用于将智能客户端应用程序连接到服务的最佳机制。</P>
<P>围绕 Web 服务通讯生成的体系结构可以在连接的环境和脱机环境中很好地工作，并且支持能够自我描述且独立的粗粒度、无状态消息。对于 Internet 协议的依赖使得客户端能够广泛分发给 Internet 上的每个人。 </P>
<DIV style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 10px"><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter3GettingConnected.mspx#top"><IMG height=9 alt=返回页首 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_up.gif" width=7 border=0></A><A class=topOfPage href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter3GettingConnected.mspx#top"><FONT color=#002c99>返回页首</FONT></A></DIV><A name=EDAA></A>
<H2>设计连接的智能客户端应用程序</H2>
<P>当您设计您的智能客户端时，您应该考虑一些建议，包括： </P>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>使用粗粒度的、封装的消息。</B></P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>避免分布式</B><B> ACID </B><B>事务。</B></P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>避免在网络中发送数据集。</B></P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>将大型数据集分解。</B></P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>将您的</B><B> Web </B><B>服务和程序集版本化。</B></P></TD></TR></TBODY></TABLE>
<H3>使用粗粒度的、封装的消息</H3>
<P>分布式网络调用是代价高昂的操作。您不应该使用与设计本地接口相同的细粒度方法来设计您的外部接口。要避免消息之间存在消息依赖性，比较好的做法是将接口方法生成为独立函数。这样做可以使您不必编写复杂的跟踪协调代码来处理依赖于其他消息成功完成的消息的失败。</P>
<H3>避免分布式 ACID 事务</H3>
<P>分布式 ACID（原子、一致、独立、持久）事务是资源密集型的，伴随大量网络通信以及挂起本地事务上的大量相互依赖的系统锁。如果您的智能客户端或服务正在等待答复，并且在收到该答复之前无法继续工作，则分布式 ACID 事务可能阻止业务过程。</P>
<P>如果您的智能客户端可能不加警告就切换到脱机模式，则分布式 ACID 事务的问题将会恶化。在此情况下，客户端可能对数据加锁，并且在可以在服务器释放该锁之前进入脱机模式。</P>
<P>如果您无法通过将接口分解为单独的不连续消息来避免消息依赖性，则您具有许多处理事务的选项，并且还可以避免分布式 ACID 事务： </P>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>将紧耦合消息提交给服务器，并且让事务协调器（如 Microsoft BizTalk® Server）来处理消息依赖性。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>自己在客户端或服务器上编写事务补偿代码。使用相应的通讯协议，以便服务器可以用来决定何时启动事务，以及如何通知客户端要完整处理的事务成功完成或失败。 </P></TD></TR></TBODY></TABLE>
<H3>避免在网络中发送数据集</H3>
<P>数据集可能太大、太详细，因而无法用作在多个层之间发送数据的通讯有效负载机制。取而代之，您应该使用数据传输对象 (DTO) 来减少发送到外部接口的消息有效负载。对于数据更改，您应该考虑只发送已更改的数据，而不是发送整个数据集。</P>
<P>有关 DTO 的详细信息，请参阅<A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter2HandlingData.mspx"><FONT color=#002c99>第 2 章：处理数据</FONT></A>。</P>
<H3>将大型数据集分解</H3>
<P>如果您试图同时显示大型数据集的所有内容，则它们可能在客户端导致性能问题。因此，您应该将它们分解为较小的数据集。以这种方式分解数据称为分页。例如，与显示电话目录的全部内容不同，您可以选择一次显示一页（例如，每屏按字母顺序显示 20 个记录）。如果您将客户端设计为使用分页，则应该确保对用户界面进行相应的设计，以便使用户可以容易地在各个页之间导航。 </P>
<P>这一分解大型数据集的概念还适用于通过网络与服务器进行的通讯。如果您可以将数据分解为可管理的数据块，则您随后可以按照需要加载所需的数据，这种技术称为<I>惰性加载</I>。在电话目录示例中，只有当前操作需要的数据将被加载，从而减小了对应用程序和网络的影响，并且可能使二者的响应更为迅速。</P>
<P>要改善用户体验，可以在对即将到来的用户请求进行预测的基础上，使用附加线程执行后台处理以及与服务之间的通讯。</P>
<P>尽管对惰性加载的支持可能是智能客户端应用程序设计的重要方面，但您应该记住应用程序的脱机要求。通过网络传输的惰性加载数据可能会妨碍应用程序像您希望的那样脱机工作。</P>
<H3>将您的 Web 服务和程序集版本化</H3>
<P>当您将新版本的智能客户端软件发布到客户端或者对该软件进行升级时，应该创建新版本的程序集。如果您使用版本化的程序集，并且如果您将服务器服务设计为支持向后兼容接口，则可以支持多个版本的客户端软件。在发布新版本的 Web 服务时，您应该通过规范的命名约定来区分它们。可以改变各个版本的命名空间，以便它们包含日期信息，从而能够清楚正在与哪个版本的 Web 服务客户端通讯。</P>
<P>有关处理多个版本的程序集的详细信息，请参阅<A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter7DeployUpdSCApp.mspx"><FONT color=#002c99>第 7 章：部署和更新智能客户端</FONT></A>。</P>
<DIV style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 10px"><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter3GettingConnected.mspx#top"><IMG height=9 alt=返回页首 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_up.gif" width=7 border=0></A><A class=topOfPage href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter3GettingConnected.mspx#top"><FONT color=#002c99>返回页首</FONT></A></DIV><A name=ECAA></A>
<H2>小结</H2>
<P>智能客户端需要访问资源（包括本地资源和远程资源）才能正常工作。要成功地设计可靠的并且能够迅速响应用户操作的智能客户端，您如何处理这一通讯可能非常关键。诸如性能、安全性和灵活性之类的要求会影响到哪种连接选择适合于您的环境。使用本章中的指导，您应该能够确定哪些形式的连接适合于您的智能客户端，然后相应地设计您的智能客户端以及它们与之进行通讯的资源。</P>
<P><A href="http://msdn.microsoft.com/library/en-us/dnpag/html/SCAG-CH03.asp" target=_blank><FONT color=#002c99>转到原英文页面</FONT></A></P><img src ="http://www.blogjava.net/TrampEagle/aggbug/30142.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/TrampEagle/" target="_blank">TrampEagle</a> 2006-02-10 14:52 <a href="http://www.blogjava.net/TrampEagle/articles/30142.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>智能客户端体系结构与设计指南 第 2 章 — 处理数据</title><link>http://www.blogjava.net/TrampEagle/articles/30141.html</link><dc:creator>TrampEagle</dc:creator><author>TrampEagle</author><pubDate>Fri, 10 Feb 2006 06:51:00 GMT</pubDate><guid>http://www.blogjava.net/TrampEagle/articles/30141.html</guid><wfw:comment>http://www.blogjava.net/TrampEagle/comments/30141.html</wfw:comment><comments>http://www.blogjava.net/TrampEagle/articles/30141.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/TrampEagle/comments/commentRss/30141.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/TrampEagle/services/trackbacks/30141.html</trackback:ping><description><![CDATA[<H1><FONT size=4>引自：</FONT><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter1Introduction.mspx"><FONT size=4>http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter1Introduction.mspx</FONT></A></H1>
<H1>第 2 章 — 处理数据</H1>
<H2 class=subtitle></H2>
<DIV class=date>发布日期： 8/20/2004<SPAN class=datePipe> | </SPAN>更新日期： 8/20/2004</DIV>
<DIV class=overview>
<DIV style="WIDTH: 250px"><IMG height=68 alt="" src="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/art/pponline.gif" width=250 border=0><BR>
<P class=figureCaption></P>
<DIV class=figureRule></DIV></DIV>
<P><B>智能客户端体系结构与设计指南</B></P>
<P>David Hill, Brenton Webster, Edward A. Jezierski, Srinath Vasireddy and Mohammad Al-Sabt, Microsoft Corporation; Blaine Wastell, Ascentium Corporation; Jonathan Rasmusson and Paul Gale, ThoughtWorks; and Paul Slater, Wadeware LLC</P>
<P>相关链接</P>
<P>Microsoft® <I>patterns &amp; practices</I> 库 <A href="http://www.microsoft.com/resources/practices/default.mspx" target=_blank><FONT color=#002c99>http://www.microsoft.com/resources/practices/default.mspx</FONT></A></P>
<P><I>Application Architecture for .NET: Designing Applications and Services</I><A href="http://msdn.microsoft.com/library/en-us/dnbda/html/distapp.asp" target=_blank><FONT color=#002c99>http://msdn.microsoft.com/library/en-us/dnbda/html/distapp.asp</FONT></A></P>
<P><B>摘要</B>：本章分析在客户端处理数据时的各种注意事项，包括数据缓存、数据并发以及数据集和 Windows 窗体数据绑定的使用。</P></DIV>
<CENTER><IMG title="" height=6 alt=* src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/3squares.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/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter2HandlingData.mspx#EHAA"><IMG height=9 alt=数据类型 hspace=4 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_down.gif" width=7 vspace=2 border=0></A></TD>
<TD class=onThisPage><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter2HandlingData.mspx#EHAA"><FONT color=#002c99>数据类型</FONT></A></TD></TR>
<TR vAlign=top>
<TD><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter2HandlingData.mspx#EGAA"><FONT color=#002c99><IMG height=9 alt=缓存数据 hspace=4 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_down.gif" width=7 vspace=2 border=0></FONT></A></TD>
<TD class=onThisPage><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter2HandlingData.mspx#EGAA"><FONT color=#002c99>缓存数据</FONT></A></TD></TR>
<TR vAlign=top>
<TD><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter2HandlingData.mspx#EFAA"><FONT color=#002c99><IMG height=9 alt=数据并发 hspace=4 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_down.gif" width=7 vspace=2 border=0></FONT></A></TD>
<TD class=onThisPage><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter2HandlingData.mspx#EFAA"><FONT color=#002c99>数据并发</FONT></A></TD></TR>
<TR vAlign=top>
<TD><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter2HandlingData.mspx#EEAA"><FONT color=#002c99><IMG height=9 alt="使用 ADO.NET 数据集来管理数据" hspace=4 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_down.gif" width=7 vspace=2 border=0></FONT></A></TD>
<TD class=onThisPage><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter2HandlingData.mspx#EEAA"><FONT color=#002c99>使用 ADO.NET 数据集来管理数据</FONT></A></TD></TR>
<TR vAlign=top>
<TD><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter2HandlingData.mspx#EDAA"><FONT color=#002c99><IMG height=9 alt="Windows 窗体数据绑定" hspace=4 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_down.gif" width=7 vspace=2 border=0></FONT></A></TD>
<TD class=onThisPage><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter2HandlingData.mspx#EDAA"><FONT color=#002c99>Windows 窗体数据绑定</FONT></A></TD></TR>
<TR vAlign=top>
<TD><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter2HandlingData.mspx#ECAA"><FONT color=#002c99><IMG height=9 alt=小结 hspace=4 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_down.gif" width=7 vspace=2 border=0></FONT></A></TD>
<TD class=onThisPage><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter2HandlingData.mspx#ECAA"><FONT color=#002c99>小结</FONT></A></TD></TR></TBODY></TABLE>
<P>在智能客户端中，可在客户端上使用应用程序数据。要使您的智能客户端有效工作，很重要的一点是对该数据进行适当的管理，以确保其有效、一致和安全。</P>
<P>可以通过服务器端应用程序（例如，通过 Web 服务）向客户端提供应用程序数据，或者应用程序可以使用它自己的本地数据。如果数据是由应用程序提供的，则智能客户端应用程序可以缓存数据以改善性能或者支持脱机使用。在这种情况下，您需要决定客户端应用程序应该如何处理就该服务器而言已经过时的数据。</P>
<P>如果智能客户端应用程序提供在本地修改数据的能力，则必须在以后将客户端更改与服务器端应用程序进行同步。在这种情况下，您必须决定客户端应用程序如何处理数据冲突，以及如何跟踪需要发送到服务器的更改。</P>
<P>在设计您的智能客户端应用程序时，您需要认真考虑这些问题以及其他许多问题。本章分析了在客户端上处理数据时的各种注意事项，包括： </P>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>数据类型。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>缓存数据。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>数据并发。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>使用 ADO.NET 数据集来管理数据。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>Windows 窗体数据绑定。 </P></TD></TR></TBODY></TABLE>
<P>本章未讨论其他许多与处理数据有关的问题。具体说来，在<A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter5SecuCons.mspx"><FONT color=#002c99>第 5 章：安全性注意事项</FONT></A>中讨论了数据处理安全性问题，在<A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter4OccConnSC.mspx"><FONT color=#002c99>第 4 章：偶尔连接的智能客户端</FONT></A>中讨论了脱机注意事项。</P><A name=EHAA></A>
<H2>数据类型</H2>
<P>智能客户端通常必须处理两种类别的数据： </P>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>只读引用数据 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>瞬态数据 </P></TD></TR></TBODY></TABLE>
<P>通常情况下，需要以不同的方式处理这些类型的数据，因此更详细地分析一下每种类型将是很有用的。</P>
<H3>只读引用数据</H3>
<P>只读引用数据是不会由客户端更改并且被客户端用于引用目的的数据。因此，从客户端的观点看来，该数据为只读数据，并且客户端不会对其执行更新、插入或删除操作。只读引用数据很容易在客户端上进行缓存。引用数据在智能客户端应用程序中具有许多种用途，包括： </P>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>提供静态引用或查找数据</B>。这方面的示例包括产品信息、价格表、发货选项和价格。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>支持数据验证，允许检查用户输入数据的正确性</B>。示例有针对交货时间表检查输入的日期。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>帮助与远程服务进行通讯</B>。示例在本地将用户选择转化为产品 ID，然后将该信息发送到 Web 服务。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>呈现数据</B>。示例包括呈现帮助文本或用户界面标签。 </P></TD></TR></TBODY></TABLE>
<P>通过在客户端上存储和使用引用数据，您可以减少需要从客户端传输到服务器的数据量，改善应用程序的性能，帮助启用脱机功能，并提供早期数据验证以提高应用程序的可用性。</P>
<P>尽管客户端无法更改只读引用数据，但可以在服务器上进行更改（例如，由管理员或超级用户更改）。您需要确定在发生数据更改时用于更新客户端的策略。此类策略可能涉及到在发生更改时将更改推到客户端上，或者按照特定的时间间隔或在客户端上执行某些操作之前从服务器拉入更改。但是，因为数据在客户端上是只读的，所以您无须跟踪客户端更改。这就简化了需要对只读引用数据进行处理的方式。</P>
<H3>瞬态数据</H3>
<P>瞬态数据既可以在服务器上更改，也可以在客户端上更改。通常情况下，瞬态数据作为用户输入和操作的直接或间接结果而发生更改。在此情况下，在客户端或服务器进行的更改都需要在某个时刻进行同步。这种类型的数据在智能客户端中具有许多种用途，包括： </P>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>添加新信息</B>。示例包括添加银行业务交易或客户详细信息。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>修改现有信息</B>。示例更新客户详细信息。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>删除现有信息</B>。示例从数据库中删除客户。 </P></TD></TR></TBODY></TABLE>
<P>在智能客户端处理瞬态数据的最困难的方面之一在于，这些数据通常可能在多个客户端上同时进行修改。当数据非常不稳定时，该问题将恶化，因为所做更改更有可能互相冲突。</P>
<P>您需要跟踪您对瞬态数据进行的任何客户端更改。在与服务器同步数据并且已经解决任何冲突之前，您不应该认为瞬态数据已被确认。您应该非常小心以避免依赖未确认的数据进行重要决策，或者在未认真考虑如何保证数据一致性（甚至在同步失败时）的情况下使用该数据作为其他本地更改的基础。</P>
<P>有关围绕脱机时处理数据的问题以及如何处理数据同步的详细信息，请参阅<A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter4OccConnSC.mspx"><FONT color=#002c99>第 4 章：偶尔连接的智能客户端</FONT></A>。</P>
<DIV style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 10px"><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter2HandlingData.mspx#top"><IMG height=9 alt=返回页首 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_up.gif" width=7 border=0></A><A class=topOfPage href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter2HandlingData.mspx#top"><FONT color=#002c99>返回页首</FONT></A></DIV><A name=EGAA></A>
<H2>缓存数据</H2>
<P>智能客户端通常需要在本地缓存数据（无论是只读引用数据还是瞬态数据）。通过缓存数据，有可能改善应用程序的性能并提供脱机工作所需的数据。但是，您需要认真考虑在客户端缓存哪些数据、如何管理这些数据以及可以在哪个上下文中使用这些数据。</P>
<P>要启用数据缓存，您的智能客户端应用程序应该实现某种形式的缓存基础结构，以便透明地处理数据缓存细节。您的缓存基础结构应该包括下列缓存机制中的一种或两种： </P>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>短期数据缓存</B>。在内存中缓存数据对性能有益，但不能持久，因此您可能需要在重新运行应用程序时从源拉入数据。这样做可能会妨碍您的应用程序脱机操作。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>长期数据缓存</B>。通过在持久性媒体（如独立存储或本地文件系统）中缓存数据，可以在没有连接到服务器时使用应用程序。您可以选择将长期存储与短期存储结合起来以改善性能。 </P></TD></TR></TBODY></TABLE>
<P>无论您采用哪种缓存机制，都应该确保仅将用户有权访问的数据提供给客户端。而且，在客户端缓存的敏感数据要求进行认真处理以确保它的安全。因此，您可能需要在将数据传输到客户端以及在客户端存储数据时，对数据进行加密。有关详细信息，请参阅<A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter5SecuCons.mspx"><FONT color=#002c99>第 5 章：安全性注意事项</FONT></A>中的“处理敏感数据”。</P>
<P>当您设计智能客户端以支持数据缓存时，您应该考虑为客户端提供一种请求新数据的机制，而无论缓存的状态如何。这意味着您可以确保应用程序随时能够执行新的事务，并且不会使用过时的数据。您还可以将客户端配置为预先获取数据，以便减少在缓存数据到期时处于脱机状态的风险。</P>
<P>只要有可能，您都应该将某种形式的元数据与该数据关联起来，以便使客户端能够以聪明的方式管理这些数据。此类元数据可用于指定数据的标识和任何约束，或者指定所需的与该数据关联的行为。您的客户端缓存基础结构应该消耗该元数据，并且使用它来适当处理缓存的数据。</P>
<P>客户端缓存的所有数据都应该是可以唯一标识的（例如，通过版本号或日期戳），以便在确定是否需要更新数据时，可以正确地识别相应的数据。这样，您的缓存基础结构就能够询问服务器它所具有的数据当前是否有效，并且确定是否需要进行更新。</P>
<P>元数据还可以用来指定与缓存数据的使用和处理相关的约束或行为。示例包括： </P>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>时间约束</B>。这些约束指定可以使用缓存数据的时间或日期范围。当该数据过时或到期时，可以将其从缓存中丢弃，或者通过从服务器获取最新数据来自动刷新数据。在某些情况下，合适的做法可能是让客户端使用过时的引用数据，并且在与服务器进行同步时将过时数据映射到最新数据。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>地理约束</B>。某些数据可能仅适用于特定地区。例如，您可能对于不同的地点有不同的价格表。可以使用您的缓存基础结构分别针对不同的地点来访问和存储数据。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>安全性要求</B>。可以将专门提供给特定用户的数据加密，以确保只有相应的用户可以访问这些数据。在此情况下，所提供的数据已经进行了加密，并且用户必须向缓存基础结构提供凭据以便对数据进行解密。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>业务规则</B>。您可能拥有与缓存数据关联的业务规则，用来规定如何使用这些数据。例如，您的缓存基础结构可能考虑用户的角色，以便确定向该用户提供哪些数据以及如何处理这些数据。 </P></TD></TR></TBODY></TABLE>
<P>您的缓存基础结构可以通过与数据关联的元数据来适当地处理这些数据，从而使您的应用程序无须关心数据缓存问题或实现细节。您可以在引用数据本身内部传递与这些数据关联的元数据，或者您可以使用带外机制。用于将元数据传输到客户端的确切机制取决于您的应用程序与网络服务的通讯方式。当使用 Web 服务时，利用 SOAP 头将元数据传输到客户端是一种很好的解决方案。</P>
<P>只读引用数据与瞬态数据之间存在的区别有时意味着您需要使用两个缓存，一个用于引用数据，一个用于瞬态数据。引用数据在客户端是只读的，并且不需要回过头来与服务器进行同步，但它的确需要偶尔进行刷新以反映在服务器上进行的任何更改和更新。</P>
<P>瞬态数据既可以在服务器上更改，也可以在客户端上更改。既然有时在客户端更新缓存中的数据，有时在服务器更新，有时在这两个位置更新，那么对客户端数据进行的更改需要在某个时刻与服务器进行同步。如果数据同时在服务器上发生了更改，则会发生数据冲突，需要对其进行适当的处理。</P>
<P>要帮助确保维持数据一致性，并且避免不适当地使用数据，您应该小心地跟踪您在客户端对瞬态数据进行的任何更改。在成功地与服务器进行同步或确认之前，此类更改是未提交的 或暂定的。</P>
<P>您应该对您的智能客户端应用程序进行适当的设计，以使其能够区分已经成功地与服务器进行同步的数据和仍然暂定的数据。这一区分过程可以帮助应用程序更加容易地检测和处理数据冲突。而且，您可能需要禁止应用程序或用户基于暂定数据进行重要决策或者启动重要操作。在将此类数据与服务器进行同步之前，不应该依赖它们。通过使用适当的缓存基础结构，可以跟踪暂定数据和已经确认的数据。</P>
<H3>缓存应用程序块（Caching Application Block）</H3>
<P>缓存应用程序块是一个 Microsoft? .NET 框架扩展，它使开发人员可以容易地缓存来自服务提供程序的数据。生成和设计它的目的是将 Microsoft 建议的缓存准则封装在 .NET 框架应用程序中，如位于 http://msdn.microsoft.com/library/en-us/dnbda/html/CachingArch.asp 的 Caching Architecture Guide for .NET Framework Applicationss 所述。</P>
<P>缓存块的总体体系结构如图 2.1 所示。</P>
<DIV style="WIDTH: 450px"><IMG height=193 alt="" src="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/art/cabwof01.gif" width=450 border=0><BR>
<P class=figureCaption><B>图</B><B> 2.1 </B>缓存块工作流</P>
<DIV class=figureRule></DIV></DIV>
<P>缓存工作流包含下列步骤： </P>
<TABLE class=numberedList cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR vAlign=top>
<TD class=listNumber noWrap align=right>
<P>1.</P></TD>
<TD>
<P>客户端或服务代理向 <B>CacheManager</B> 发出对缓存数据项的请求。 </P></TD></TR>
<TR vAlign=top>
<TD class=listNumber noWrap align=right>
<P>2.</P></TD>
<TD>
<P>如果该数据项已被缓存，则 <B>CacheManager</B> 会从存储中检索该项，并将其作为 <B>CacheItem</B> 对象返回。如果该项尚未缓存，则会通知客户端。 </P></TD></TR>
<TR vAlign=top>
<TD class=listNumber noWrap align=right>
<P>3.</P></TD>
<TD>
<P>在从服务提供程序检索未缓存的数据之后，客户端将该数据发送给 <B>CacheManager</B>。<B>CacheManager</B> 会将一个签名（即，元数据）如密钥、到期时间或优先级等添加到该数据项中，并将其加载到缓存中。 </P></TD></TR>
<TR vAlign=top>
<TD class=listNumber noWrap align=right>
<P>4.</P></TD>
<TD>
<P><B>CacheService</B> 监控 <B>CacheItems</B> 的生存期。当 <B>CacheItem</B> 到期时，<B>CacheService</B> 会删除它并根据情况调用回调委托。 </P></TD></TR>
<TR vAlign=top>
<TD class=listNumber noWrap align=right>
<P>5.</P></TD>
<TD>
<P><B>CacheService</B> 还可以将所有数据项从缓存中清除出去。 </P></TD></TR></TBODY></TABLE>
<P>缓存块提供了多种缓存到期选项，如表 2.1 所述。</P>
<P><B>表</B><B> 2.1 </B><B>缓存块到期选项</B></P>
<TABLE class=dataTable id=ECAGAA cellSpacing=0 cellPadding=0>
<THEAD>
<TR class=stdHeader vAlign=top>
<TD id=colEBBCAGAA>类</TD>
<TD id=colEABCAGAA style="BORDER-RIGHT: #cccccc 1px solid">说明</TD></TR></THEAD>
<TBODY>
<TR class=record vAlign=top>
<TD>
<P class=lastInCell>AbsoluteTime</P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>用于设置到期时间的绝对时间。</P></TD></TR>
<TR class=evenRecord vAlign=top>
<TD>
<P class=lastInCell>ExtendedFormatTime</P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>用于基于表达式（如 every minute 或 every Monday）设置到期时间。</P></TD></TR>
<TR class=record vAlign=top>
<TD>
<P class=lastInCell>FileDependency</P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>用于基于文件是否更改来设置到期时间。</P></TD></TR>
<TR class=evenRecord vAlign=top>
<TD>
<P class=lastInCell>SlidingTime</P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>用于设置项的生存期，方法是基于项的上次访问时间来指定到期时间。</P></TD></TR></TBODY></TABLE>
<DIV class=dataTableBottomMargin></DIV>
<P>下列存储机制可供缓存块使用： </P>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>内存映射文件</B><B> (MMF)</B>。MMF 最适合于基于客户端的高性能缓存方案。您可以使用 MMF 来开发可在同一台计算机中的多个应用程序域和进程之间共享的缓存。.NET 框架不支持 MMF，因此 MMF 缓存的任何实现都以非托管代码的形式运行，并且不会从任何 .NET 框架功能中受益，包括内存管理功能（如垃圾回收）和安全性功能（如代码访问安全性）。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>Singleton </B><B>对象</B>。可以使用 .NET 远程处理 singleton 对象来缓存可在一台或多台计算机中的进程之间共享的数据。方法是使用通过 .NET 远程处理为多个客户端提供服务的 singleton 对象来实现缓存服务。单例缓存的实现很简单，但它缺乏基于 Microsoft SQL Server™ 的解决方案所提供的性能和可伸缩性。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>Microsoft SQL Server 2000 </B><B>数据库</B>。SQL Server 2000 存储最适合于应用程序要求具有高持续性或者您需要缓存大量数据的场合。因为缓存服务需要通过网络访问 SQL Server，并且使用数据库查询检索数据，所以数据访问的速度相对比较慢。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>Microsoft SQL Server </B><B>桌面引擎</B><B> (MSDE)</B>。MSDE 是 SQL Server 2000 的轻型数据库替代产品。它提供了可靠性和安全性功能，但具有比 SQL Server 更小的客户端足迹，因此它需要较少的设置和配置。因为 MSDE 支持 SQL，所以开发人员可以得到数据库的很多功能。如有必要，您可以将 MSDE 数据库迁移到 SQL Server 数据库。 </P></TD></TR></TBODY></TABLE>
<DIV style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 10px"><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter2HandlingData.mspx#top"><IMG height=9 alt=返回页首 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_up.gif" width=7 border=0></A><A class=topOfPage href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter2HandlingData.mspx#top"><FONT color=#002c99>返回页首</FONT></A></DIV><A name=EFAA></A>
<H2>数据并发</H2>
<P>正如前面所提到的，使用智能客户端的一个问题是：在将任何客户端更改与服务器进行同步之前，服务器上保存的数据可能发生更改。您需要采用某种机制来确保在对数据进行同步时，数据冲突能够得到适当的处理，并且最后得到的数据是一致和正确的。数据能够由多个客户端进行更新的能力称为“数据并发”。</P>
<P>您可以使用两种方法来处理数据并发： </P>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>保守式并发</B>。保守式并发允许一个客户端保持数据上的锁，以禁止任何其他客户端修改数据，直至客户端自己的更改完成为止。在这种情况下，如果另一个客户端尝试修改数据，则在锁的拥有者释放该锁之前，这些尝试将失败或者被阻止。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>保守式并发可能有问题，因为单个用户或客户端可能由于疏忽而长时间地保持锁定。所以，该锁可能会妨碍重要资源（如数据库行或文件）及时得到释放，从而严重影响应用程序的可伸缩性和可用性。但是，当您需要完全控制对重要资源所做的更改时，保守式并发可能是适当的。请注意，如果您的客户端要脱机工作，则不能使用这种并发，因为客户端无法对数据加锁。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>开放式并发</B>。开放式并发不会锁定数据。要判断是否实际需要更新，可以将原始数据随更新请求和已更改的数据一起发送。随后，将针对当前数据检查原始数据，以查看是否同时对原始数据进行了更新。如果原始数据和当前数据匹配，则执行更新；否则，拒绝请求，并产生开放式失败。要优化该过程，您可以在数据中使用时间戳或更新计数器，而不必发送原始数据，此时只需要检查时间戳或计数器。 </P>
<P>开放式并发提供了一种良好的机制，可用来更新不会非常频繁更改的主数据，如客户的电话号码或地址。开放式并发允许每个人读取数据，在发生更新的概率小于读取操作的情况下，开放式失败的风险或许是可以接受的。在数据频繁更改以及开放式更新可能经常失败的情况下，开放式并发可能并不适合。 </P></TD></TR></TBODY></TABLE>
<P>在大多数智能客户端方案（包括客户端将要脱机工作的方案）中，开放式并发是正确的方法，因为它允许多个客户端同时使用数据，而不会不必要地锁定数据和影响所有其他客户端。 </P>
<P>有关开放式和保守式并发的详细信息，请参阅 .NET Framework Developer's Guide 中的“Optimistic Concurrency”，网址为：<A href="http://msdn.microsoft.com/library/en-us/cpguide/html/cpconoptimisticconcurrency.asp" target=_blank><FONT color=#002c99>http://msdn.microsoft.com/library/en-us/cpguide/html/cpconoptimisticconcurrency.asp</FONT></A>。</P>
<DIV style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 10px"><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter2HandlingData.mspx#top"><IMG height=9 alt=返回页首 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_up.gif" width=7 border=0></A><A class=topOfPage href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter2HandlingData.mspx#top"><FONT color=#002c99>返回页首</FONT></A></DIV><A name=EEAA></A>
<H2>使用 ADO.NET 数据集来管理数据</H2>
<P><B>DataSet</B> 是一个表示一个或多个关系数据库表的对象。数据集在断开连接的缓存中存储数据。<B>DataSets</B>的结构与关系数据库类似：它公开了一个由表、行和列组成的层次结构对象模型。另外，它还包含为<B>DataSets</B>定义的约束和关系。</P>
<P>ADO.NET <B>DataSet</B> 包含零个或更多个由 <B>DataTable</B> 对象表示的表组成的集合。<B>DataTable</B> 在 <B>System.Data </B>命名空间中定义，并且表示单个由内存驻留数据组成的表。它包含由 <B>DataColumnCollection</B> 表示的列和由 <B>ConstraintCollection</B> 表示的约束组成的集合，它们共同定义了该表的架构。<B>DataTable</B> 还包含由 <B>DataRowCollection</B>（它包含该表中的数据）表示的行组成的集合。与其当前状态一起，<B>DataRow</B> 保留其当前版本和原始版本，以便标识对该行中存储的值所做的更改。</P>
<P><B>DataSets</B>可以强类型化或非类型化。类型化的 <B>DataSet</B> 从 <B>DataSet</B> 基类继承，但是向 <B>DataSet</B> 中添加了强类型化的语言功能，从而使用户可以用更加强类型化的编程方式访问内容。在生成应用程序时，可以使用任一种类型。但是，Microsoft Visual Studio ® 开发系统对类型化<B>DataSets</B>具有更多支持，它们使得用<B>DataSets</B>编程变得更加容易，而且更不容易出错。</P>
<P>DataSets在智能客户端环境中尤其有用，因为它们提供了能够帮助客户端在脱机状态下使用数据的功能。它们可以跟踪对数据进行的本地更改，这有助于与服务器同步数据以及协调数据冲突，并且它们还可用于合并来自不同源的数据。</P>
<P>有关如何使用DataSets的详细信息，请参阅 Visual Basic and Visual C# Concepts 中的“Introduction to DataSets”，网址为：<A href="http://msdn.microsoft.com/library/en-us/vbcon/html/vbconDataSets.asp" target=_blank><FONT color=#002c99>http://msdn.microsoft.com/library/en-us/vbcon/html/vbconDataSets.asp</FONT></A>。</P>
<H3>用DataSets合并数据</H3>
<P><B>DataSets</B>具有将 <B>DataSet</B>、<B>DataTable</B> 或 <B>DataRow</B> 对象的内容合并到现有<B>DataSets</B>的能力。对于跟踪在客户端上进行的更改以及与服务器的已更新内容进行合并而言，该功能尤其有用。图 2.2 显示了一个从 Web 服务请求更新的智能客户端，新数据作为数据传输对象 (DTO) 返回。DTO 是一种企业模式，它使您可以将所有需要与 Web 服务进行通讯的数据打包到一个对象中。使用 DTO 通常意味着您可以对 Web 服务进行单个调用而不是多个调用。</P>
<DIV style="WIDTH: 393px"><IMG height=99 alt="" src="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/art/dataf02.gif" width=393 border=0><BR>
<P class=figureCaption><B>图</B><B> 2.2 </B>通过使用DataSets合并客户端上的数据</P>
<DIV class=figureRule></DIV></DIV>
<P>在该示例中，当 DTO 被返回到客户端时，该 DTO 将被用于在客户端上以本地方式创建一个新的DataSets。</P>
<P><B>注</B> 在合并操作之后，ADO.NET 不会自动将行状态从 modified 更改为 unchanged。因此，在将新的<B>DataSets</B>与本地客户端<B>DataSets</B>合并之后，您需要调用<B>DataSets</B>上的 AccceptChanges 方法，将 RowState 属性重置为 unchanged。 </P>
<P>有关如何使用<B>DataSets</B>的详细信息，请参阅 <I>.NET Framework Developer's Guide</I> 中的“Merging DataSet Contents”，网址为：<A href="http://msdn.microsoft.com/library/en-us/cpguide/html/cpconmergingDataSetcontents.asp" target=_blank><FONT color=#002c99>http://msdn.microsoft.com/library/en-us/cpguide/html/cpconmergingDataSetcontents.asp。</FONT></A></P>
<H3>提高DataSets的性能</H3>
<P><B>DataSets</B>通常可以包含大量数据，如果通过网络传递这些数据，则可能导致性能问题。幸而，通过 ADO.NET <B>DataSets</B>，您可以使用<B>DataSets</B>上的 <B>GetChanges</B> 方法来确保只在客户端和服务器之间传送在<B>DataSets</B>中更改过的数据，并且将该数据打包到 DTO 中。该数据随后将被合并到其目的地的<B>DataSets</B>中。</P>
<P>图 2.3 显示了一个智能客户端，它对本地数据进行更改，并且使用DataSets上的 GetChanges 方法仅将已更改的数据提交给服务器。出于性能原因，该数据被传输给 DTO。</P>
<DIV style="WIDTH: 404px"><IMG height=127 alt="" src="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/art/dataf01.gif" width=404 border=0><BR>
<P class=figureCaption><B>图</B><B> 2.3 </B>使用 DTO 改善性能</P>
<DIV class=figureRule></DIV></DIV>
<P>可以将 <B>GetChanges</B> 方法用于需要脱机工作的智能客户端应用程序。当应用程序重新联机时，您可以使用 <B>GetChanges</B> 方法确定哪些信息已经更改，并且随后生成一个与 Web 服务通讯的 DTO，以便确保将更改提交给数据库。</P>
<DIV style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 10px"><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter2HandlingData.mspx#top"><IMG height=9 alt=返回页首 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_up.gif" width=7 border=0></A><A class=topOfPage href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter2HandlingData.mspx#top"><FONT color=#002c99>返回页首</FONT></A></DIV><A name=EDAA></A>
<H2>Windows 窗体数据绑定</H2>
<P>通过 Windows 窗体数据绑定，您可以将应用程序的用户界面连接到该应用程序的基础数据。Windows 窗体数据绑定支持双向绑定，因此您可以将数据结构绑定到用户界面，向用户显示当前数据值，使用户可以编辑数据，然后使用用户输入的值自动更新基础数据。</P>
<P>您可以使用 Windows 窗体数据绑定将几乎任何数据结构或对象绑定到用户界面控件的任何属性。您可以将单个数据项绑定到控件的单个属性，还可以将更为复杂的数据（例如，数据项集合或数据库表）绑定到该控件，以便它可以在数据网格或列表框中显示所有数据。</P>
<P><B>注</B> 您可以绑定任何支持一个或多个公共属性的对象。您只能绑定到类的公共属性而不是公共成员。</P>
<P>通过 Windows 窗体数据绑定，您可以随您的应用程序一起提供灵活的、数据驱动的用户界面。您可以使用数据绑定提供对用户界面外观的自定义控制（例如，通过绑定到某些控件属性，如背景或前景颜色、大小、图像或图标）。</P>
<P>数据绑定具有许多种用途。例如，可以使用它完成下列任务： </P>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>向用户显示只读数据。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>使用户可以从用户界面更新数据。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>提供数据上的主从视图。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>使用户可以浏览复杂的相关数据项。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>提供查找表功能，使用户界面可以连接用户友好的显示名称。 </P></TD></TR></TBODY></TABLE>
<P>本节分析数据绑定的一些功能，并讨论一些您经常需要在智能客户端应用程序中实现的数据绑定功能。</P>
<P>有关数据绑定的详细信息，请参阅“Windows Forms Data Binding and Objects”，网址为：<A href="http://msdn.microsoft.com/library/en-us/dnadvnet/html/vbnet02252003.asp" target=_blank><FONT color=#002c99>http://msdn.microsoft.com/library/en-us/dnadvnet/html/vbnet02252003.asp</FONT></A>。</P>
<H3>Windows 窗体数据绑定体系结构</H3>
<P>Windows 窗体数据绑定提供了一种用于将数据双向连接到用户界面的灵活的基础结构。图 2.4 显示了 Windows 窗体数据绑定的总体体系结构的示意图。</P>
<DIV style="WIDTH: 450px"><IMG height=297 alt="" src="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/art/bindf01.gif" width=450 border=0><BR>
<P class=figureCaption><B>图</B><B> 2.4</B> Windows 窗体数据绑定的体系结构</P>
<DIV class=figureRule></DIV></DIV>
<P>Windows 窗体数据绑定使用下列对象： </P>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>数据源</B>。数据源是包含要绑定到用户界面的数据的对象。数据提供程序可以是任何具有公共属性的对象，可以是支持 IList 接口的数组或集合，还可以是复杂数据类（例如，DataSet 或 DataTable）的实例。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>CurrencyManager</B>。<B>CurrencyManager</B> 对象用于跟踪绑定到用户界面的数组、集合或表内的数据的当前位置。通过 <B>CurrencyManager</B> 可以将数据集合绑定到用户界面以及在相应的数据中导航，同时更新用户界面以反映集合内当前选择的项。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>PropertyManager</B>。<B>PropertyManager</B> 对象负责维护绑定到控件的对象的当前属性。<B>PropertyManager</B> 类和 <B>CurrencyManager</B> 类都从公用基类 <B>BindingManagerBase</B> 中继承。所有绑定到控件的数据提供程序都具有一个关联的 <B>CurrencyManager</B> 或 <B>PropertyManager</B> 对象。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>BindingContext</B>。每个 Windows 窗体都具有一个默认的 <B>BindingContext</B> 对象，该对象跟踪相应窗体上的所有 <B>CurrencyManager</B> 和 <B>PropertyManager</B> 对象。通过 <B>BindingContext</B> 对象可以容易地检索特定数据源的 <B>CurrencyManager</B> 或 <B>PropertyManager</B> 对象。您可以将特定的 <B>BindingContext</B> 对象分配给包含数据绑定控件的容器控件（如 <B>GroupBox</B>、<B>Panel</B> 或 <B>TabControl</B>）。这样做可以使窗体的每个部分都由它自己的 <B>CurrencyManager</B> 或 <B>PropertyManager</B> 对象管理。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>Binding</B>。<B>Binding</B> 对象用于在控件的单个属性与另一个对象的属性或某个对象列表中当前对象的属性之间创建和维护简单绑定。 </P></TD></TR></TBODY></TABLE>
<H3>将数据绑定到 Windows 窗体控件</H3>
<P>有许多可用于绑定到特定 Windows 窗体控件的属性和方法。表 2.2 显示了其中一些比较重要的属性和方法。</P>
<P><B>表</B><B> 2.2 </B><B>用于绑定到</B><B> Windows </B><B>窗体控件的属性和方法</B></P>
<TABLE class=dataTable id=EFFDAA cellSpacing=0 cellPadding=0>
<THEAD>
<TR class=stdHeader vAlign=top>
<TD id=colECBFFDAA>属性或方法</TD>
<TD id=colEBBFFDAA>Windows 窗体控件</TD>
<TD id=colEABFFDAA style="BORDER-RIGHT: #cccccc 1px solid">说明</TD></TR></THEAD>
<TBODY>
<TR class=record vAlign=top>
<TD>
<P class=lastInCell>DataSource 属性</P></TD>
<TD>
<P>ListControls（例如，ListBox 或 Combo Box）、</P>
<P>DataGrid 控件</P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>使您可以指定要绑定到用户界面控件的数据提供程序对象。</P></TD></TR>
<TR class=evenRecord vAlign=top>
<TD>
<P class=lastInCell>DisplayMember 属性</P></TD>
<TD>
<P class=lastInCell>ListControls</P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>使您可以指定要显示给用户的数据提供程序的成员。</P></TD></TR>
<TR class=record vAlign=top>
<TD>
<P class=lastInCell>ValueMember 属性</P></TD>
<TD>
<P class=lastInCell>ListControls</P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>使您可以指定与显示值相关联的、供您的应用程序内部使用的值。</P></TD></TR>
<TR class=evenRecord vAlign=top>
<TD>
<P class=lastInCell>DataMember 属性</P></TD>
<TD>
<P class=lastInCell>DataGrid 控件</P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>如果数据源包含多个数据源（例如，如果您指定了包含多个表的DataSets），请使用 DataMember 属性来指定要绑定到网格的数据源。（参阅表后面的备注。）</P></TD></TR>
<TR class=record vAlign=top>
<TD>
<P class=lastInCell>SetDataBinding 方法</P></TD>
<TD>
<P class=lastInCell>DataGrid 控件</P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>使您可以在运行时重置 DataSource 方法。</P></TD></TR></TBODY></TABLE>
<DIV class=dataTableBottomMargin></DIV>
<P><B>注</B> 如果 <B>DataSource</B> 是 <B>DataTable</B>、<B>DataView</B>、集合或数组，则无须设置 <B>DataMember</B> 属性。</P>
<P>您还可以使用所有 Windows 窗体控件对象上提供的 <B>DataBindings</B> 集合属性将 <B>Binding</B> 对象显式添加到任何控件对象。<B>Binding</B> 对象用于将控件上的单个属性绑定到数据提供程序的单个数据成员。下面的代码示例在一个文本框控件的 <B>Text</B> 属性和一个数据集的 customers 表中的客户名称之间添加了绑定。</P><PRE class=codeSample>textBox1.DataBindings.Add( 
        new Binding( "Text", DataSet, "customers.customerName" ) ); 
</PRE>
<P>当您用 <B>Binding</B> 构造函数构建 <B>Binding</B> 示例时，您必须指定要绑定到的控件属性的名称、数据源以及可解析为该数据源中的列表或属性的导航路径。该导航路径可以是空字符串、单个属性名或句点分隔的名称层次结构。您可以使用分层的导航路径在 <B>DataSet</B> 对象中的数据表和关系中导航，或者在对象的属性向其他对象返回实例的对象模型中导航。如果您将导航路径设置为空字符串，则会在基础数据源对象上调用 <B>ToString</B> 方法。</P>
<P><B>注</B> 如果属性是只读的（即，对象不支持对该属性进行的设置操作），则数据绑定默认情况下不会使绑定的 Windows 窗体控件成为只读的。这可能给用户带来混乱，因为用户可以编辑用户界面中的值，但绑定对象中的值将不会得到更新。所以，请确保将所有被绑定到只读属性的 Windows 窗体控件的只读标志设置为 <B>true</B>。</P>
<H3>将控件绑定到DataSets</H3>
<P>将控件绑定到数据集通常是有用的。这样做使您可以在数据网格中显示数据集数据，并且使用户可以容易地更新数据。您可以使用以下代码将数据网格控件绑定到 <B>DataSet</B>。</P><PRE class=codeSample>DataSet newDataSet = webServiceProxy.GetDataSet(); 
this.DataGrid.SetDataBinding( newDataSet, "tableName" ); 
</PRE>
<P>有时，在已经建立与您的控件的所有绑定之后，您需要替换数据集的内容。但是，在用新的集合替换现有集合时，所有绑定仍将指向旧的数据集。</P>
<P>比用新的数据源手动重新创建数据绑定更好的办法是，您可以使用 <B>DataSet</B> 类的 <B>Merge</B> 方法将新数据集中的数据导入现有数据集，如下面的代码示例所示。</P><PRE class=codeSample>DataSet newDataSet = myService.GetDataSet(); 
this.DataSet1.Clear();    
this.DataSet1.Merge( newDataSet ); 
</PRE>
<P><B>注</B> 要避免线程化问题，您应该只在 UI 线程上更新绑定的数据对象。有关详细信息，请参阅<A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter6UsingMultThr.mspx"><FONT color=#002c99>第 6 章：使用多个线程</FONT></A>。</P>
<H3>在数据集合中导航</H3>
<P>如果您的数据源包含项集合，则可以将该数据集合绑定到 Windows 窗体控件，并且在该数据集合中逐项导航。用户界面将自动更新以反映集合中的当前项。</P>
<P>您可以绑定到任何支持 <B>IList</B> 接口的集合对象。当您绑定到对象集合时，您可以让用户导航该集合中的每个项，并自动更新每个项的用户界面。.NET Framework 提供的许多集合和复杂数据类已经支持 <B>IList</B> 接口，因此您可以容易地绑定到数组或复杂数据，如数据行或数据视图。例如，任何作为 <B>System.Array</B> 类的实例的数组对象默认情况下都实现了 <B>IList</B> 接口，因而可以绑定到用户界面。许多 ADO.NET 对象还支持 <B>IList</B> 接口或它的派生接口，从而使这些对象也可以容易地绑定。例如，<B>DataViewManager</B>、<B>DataSet</B>、<B>DataTable</B>、<B>DataView</B> 和 <B>DataColumn</B> 类都以这种方式支持数据绑定。</P>
<P>实现了 <B>IList</B> 接口的数据源由 <B>CurrencyManager</B> 对象管理。该对象通过它的 <B>Position</B> 属性维护数据集合的索引。该索引用于确保绑定到该数据源的所有控件都读/写数据集合中的相同项。</P>
<P>如果您的窗体包含绑定到多个数据源的控件，则它将具有多个 <B>CurrencyManager</B> 对象，分别对应于各个独立的数据源。<B>BindingContext</B> 对象提供对该窗体上的所有 <B>CurrencyManager</B> 对象的方便访问。下面的代码示例显示了如何在 customers 集合内部递增当前位置。</P><PRE class=codeSample>this.BindingContext[ DataSet, "customers" ].Position += 1; 
</PRE>
<P>您应该像以下代码示例中所示的那样，使用 <B>CurrencyManager</B> 对象上的 <B>Count</B> 属性来确保不会设置无效位置。</P><PRE class=codeSample>if ( this.BindingContext[ DataSet, "customer" ].Position &lt; 
     ( this.BindingContext[ DataSet, "customer" ].Count – 1 ) ) 
{ 
    this.BindingContext[ DataSet, "customers" ].Position += 1; 
} 
</PRE>
<P><B>CurrencyManager</B> 对象还支持 <B>PositionChanged</B> 事件。您可以创建该事件的处理程序，以便更新您的用户界面以反映当前绑定位置。下面的代码示例显示了一个标签，以说明当前位置和记录总数。</P><PRE class=codeSample>this.BindingContext[ DataSet, "customers" ].PositionChanged += 
        new EventHandler( this.BindingPositionChanged ); 
</PRE>
<P>方法 <B>BindingPositionChanged</B> 的实现方式如下所示。</P><PRE class=codeSample>private void BindingPositionChanged( object sender, System.EventArgs e ) 
{    
    positionLabel.Text = string.Format( "Record {0} of {1}", 
        this.BindingContext[dsPubs1, "authors"].Position + 1,  
        this.BindingContext[dsPubs1, "authors"].Count ); 
} 
</PRE>
<H3>自定义格式和数据类型转换</H3>
<P>您可以使用 <B>Binding</B> 类的<B>Format</B> 和 <B>Parse</B> 事件为绑定到控件的数据提供自定义格式。通过这些事件，您可以控制在用户界面中显示数据的方式以及从用户界面中获取数据和分析数据的方式，以便更新基础数据。还可以使用这些事件来转换数据类型，以便源数据类型和目标数据类型兼容。</P>
<P><B>注</B> 如果控件上绑定属性的数据类型与数据源中数据的数据类型不匹配，则会引发异常。如果您需要绑定不兼容的类型，则应该使用 <B>Binding</B> 对象上的 <B>Format</B> 和 <B>Parse</B> 事件。</P>
<P>当从数据源中读取数据并将其显示在控件中时，以及当从控件中读取数据并使用它来更新数据源时，将发生 <B>Format</B> 事件。当从数据源中读取数据时，<B>Binding</B> 对象将使用 <B>Format</B> 事件在控件中显示格式化数据。当从控件中读取数据并使用它来更新数据源时，<B>Binding</B> 对象将使用 <B>Parse</B> 事件来分析数据。</P>
<P><B>Format</B> 和 <B>Parse</B> 事件使您可以创建用于显示数据的自定义格式。例如，如果表中的数据的类型是 <B>Decimal</B>，则您可以通过将 <B>ConvertEventArgs</B> 对象的 <B>Value</B> 属性设置为 <B>Format</B> 事件中的格式化值，以本地货币格式显示数据。因此，您必须在 <B>Parse</B> 事件中格式化显示的值。</P>
<P>下面的代码示例将订单金额绑定到文本框。<B>Format</B> 和 <B>Parse</B> 事件用于在文本框期望的 string 类型和数据源期望的 decimal 类型之间进行转换。</P><PRE class=codeSample>private void BindControl() 
{ 
    Binding binding = new Binding( "Text", DataSet, 
"customers.custToOrders.OrderAmount" ); 
    // Add the delegates to the event. 
    binding.Format += new ConvertEventHandler( DecimalToCurrencyString ); 
    binding.Parse  += new ConvertEventHandler( CurrencyStringToDecimal ); 
    text1.DataBindings.Add( binding ); 
} 
private void DecimalToCurrencyString( object sender, ConvertEventArgs cevent ) 
{ 
    // The method converts only to string type. Test this using the  
DesiredType. 
    if( cevent.DesiredType != typeof( string ) ) return; 
    // Use the ToString method to format the value as currency ("c"). 
    cevent.Value = ((decimal)cevent.Value).ToString( "c" ); 
} 
private void CurrencyStringToDecimal( object sender, ConvertEventArgs cevent ) 
{ 
    // The method converts back to decimal type only.  
    if( cevent.DesiredType != typeof( decimal ) ) return; 
    // Converts the string back to decimal using the static Parse method. 
    cevent.Value = Decimal.Parse( cevent.Value.ToString(), 
                NumberStyles.Currency, null ); 
} 
</PRE>
<H3>使用模型-视图-控制器模式来实现数据验证</H3>
<P>通过将数据结构绑定到用户界面元素，用户可以编辑数据并确保所做更改随后被写回到基础数据结构。通常，您需要检查用户对数据所做的更改，以确保输入的值有效。</P>
<P>上一节中介绍的 <B>Format</B> 和 <B>Parse</B> 事件提供了一种用于截获用户对数据所做更改的方法，以便可以检查数据的有效性。但是，该方法要求与自定义格式代码一起实现数据验证逻辑（通常是在窗体级别）。如果在事件处理程序中同时实现这两种职责，则会使您的代码难以理解和维护。</P>
<P>更为雅致的办法是对代码进行设计，以使其使用模型-视图-控制器 (MVC) 模式。该模式提供了在通过数据绑定编辑和更改数据时涉及到的各种职责的自然分隔。您应该在负责以特定格式呈现数据的窗体内实现自定义格式，然后将验证规则与数据本身相关联，以便在多个窗体中重新使用这些规则。</P>
<P>在 MVC 模式中，数据本身被封装在模型对象中。视图对象是数据所绑定到的 Windows 窗体控件。对该模型所做的所有更改都由一个中间控制器对象处理，该对象负责提供对数据的访问，并且负责控制通过视图对象对数据所做的任何更改。控制器对象提供了一个用于验证对数据所做更改的自然位置，所有用户界面验证逻辑都应该在这里实现。</P>
<P>图 2.5 描绘了 <I>MVC</I> 模式中的三个对象之间的结构关系。</P>
<DIV style="WIDTH: 317px"><IMG height=157 alt="" src="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/art/uipfig02.gif" width=317 border=0><BR>
<P class=figureCaption><B>图</B><B> 2.5</B> 模型-视图-控制器模式中的对象</P>
<DIV class=figureRule></DIV></DIV>
<P>以这种方式使用控制器对象具有许多优点。您可以配置一个普通的控制器以提供自定义验证规则，这些规则可以在运行时根据某些上下文信息（例如，用户的角色）进行配置。或者，您还可以提供许多个控制器对象，每个控制器对象都实现特定的验证规则，然后在运行时选择适当的对象。无论采用哪种方法，因为所有验证逻辑都被封装在控制器对象中，所以视图和模型对象都不需要更改。 </P>
<P>除了分隔数据、验证逻辑和用户界面控件以外，MVC 模型还为您提供了一种在基础数据更改时自动更新用户界面的简单方法。控制器对象负责在发生通过其他某些编程手段对数据进行更改时通知用户界面。Windows 窗体数据绑定侦听由绑定到控件的对象生成的事件，以便用户界面可以自动响应对基础数据所做的更改。</P>
<P>要实现用户界面的自动更新，您应该确保控制器为每个可能更改的属性实现一个更改通知事件。事件应该遵循命名约定<B><I>&lt;property&gt;</I></B>Changed，其中 <B><I>&lt;property&gt; </I></B>是属性的名称。例如，如果控制器支持 <B>Name</B> 属性，则它还应该支持 <B>NameChanged</B> 事件。如果名称属性的值更改，则应该激发该事件，以便 Windows 窗体数据绑定可以处理它并更新用户界面。</P>
<P>下面的代码示例定义了一个 <B>Customer</B> 对象，该对象实现了 <B>Name</B> 属性。<B>CustomerController</B> 对象处理 <B>Customer</B> 对象的验证逻辑并支持 <B>Name</B> 属性，而该属性又表示基础 <B>Customer</B> 对象上的 <B>Name</B> 属性。每当该名称更改时，此控制器都将激发一个事件。</P><PRE class=codeSample>public class Customer 
{ 
    private string _name; 
    public Customer( string name ) { _name = name; } 
    public string Name 
    { 
        get { return _name; } 
        set { _name = value; } 
    } 
} 
public class CustomerController 
{ 
    private Customer _customer = null; 
    public event EventHandler NameChanged; 
    public Customer( Customer customer ) 
    { 
        this._customer = customer; 
    } 
    public string Name 
    { 
        get { return _customer.Name; } 
        set 
        { 
             // TODO: Validate new name to make sure it is valid. 
            _customer.Name = value;  
            // Notify bound control of change. 
            if ( NameChanged != null ) 
                NameChanged( this, EventArgs.Empty ); 
        } 
    } 
} 
</PRE>
<P><B>注</B> Customer 数据源成员在声明时需要进行初始化。在前面的示例中，需要将 <B>customer.Name</B> 成员初始化为空字符串。这是因为在数据绑定发生之前，.NET 框架没有机会与该对象进行交互并设置默认的空字符串设置。如果未初始化 customer 数据源成员，则在尝试从未初始化的变量中检索值时，将导致运行时异常。</P>
<P>在下面的代码示例中，窗体具有一个 <B>TextBox</B> 对象 <B>textbox1</B>，它需要绑定到客户的名称。代码将 <B>TextBox</B> 对象的 <B>Text</B> 属性绑定到控制器的 <B>Name</B> 属性。</P><PRE class=codeSample>_customer = new Customer( "Kelly Blue" ); 
_controller = new CustomerController( _customer ); 
Binding binding = new Binding( "Text", _controller, "Name" ); 
textBox1.DataBindings.Add( binding ); 
</PRE>
<P>如果更改了客户的名称（使用控制器上的 <B>Name</B> 属性），则会激发 <B>NameChanged</B> 事件，并且自动更新文本框以反映新的名称值。</P>
<H3>在基础数据更改时更新用户界面</H3>
<P>您可以使用 Windows 窗体数据绑定在相应的基础数据更改时自动更新用户界面。通过在绑定的对象上实现一个更改通知事件，可以完成该任务。更改通知事件按照以下约定命名。</P><PRE class=codeSample>public event EventHandler Changed; 
</PRE>
<P>因此，假设您将某个对象的 <B>Name</B> 属性绑定到用户界面，然后该对象的名称由于其他某种处理而更改，则您可以通过实现绑定对象上的 <B>NameChanged</B> 事件来自动更新用户界面，以反映新的 <B>Name</B> 值。</P>
<DIV style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 10px"><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter2HandlingData.mspx#top"><IMG height=9 alt=返回页首 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_up.gif" width=7 border=0></A><A class=topOfPage href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter2HandlingData.mspx#top"><FONT color=#002c99>返回页首</FONT></A></DIV><A name=ECAA></A>
<H2>小结</H2>
<P>在确定如何在智能客户端处理数据时，涉及到许多不同的注意事项。您需要确定是否缓存以及如何缓存您的数据，并且确定如何处理数据并发问题。您将经常决定使用 ADO.NET 数据集来处理您的数据，并且您还可能将决定利用 Windows 窗体数据绑定功能。</P>
<P>在许多情况下，只读引用数据和瞬态数据需要进行不同的处理。因为智能客户通常使用这两种类型的数据，所以您需要确定在应用程序中处理各个类别数据的最佳方式。</P>
<P><A href="http://msdn.microsoft.com/library/en-us/dnpag/html/SCAG-CH02.asp" target=_blank><FONT color=#002c99>转到原英文页面</FONT></A></P><BR style="FONT-SIZE: 0pt" clear=all><img src ="http://www.blogjava.net/TrampEagle/aggbug/30141.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/TrampEagle/" target="_blank">TrampEagle</a> 2006-02-10 14:51 <a href="http://www.blogjava.net/TrampEagle/articles/30141.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>智能客户端体系结构与设计指南 第 1 章 — 简介</title><link>http://www.blogjava.net/TrampEagle/articles/30139.html</link><dc:creator>TrampEagle</dc:creator><author>TrampEagle</author><pubDate>Fri, 10 Feb 2006 06:49:00 GMT</pubDate><guid>http://www.blogjava.net/TrampEagle/articles/30139.html</guid><wfw:comment>http://www.blogjava.net/TrampEagle/comments/30139.html</wfw:comment><comments>http://www.blogjava.net/TrampEagle/articles/30139.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/TrampEagle/comments/commentRss/30139.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/TrampEagle/services/trackbacks/30139.html</trackback:ping><description><![CDATA[<H1><FONT size=4>引自：</FONT><A HREF="/TrampEagle/admin/EditArticles.aspx"><FONT size=4>http://www.blogjava.net/TrampEagle/admin/EditArticles.aspx</FONT></A></H1>
<H1>第 1 章 — 简介</H1>
<H2 class=subtitle></H2>
<DIV class=date>发布日期： 8/20/2004<SPAN class=datePipe> | </SPAN>更新日期： 8/20/2004</DIV>
<DIV class=overview>
<DIV style="WIDTH: 250px"><IMG height=68 alt=a src="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/art/pponline.gif" width=250 border=0><BR>
<P class=figureCaption></P>
<DIV class=figureRule></DIV></DIV>
<P><B>智能客户端体系结构与设计指南</B></P>
<P>David Hill, Brenton Webster, Edward A. Jezierski, Srinath Vasireddy and Mohammad Al-Sabt, Microsoft Corporation; Blaine Wastell, Ascentium Corporation; Jonathan Rasmusson and Paul Gale, ThoughtWorks; and Paul Slater, Wadeware LLC</P>
<P>相关链接</P>
<P>Microsoft® <I>patterns &amp; practices</I> 库 <A href="http://www.microsoft.com/resources/practices/default.mspx" target=_blank>http://www.microsoft.com/resources/practices/default.mspx</A></P>
<P><I>Application Architecture for .NET:Designing Applications and Services</I> h<A href="http://msdn.microsoft.com/library/en-us/dnbda/html/distapp.asp" target=_blank>ttp://msdn.microsoft.com/library/en-us/dnbda/html/distapp.as</A>p</P>
<P><B>摘要</B>：本章对智能客户端应用程序进行了高级介绍，并描述了它们的一些基本性质和优点。然后，本章讨论了一些高级体系结构问题，并且提供了相关指导以帮助您确定智能客户端体系结构是否适合于您的应用程序。</P></DIV>
<CENTER><IMG title="" height=6 alt=* src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/3squares.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/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter1Introduction.mspx#ELAA"><IMG height=9 alt=什么是智能客户端？ hspace=4 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_down.gif" width=7 vspace=2 border=0></A></TD>
<TD class=onThisPage><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter1Introduction.mspx#ELAA">什么是智能客户端？</A></TD></TR>
<TR vAlign=top>
<TD><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter1Introduction.mspx#EKAA"><IMG height=9 alt=智能客户端的类型 hspace=4 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_down.gif" width=7 vspace=2 border=0></A></TD>
<TD class=onThisPage><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter1Introduction.mspx#EKAA">智能客户端的类型</A></TD></TR>
<TR vAlign=top>
<TD><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter1Introduction.mspx#EJAA"><IMG height=9 alt=对智能客户端和瘦客户端进行取舍 hspace=4 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_down.gif" width=7 vspace=2 border=0></A></TD>
<TD class=onThisPage><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter1Introduction.mspx#EJAA">对智能客户端和瘦客户端进行取舍</A></TD></TR>
<TR vAlign=top>
<TD><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter1Introduction.mspx#EIAA"><IMG height=9 alt=智能客户端体系结构难题 hspace=4 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_down.gif" width=7 vspace=2 border=0></A></TD>
<TD class=onThisPage><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter1Introduction.mspx#EIAA">智能客户端体系结构难题</A></TD></TR>
<TR vAlign=top>
<TD><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter1Introduction.mspx#EHAA"><IMG height=9 alt=本指南的范围 hspace=4 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_down.gif" width=7 vspace=2 border=0></A></TD>
<TD class=onThisPage><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter1Introduction.mspx#EHAA">本指南的范围</A></TD></TR>
<TR vAlign=top>
<TD><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter1Introduction.mspx#EGAA"><IMG height=9 alt=如何使用本指南 hspace=4 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_down.gif" width=7 vspace=2 border=0></A></TD>
<TD class=onThisPage><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter1Introduction.mspx#EGAA">如何使用本指南</A></TD></TR>
<TR vAlign=top>
<TD><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter1Introduction.mspx#EFAA"><IMG height=9 alt=本指南面向的读者 hspace=4 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_down.gif" width=7 vspace=2 border=0></A></TD>
<TD class=onThisPage><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter1Introduction.mspx#EFAA">本指南面向的读者</A></TD></TR>
<TR vAlign=top>
<TD><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter1Introduction.mspx#EEAA"><IMG height=9 alt=章节概要 hspace=4 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_down.gif" width=7 vspace=2 border=0></A></TD>
<TD class=onThisPage><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter1Introduction.mspx#EEAA">章节概要</A></TD></TR>
<TR vAlign=top>
<TD><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter1Introduction.mspx#EDAA"><IMG height=9 alt=小结 hspace=4 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_down.gif" width=7 vspace=2 border=0></A></TD>
<TD class=onThisPage><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter1Introduction.mspx#EDAA">小结</A></TD></TR>
<TR vAlign=top>
<TD><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter1Introduction.mspx#ECAA"><IMG height=9 alt=更多信息 hspace=4 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_down.gif" width=7 vspace=2 border=0></A></TD>
<TD class=onThisPage><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter1Introduction.mspx#ECAA">更多信息</A></TD></TR></TBODY></TABLE>
<P>欢迎阅读《智能客户端体系结构与设计指南》。智能客户端应用程序是瘦客户端应用程序的强大替代产品。它们可以为用户提供内容丰富且响应迅速的用户界面，提供脱机工作的能力，并且提供利用本地硬件和软件资源的方法。此外，可以将它们设计为在各种各样的客户端设备上运行，包括桌面 PC、平板 PC 以及手持移动设备（如 Pocket PC 和 Smartphone）。智能客户端为用户提供了在强大且直观的客户端环境中访问信息和远程服务的能力，并且是一种用来开发灵活的、面向用户的应用程序以及提高用户工作效率和满意度的有效的解决方案。</P>
<P>经过设计，智能客户端应用程序可以将胖客户端应用程序的传统优点与瘦客户端应用程序的可管理性优点结合起来。然而，要完全实现智能客户端应用程序的优点，需要考虑许多体系结构和设计问题。本指南描述了您在设计和实现智能客户端应用程序时将面临的体系结构和设计难题。它提供了有关如何克服这些难题的指导，从而使您能够在尽可能短的时间内实现智能客户端应用程序的优点。</P>
<P><B>注</B> 有关智能客户端的其他技术资源，请参考“智能客户端开发人员中心”，网址为：<A href="http://msdn.microsoft.com/smartclient/" target=_blank>http://msdn.microsoft.com/smartclient/</A>。在位于 <A href="http://www.microsoft.com/net/smartclient/default.mspx" target=_blank>http://www.microsoft.com/net/smartclient/default.mspx</A> 的 Microsoft .NET 站点上讨论了智能客户端的业务价值。</P><A name=ELAA></A>
<H2>什么是智能客户端？</H2>
<P>要完全了解智能客户端如何将胖客户端与瘦客户端的优点结合起来，有用的做法是分析胖/瘦客户端应用程序模型背后的历史和基础原理，并且回顾一些与它们相关联的优点和缺点。</P>
<H3>胖客户端应用程序</H3>
<P>在二十世纪九十年代中期，为 Microsoft® Windows® 操作系统开发的胖客户端应用程序的数量急剧增长。设计这些客户端的目的是利用本地硬件资源以及客户端操作系统平台的功能。 </P>
<P>尽管许多上述应用程序的功能令人印象深刻，但它们都具有局限性。许多上述应用程序是独立的并且在客户计算机上工作，对它们的工作环境所知甚少或一无所知。该环境包括其他计算机和网络上的任何服务，以及用户计算机上的任何其他应用程序。非常常见的情况是，应用程序之间的集成局限于使用 Windows 提供的剪切或复制并粘贴功能在应用程序之间传输少量的数据。</P>
<P>有一些有助于提高胖客户端应用程序连接性的技术。例如，两层应用程序使多个用户可以访问驻留在网络上的公用数据，而 DCOM 使应用程序可以具有更高的分布性。（就 DCOM 而言，不再将逻辑和状态与客户计算机相联系，而是将其封装在对象内，然后在多台计算机中分布这些对象。）但是，连接的应用程序的开发要复杂得多。随着这些分布式应用程序的规模和复杂性逐渐增加，将越来越难以维持客户端应用程序及它们使用的服务之间的紧耦合。</P>
<P>尽管胖客户端通常提供了高质量、响应迅速的用户体验，并且具有良好的开发人员和平台支持，但它们非常难于部署和维护。随着应用程序和客户端平台的复杂性不断增加，以可靠且安全的方式将应用程序部署到客户计算机的难度也将不断增加。如果部署了不兼容的共享组件或软件库，则一个应用程序可以很容易地破坏另一个应用程序，这种现象称为<I>应用程序脆弱性</I>。新版本的应用程序通常通过重新部署整个应用程序来提供，这可能使应用程序脆弱性问题变得更加严重。</P>
<H3>瘦客户端应用程序</H3>
<P>Internet 提供了传统胖客户端模型的替代模型，它解决了许多与应用程序部署和维护相关联的问题。基于浏览器的瘦客户端应用程序是在中央 Web 服务器上部署和更新的；因此，它们消除了将应用程序的任何部分显式部署到客户计算机并加以管理的必要性。</P>
<P>该模型使各个公司可以非常高效地将它们的应用程序公开给规模庞大、多种多样的外部受众。因为瘦客户端已被证明能够高效地解决一些部署和可管理性问题，所以它们现在用于向组织内的用户提供对许多业务线 (LOB) 应用程序的访问，以及向客户和合作伙伴提供对面向外界的应用程序的访问。尽管事实上这两种用户的需要和期望通常是根本不同的，也是如此。</P>
<P>瘦客户端应用程序具有一些缺点。浏览器必须总是具有网络连接。这意味着移动用户在断开连接时将无法访问应用程序，因此当他们返回办公室时，必须重新输入数据。而且，常用的应用程序功能（如拖放、撤消-重复以及上下文相关帮助）可能不可用，这可能降低应用程序的可用性。</P>
<P>因为应用程序的大部分逻辑和状态位于服务器上，所以瘦客户端会频繁地向服务器发回数据和处理请求。浏览器必须等待响应到达，然后用户才能继续使用该应用程序；因此，该应用程序的响应速度通常要比胖客户端应用程序慢得多。该问题在低带宽或高延迟的情况下被恶化了，并且产生的性能问题可能导致应用程序可用性和用户效率大幅度下降。要求输入大量数据以及/或者在多个窗口中频繁导航的 LOB 应用程序尤其会受到这一问题的影响。</P>
<H3>智能客户端应用程序</H3>
<P>经过设计，智能客户端应用程序可以将胖客户端应用程序的优点与瘦客户端应用程序的部署和可管理性优点结合起来，尽管这两种方法之间的平衡的准确性质取决于确切的情况。</P>
<P>智能客户端应用程序通常具有形形色色的要求，因此在设计和实现方面会有极大的差异。但是，所有智能客户端都具有下列部分或全部特征： </P>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>利用本地资源 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>利用网络资源 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>支持偶尔连接的用户 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>提供智能安装和更新 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>提供客户端设备灵活性 </P></TD></TR></TBODY></TABLE>
<P>许多应用程序不需要具有上述所有特征。当您设计您的智能客户端时，将需要仔细考虑您的应用程序方案，并且决定您的智能客户端应用程序要求具备上述哪些特征。要将上述所有特征合并到您的应用程序中，将需要进行非常认真的计划和设计，并且在很多情况下您将需要大量的实现资源。</P>
<P><B>注</B> .NET 框架可以帮助您实现智能客户端应用程序的许多特征。该框架提供了具备自我描述能力且牢固绑定的程序集，并且支持单独和并列安装应用程序的多个版本，从而有助于减少与胖客户端相关联的应用程序部署和脆弱性问题。.NET 框架基类库为与 Web 服务进行交互提供了广泛的支持，并且提供了 Windows 窗体。通过使用公共语言运行库 (CLR)，您可以利用任何受到 .NET 支持的语言来开发智能客户端。</P>
<H3>使用本地资源</H3>
<P>设计良好的智能客户端应用程序最大限度地利用了代码和数据部署在客户端上并且在本地执行和访问这一事实。它为应用程序提供了内容丰富且响应迅速的用户界面，以及强大的客户端处理能力。例如，它可能使用户能够执行复杂的数据操作、可视化、搜索或排序操作。</P>
<P>智能客户端可以利用客户端硬件资源（如电话或条码读取器）以及其他软件和应用程序。这使它们非常适合于解决瘦客户端应用程序（如销售点终端应用程序）无法很好解决的问题。智能客户端还可以利用本地软件（如 Microsoft Office 应用程序）或客户计算机上安装的任何 LOB 应用程序。通过创建能够与多个 LOB 应用程序集成并对这些应用程序进行协调的解决方案，您的用户可以更为有效地工作，进行更好的决策，并减少数据输入错误。此类解决方案还可以使您的应用程序更加紧密地与用户的工作环境集成（例如，通过采用自定义的或熟悉的用户界面），从而降低培训成本。</P>
<P>可以通过智能客户端应用程序集成或协调其他客户端应用程序，以便提供一致且高效的总体解决方案。这些应用程序还应该了解正在使用应用程序的上下文，并且应该适应该上下文以尽可能地帮助用户；例如，通过根据用户的使用模式或角色抢先缓存适当且有用的数据。</P>
<P>通过最大限度地使用本地资源以及将本地资源集成到您的智能客户端应用程序，可以使您的应用程序更好、更有效地使用已经提供给您的硬件。非常常见的情况是，处理能力、内存和高级图形功能没有得到利用。使用客户计算机上的资源还可以减少服务器端硬件要求。</P>
<H3>使用网络资源</H3>
<P>智能客户端可以通过网络消耗和使用不同的服务和数据。它们是从许多不同的源检索数据的有效方式，并且可以设计为对数据进行分析或整合，从而使用户能够进行更为有效和明智的决策。例如，智能客户端可以使用映射服务来提供有关地点和驾驶方向的详细信息。</P>
<P>智能客户端应用程序应该尽可能地连接，并且应该利用可以通过网络使用的资源和服务。它们不应该是独立的应用程序，并且应该总是构成更大的分布式解决方案的一部分。智能客户端应用程序起码应该使用有助于维护该应用程序以及提供部署和更新服务的集中式服务。</P>
<P>智能客户端应用程序的连接性质使其可以提供有价值的数据整合、分析和转换服务。它们使用户可以实时地或者在一段时间内协作完成任务。在许多情况下，智能客户端应用程序可以向用户提供类似于门户的功能，从而将完全不同的数据和服务加以协调并集成到总体解决方案中。</P>
<P>有关如何设计智能客户端以利用连接的服务的详细信息，请参阅<A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter2HandlingData.mspx">第 2 章：处理数据</A>。</P>
<H3>支持偶尔连接的用户</H3>
<P>可以将智能客户端设计为向偶尔连接到网络的用户提供功能，从而使用户可以在明确脱机、使用低带宽或高延迟网络，或者连接时断时续的情况下继续高效地工作。对于移动应用程序，智能客户端还可以优化网络带宽 — 例如，通过将请求批量发送到服务器，以便更好地使用代价昂贵的连接。</P>
<P>即使当客户端大多数时间都连接到网络时，智能客户端应用程序也可通过以智能方式缓存数据和管理连接来改善性能和可用性。例如，在低带宽或高延迟环境中，智能客户端应用程序可以用特殊的方式管理连接，即不损害应用程序的可用性和响应性，并且用户可以继续高效地工作。</P>
<P>通过使用户能够在断开连接或只偶尔连接的情况下工作，提高了用户的工作效率和满意度。智能客户端应用程序应该致力于在脱机时提供尽可能多的功能。</P>
<P>有关如何设计智能客户端应用程序以支持偶尔连接的用户的详细信息，请参阅<A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter4OccConnSC.mspx">第 4 章：偶尔连接的智能客户端</A>。</P>
<H3>提供智能安装和更新</H3>
<P>传统胖客户端所具有的一些最大的问题发生在部署或更新应用程序的时候。许多胖客户端应用程序具有大量复杂的安装要求，并且可能通过注册组件以及/或者在公共位置安装 DLL 来共享代码，从而导致应用程序脆弱性和更新困难。</P>
<P>可以对智能客户端应用程序进行设计，以使其按照远比传统胖客户端应用程序更智能和灵活的方式来管理它们的部署和更新。它们可以避免上述常见问题，从而有助于减少应用程序的管理成本。</P>
<P>有许多部署智能客户端的不同方式。这些方式包括：简单地将文件复制到本地计算机；使用非接触式部署自动从中央服务器下载代码；或者使用企业推技术如 Microsoft Systems Management Server (SMS) 来部署 Windows Installer 软件包。您选择的方法将依赖于您的特定情况。</P>
<P>智能客户端应用程序可以在其运行时或位于后台时对自身进行自动更新。这一功能使其可以逐个角色地进行更新；以分阶段的方式更新，从而可以将应用程序推介给先导小组或受限的用户组；或者按照制定的时间表更新。</P>
<P>.NET 框架使您可以对应用程序组件进行强命名，这意味着应用程序可以指定用来生成和测试其确切版本的组件，并通过这些版本的组件运行。.NET 框架使应用程序可以相互隔离，以便在安装一个应用程序时不会破坏另一个应用程序，并且同一应用程序的多个版本可以并列部署。这些功能大大简化了应用程序部署，并且消除了许多与胖客户端应用程序相关联的应用程序脆弱性问题。</P>
<P>有关智能安装和更新的详细信息，请参阅<A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter7DeployUpdSCApp.mspx">第 7 章：部署和更新智能客户端</A>。</P>
<H3>提供客户端设备灵活性</H3>
<P>智能客户端还可以提供灵活且可自定义的客户端环境，从而使用户可以将应用程序配置为支持他或她喜欢的工作方式。智能客户端应用程序没有被限制到桌面计算机或膝上型计算机。随着小规模设备的连接性和能力的增加，愈发需要能够提供对多个设备上重要数据和服务的访问的有用客户端应用程序。与 .NET 框架压缩版一起，.NET 框架提供了一个可用来生成智能客户端应用程序的通用平台。</P>
<P>可以对智能客户端进行设计以使其适应宿主环境，并且为它们运行时所在的设备提供适当的功能。例如，适合在 Pocket PC 上运行的智能客户端应用程序应该提供相应的用户界面，该用户界面在较小的屏幕区域上被调整为使用笔针。</P>
<P>在许多情况下，您需要设计多个版本的智能客户端应用程序，每个版本都面向特定的设备类型，以便充分利用该设备所支持的特定功能。因为小规模设备通常在提供完整范围的智能客户端应用程序功能方面受到限制，所以它们可能只提供对功能完善的智能客户端应用程序所提供的数据和服务子集的移动访问，或者它们可用于在用户移动时收集和整合数据。最后，可以由功能更加完善的智能客户端应用程序或服务器端应用程序来分析或处理这些数据。</P>
<P>能够感知目标设备的功能和使用环境（无论它是桌面、膝上型、平板还是移动设备），以及能够定制应用程序以提供最适当的功能，这些都是许多智能客户端应用程序的基本特点。</P>
<P><B>注</B> 本指南不包含特定于在移动设备上运行的智能客户端应用程序开发的体系结构和设计细节，但是，无论应用程序是在桌面计算机上运行还是在其他设备上运行，本指南包含的许多主题都同样适用。</P>
<DIV style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 10px"><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter1Introduction.mspx#top"><IMG height=9 alt=返回页首 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_up.gif" width=7 border=0></A><A class=topOfPage href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter1Introduction.mspx#top">返回页首</A></DIV><A name=EKAA></A>
<H2>智能客户端的类型</H2>
<P>智能客户端在设计和实现方面差异极大，这既包括应用程序要求，也包括可以使用它们的方案和环境的数量。因此，智能客户端可以采取许多不同的形式和风格。根据智能客户端应用程序所面向的平台，可以将这些形式划分为三大类： </P>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>Windows 智能客户端应用程序 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>Office 智能客户端应用程序 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>移动智能客户端应用程序 </P></TD></TR></TBODY></TABLE>
<P>智能客户端应用程序面向上述一种或多种平台是很常见的情况，具体取决于用户的角色以及需要的功能。这种灵活性是智能客户端应用程序的主要长处之一。</P>
<P>本指南的其余部分将重点讨论所有三种类型的智能客户端应用程序所共有的问题，而不是详细解释影响个别类别的问题。但是，依次对各个类型进行简要分析，以便您可以确定哪种风格的应用程序最适合您的情况，这将是很有用的。</P>
<H3>Windows 智能客户端应用程序</H3>
<P>当您想到胖客户端应用程序时，您通常可能会想到使用可用的系统资源并且提供内容丰富的用户界面的桌面应用程序。面向 Windows 的智能客户端应用程序是在传统胖客户端应用程序的基础上发展而来的，并且提供特定的面向特殊目标的功能。</P>
<P>这些种类的应用程序通常使用 Windows 窗体来提供熟悉的 Windows 风格的用户界面，并由应用程序本身提供大部分功能，而且不依赖于其他应用程序来提供主用户界面。这样的智能客户端既可能包括通过 HTTP 部署的简单应用程序，也可能包括非常复杂的应用程序。</P>
<P>Windows 智能客户端应用程序适合于需要将应用程序作为熟悉的桌面类型应用程序进行部署和访问的情况。这些类型的应用程序通常由其自身提供其大部分功能，但是在适当的时候可以与其他应用程序集成或者协调其他应用程序。它们提供针对特定任务进行调整的应用程序功能，以提供特定的或高性能的处理或图形能力。</P>
<P>Windows 智能客户端应用程序通常最适合于在桌面 PC、膝上型 PC 或平板 PC 上运行的应用程序。此外，它们通常不会提供与特定文档或文档类型紧密关联的功能。</P>
<P>可以在各种各样的场合下使用这些种类的 Windows 智能客户端应用程序，例如作为 LOB、财务、科学或协作应用程序使用。这些种类的应用程序的示例有 Microsoft Money 以及 Microsoft Outlook® 消息处理和协作客户端。 </P>
<H3>Office 智能客户端应用程序</H3>
<P>Microsoft Office System 2003 为您提供了用来生成智能客户端应用程序（尤其是在企业设置中）的有用平台。通过 Office 智能客户端解决方案，您可以将通过 Web 服务访问的数据源与 Word 2003、Excel 2003、InfoPath 2003 或其他 Office应用程序的功能集成起来，以开发智能客户端解决方案。</P>
<P>这样的 Office 智能客户端应用程序可以成为组织的信息管理周期的集成部分，而不只是文档数据的静态容器。当用户在文档内工作时，它们可以提供上下文相关的数据，以及可以将 Web 服务公开的数据转换为有用信息的工作流和任务指导、数据分析、协作、报告和呈现功能。 </P>
<P>Microsoft Office 支持 XML，并且可以将该数据与文档的其他方面分开，以便它可以由其他应用程序重新使用。因为 Microsoft Office 中的应用程序数据可以由多个应用程序中相同的客户定义 XML 架构进行说明，所以开发人员可以将这些数据集成到智能客户端应用程序中。</P>
<P>Microsoft Office 2003 具有许多用于生成智能客户端解决方案的重要功能和选项。这些功能和选项包括： </P>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>智能标记</B>。智能标记为应用程序提供一种方法，可以为用户提供与文档内容有关的上下文相关数据，用户在文档内工作时，可以通过该方法容易地查看和使用相关信息。例如，使用智能标记，可以在文档内引用客户时提供相应客户的帐户状态，或者可以在键入订单 ID 时提供订单状态信息。这种上下文相关的反馈使用户可以在工作时进行更为明智的决策。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>智能文档</B>。智能文档为用户提供了更为强大的与文档和业务 Web 服务进行交互的方法。智能文档是 Word 2003 和 Excel 2003（它们具有基础 XML 结构和自定义的任务窗格）的一种新的解决方案模型。可以使用该任务窗格向用户显示上下文信息、任务、工具、后续步骤以及其他相关信息。用户能够通过与该任务窗格交互来启动其他操作和任务，从而可以构建综合性业务解决方案。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>适用于</B><B> Microsoft Office System </B><B>的</B><B> Microsoft Visual Studio_ </B><B>工具</B>。该工具套件使开发人员能够通过使用 Microsoft Visual Studio .NET 2003 开发系统来创建托管代码 Office 智能客户端应用程序。开发人员可以将文档解决方案与基础代码分开（与以前的包含带有自定义逻辑的 Visual Basic for Applications 宏的智能客户端模型相比，这是一种替代解决方案）。通过将托管代码与 Microsoft Office 一起使用，开发人员可以获得更多为智能客户端解决方案创建、部署和管理更新的有效选项。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>Microsoft Office InfoPath™ 2003</B>。InfoPath 2003 是能够使用类似于表单的界面从用户那里收集结构性数据的应用程序。InfoPath 2003 提供了对 XML Web 服务（一种基于表单的用户界面）的支持，以及对标准技术（如 WSDL 和 UDDI）的支持。InfoPath 2003 支持有限的脱机使用，方法是允许用户在脱机时与表单进行交互，然后允许用户在联机时将该表单转发给 Web 服务。 </P></TD></TR></TBODY></TABLE>
<P>本指南并不试图涉及特定于 Office 智能客户端的所有问题，但所涉及的大多数主题都完全与上面讨论的智能客户端应用程序有关。</P>
<H3>移动智能客户端应用程序</H3>
<P>移动智能客户端是在智能设备上运行的应用程序，这些智能设备包括 Pocket PC、Smartphone 以及其他超小型台式设备（如机顶盒）。这些应用程序是使用 .NET 框架压缩版（它是完整 .NET 框架的子集）开发的。</P>
<P>.NET 框架压缩版具有完整 .NET 框架的许多功能，支持 XML，并且消耗 Web 服务。它被进行了优化以便在超小型台式设备上使用，并且它包含用于开发用户界面的 Windows 窗体设计器。</P>
<P>通过使用 Visual Studio .NET 智能设备项目，您可以开发能够在 .NET 框架压缩版上运行的智能客户端。采用这一方法，您可以通过在超小型台式设备的模拟器上使用 Visual Studio .NET 来开发、测试和调试应用程序。模拟器的使用大大加快了这些类型应用程序的开发和测试速度。</P>
<P>移动智能客户端应用程序通常用于提供对重要数据和服务的移动访问，或者在用户处于移动状态时收集和整合数据。这些类型应用程序的示例有保险和金融数据收集应用程序、库存管理应用程序和个人工作效率管理应用程序。</P>
<P>本指南并未特地集中讨论移动智能客户端应用程序，尽管所讨论的许多体系结构问题和解决方案都与智能设备相关。</P>
<DIV style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 10px"><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter1Introduction.mspx#top"><IMG height=9 alt=返回页首 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_up.gif" width=7 border=0></A><A class=topOfPage href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter1Introduction.mspx#top">返回页首</A></DIV><A name=EJAA></A>
<H2>对智能客户端和瘦客户端进行取舍</H2>
<P>要针对您的情况选择正确的应用程序体系结构，必须考虑许多因素。要确定智能客户端方法是否最适合您的应用程序，请认真考虑您当前和将来业务应用程序的需要。如果您的应用程序基于不适合的体系结构，则它可能无法满足用户和整个企业的要求和期望。如果以后更改体系结构以满足新的要求或者利用新的机遇，则可能要付出极为高昂的代价。</P>
<P>如果您需要向各种外部受众提供面向外界的应用程序，则瘦客户端体系结构通常最为适当；而对于需要与其他客户端应用程序或硬件集成或者对它们进行协调，或者需要脱机工作或通过响应迅速的用户界面提供特定高性能功能的内部应用程序而言，智能客户端体系结构通常最为适当。</P>
<P>实际上，这两种方法在很大程度上互相重叠，而且每种方法都具有明显的优点和缺点。您只有在认真考虑您的要求并且了解如何在您所处的场合下应用每种方法之后，才能够选择正确的方法。您可以使用表 1.1 来帮助您对智能客户端和瘦客户端体系结构进行取舍。</P>
<P><B>表</B><B> 1.1 </B><B>瘦客户端和智能客户端的特点</B></P>
<TABLE class=dataTable id=EAJAA cellSpacing=0 cellPadding=0>
<THEAD>
<TR class=stdHeader vAlign=top>
<TD id=colECBAJAA>特点</TD>
<TD id=colEBBAJAA>瘦客户端</TD>
<TD id=colEABAJAA style="BORDER-RIGHT: #cccccc 1px solid">智能客户端</TD></TR></THEAD>
<TBODY>
<TR class=record vAlign=top>
<TD>
<P class=lastInCell>提供内容丰富的用户界面</P></TD>
<TD>
<P class=lastInCell>可以，但难以开发、测试和调试。通常将应用程序与单个浏览器联系起来。</P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>是。更易于开发、测试和调试。</P></TD></TR>
<TR class=evenRecord vAlign=top>
<TD>
<P class=lastInCell>可以利用本地计算机上的硬件资源</P></TD>
<TD>
<P class=lastInCell>是，但只能通过 COM 组件使用。</P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>是</P></TD></TR>
<TR class=record vAlign=top>
<TD>
<P class=lastInCell>可以与其他本地应用程序交互</P></TD>
<TD>
<P class=lastInCell>否</P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>是</P></TD></TR>
<TR class=evenRecord vAlign=top>
<TD>
<P class=lastInCell>可以多线程化</P></TD>
<TD>
<P class=lastInCell>否</P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>是</P></TD></TR>
<TR class=record vAlign=top>
<TD>
<P class=lastInCell>可以脱机工作</P></TD>
<TD>
<P class=lastInCell>否</P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>是</P></TD></TR>
<TR class=evenRecord vAlign=top>
<TD>
<P class=lastInCell>在低带宽和高延迟环境中可以很好地执行。</P></TD>
<TD>
<P class=lastInCell>否</P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>是</P></TD></TR>
<TR class=record vAlign=top>
<TD>
<P class=lastInCell>易于部署</P></TD>
<TD>
<P class=lastInCell>是</P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>不一定。困难程度取决于应用程序要求。</P></TD></TR>
<TR class=evenRecord vAlign=top>
<TD>
<P class=lastInCell>低维护和更改管理成本</P></TD>
<TD>
<P class=lastInCell>是</P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>不一定。成本取决于应用程序要求。</P></TD></TR>
<TR class=record vAlign=top>
<TD>
<P class=lastInCell>可以部署到具有不同功能的多种客户端上。</P></TD>
<TD>
<P class=lastInCell>是，尽管更复杂的瘦客户端可能要求单个浏览器。</P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>是。可以在支持 .NET 框架（包括 .NET 框架压缩版）的任何平台上部署。</P></TD></TR></TBODY></TABLE>
<DIV class=dataTableBottomMargin></DIV>
<DIV style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 10px"><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter1Introduction.mspx#top"><IMG height=9 alt=返回页首 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_up.gif" width=7 border=0></A><A class=topOfPage href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter1Introduction.mspx#top">返回页首</A></DIV><A name=EIAA></A>
<H2>智能客户端体系结构难题</H2>
<P>智能客户端的体系结构难题不同于瘦客户端，并且您需要在您的应用程序设计中予以考虑。智能客户端应用程序具有显著的优点，但是您只有在适当地解决这些难题之后才能实现这些优点。</P>
<P>通过智能客户端可以将数据和逻辑分发到客户计算机，而瘦客户端则倾向于将数据和逻辑集中存放在 Web 服务器和其他后端服务中。尽管可以通过智能客户端方法使应用程序变得更加高效，并且不需要与服务器进行往返通讯以确定后续步骤，但您需要考虑到应用程序及其数据现在比瘦客户端应用程序分布得更为广泛，并且相应地修改您的设计。</P>
<P>如果您要在客户端上实现业务规则，您将需要在必要时更新这些规则，而不是更新整个应用程序。这可能意味着您需要使用不同的机制来分别更新该应用程序以及更新该应用程序内部的业务规则。</P>
<P>通过在客户端上缓存数据，您可以显著改善应用程序的性能和可用性，但您必须确保适当地刷新数据并且不会使用陈旧的数据。因为许多用户可以访问和使用相同的数据，您还必须考虑数据并发的影响。您的应用程序必须能够处理出现的数据冲突或一致问题，因为应用程序现在分布得更为广泛，并且可以脱机操作。<A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter3GettingConnected.mspx">第 3 章：建立连接</A>详细讨论了这些问题。</P>
<P>.NET 框架在智能客户端应用程序的宿主方式方面提供了极大的灵活性。应用程序可以作为传统桌面应用程序运行，还可以宿主在 Office 或 Microsoft Internet Explorer 内。也可以进行许多种组合。例如，Windows 窗体应用程序可以宿主 Internet Explorer 或 Office 组件，并且任何宿主都可以包含任何其他宿主。</P>
<P>您可以将不稳定的应用程序逻辑（例如，控制大订单折扣的业务规则）结合到根据需要通过 HTTP 下载的程序集中。这样做消除了在开发新应用程序逻辑时部署新版本客户端应用程序的需要。您可以使用与附加（或不常使用）应用程序功能相同的模型，以便使初始应用程序规模保持最小，并且根据需要安装附加功能。</P>
<P>您可以选择将您的智能客户端作为复合应用程序进行部署，此时许多应用程序组合起来构成一个统一的解决方案。可以通过耦合桌面应用程序，或者通过提供一般性的外壳应用程序以容纳多个共同构成解决方案的轻型应用程序，来构成这样的解决方案。</P>
<P>当用户必须访问许多应用程序以完成其工作时，复合应用程序尤其有用。例如，呼叫中心中的客户服务代理通常必须使用许多 LOB 应用程序，包括桌面应用程序、基于浏览器的应用程序以及基于终端的应用程序。所有这些 LOB 应用程序都可以宿主到在它们之间提供集成的普通 Windows 窗体应用程序中，从而大大简化用户的工作，最为重要的是减少花在特定呼叫上的时间。通过提供普通外壳来宿主这些 LOB 应用程序，可以在不同的解决方案中开发、测试和重用通用基础结构功能，如安全性、部署、窗口管理、应用程序集成、审核等等，从而使 LOB 应用程序的开发人员能够将精力集中于业务功能。</P>
<P>面向服务的体系结构的出现意味着您可以设计智能客户端来利用网络服务。所有此类服务都是以行业标准方式提供的，这就改善了互操作性、开发人员工具支持以及在智能客户端应用程序中内置新功能的容易程度。</P>
<DIV style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 10px"><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter1Introduction.mspx#top"><IMG height=9 alt=返回页首 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_up.gif" width=7 border=0></A><A class=topOfPage href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter1Introduction.mspx#top">返回页首</A></DIV><A name=EHAA></A>
<H2>本指南的范围</H2>
<P>本指南重点讨论围绕建立在 Microsoft .NET 技术基础之上的智能客户端应用程序的体系结构和设计问题。它假设您要使用 Microsoft .NET 框架生成您的智能客户端应用程序，并且使用 Microsoft .NET Windows 窗体来生成任何用户界面。</P>
<P>本指南不会深入讨论实现问题。具体说来，本指南不会讨论在 Microsoft Office 2003 或移动设备上实现智能客户端应用程序的细节，尽管本指南所讨论的许多问题都与智能客户端应用程序有关 — 无论它们是独立的 Windows 窗体应用程序、Office 应用程序还是移动设备应用程序。</P>
<DIV style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 10px"><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter1Introduction.mspx#top"><IMG height=9 alt=返回页首 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_up.gif" width=7 border=0></A><A class=topOfPage href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter1Introduction.mspx#top">返回页首</A></DIV><A name=EGAA></A>
<H2>如何使用本指南</H2>
<P>本指南适合于以下面两种方式之一使用。首先，本指南具有很好的结构体系，对于您在生成智能客户端应用程序时可能面临的体系结构和设计问题提供了相当全面的概述。通过从头到尾阅读本指南，您将对可能面临的问题以及克服这些问题的办法有一个最完整的了解。</P>
<P>或者，如果您喜欢深入了解围绕特定主题的问题，则可以单独阅读相关章节，以学习对相关问题的完备讨论。</P>
<DIV style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 10px"><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter1Introduction.mspx#top"><IMG height=9 alt=返回页首 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_up.gif" width=7 border=0></A><A class=topOfPage href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter1Introduction.mspx#top">返回页首</A></DIV><A name=EFAA></A>
<H2>本指南面向的读者</H2>
<P>本指南适合于要开发建立在 Microsoft .NET 技术基础之上的智能客户端应用程序的软件架构师和开发人员。</P>
<H3>前提条件</H3>
<P>要从本指南获得最大的收获，您应该了解下列技术和概念： </P>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>Microsoft .NET 框架 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>Microsoft Visual Studio .NET 2003 开发工具 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>Microsoft® Visual C#® 开发工具 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>可扩展标记语言 (XML) </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>消息队列 (MSMQ) </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>多线程处理 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>关系数据库操作 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>分布式应用程序设计和体系结构 </P>
<P>注 有关分布式应用程序设计和体系结构的详细信息，请参阅 <A href="http://msdn.microsoft.com/library/en-us/vsent7/html/vxoriDesignConsiderationsForDistributedApplications.asp" target=_blank>http://msdn.microsoft.com/library/en-us/vsent7/html/vxoriDesignConsiderationsForDistributedApplications.asp</A> 和 <A href="http://msdn.microsoft.com/library/en-us/vsent7/html/vxoriplanningdistributedapplications.asp" target=_blank>http://msdn.microsoft.com/library/en-us/vsent7/html/vxoriplanningdistributedapplications.asp</A>。</P></TD></TR></TBODY></TABLE>
<DIV style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 10px"><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter1Introduction.mspx#top"><IMG height=9 alt=返回页首 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_up.gif" width=7 border=0></A><A class=topOfPage href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter1Introduction.mspx#top">返回页首</A></DIV><A name=EEAA></A>
<H2>章节概要</H2>
<P>本指南包含下列几章，每一章都讨论与智能客户端相关的特定问题。您可以根据需要阅读各章的全部或部分内容。</P>
<P><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter1Introduction.mspx">第 1 章：简介</A></P>
<P>本章对智能客户端应用程序进行了高级介绍，并描述了它们的一些基本性质和优点。然后，本章讨论了一些高级体系结构问题，并且提供了相关指导以帮助您确定智能客户端体系结构是否适合于您的应用程序。</P>
<P><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter2HandlingData.mspx">第 2 章：处理数据</A></P>
<P>在智能客户端中，可以在客户端上使用应用程序数据。需要对这些数据进行适当的管理，以确保其有效、一致和安全。如果这些数据是由服务器应用程序提供的，则智能客户端应用程序可以缓存这些数据来改善性能或者支持脱机使用。如果智能客户端应用程序提供在本地修改数据的能力，则必须在以后将客户端更改与服务器端应用程序进行同步。本章分析在客户端处理数据时的各种注意事项，包括数据缓存、数据并发以及数据集和 Windows 窗体数据绑定的使用。</P>
<P><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter3GettingConnected.mspx">第 3 章：建立连接</A></P>
<P>智能客户端应用程序通常构成更大的分布式应用程序的一部分，因此它们会频繁连接到网络并且与网络资源（如 Web 服务）以及客户计算机本身上的组件或进程交互。本章介绍了您的应用程序可以用来连接和使用这些资源的许多方法，并且讨论了每种方法的优点和缺点。</P>
<P><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter4OccConnSC.mspx">第 4 章：偶尔连接的智能客户端</A></P>
<P>本章讨论的问题涉及到您在设计和生成偶尔连接到网络的智能客户端应用程序时可能面临的问题。本章讨论了连接性的概念，介绍了两种实现脱机功能的主要方法，并且讨论了您在使应用程序可供脱机使用时需要考虑的一些问题。</P>
<P><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter5SecuCons.mspx">第 5 章：安全性注意事项</A></P>
<P>本章讨论了智能客户端安全性的问题。智能客户端将逻辑和数据分布到客户计算机上；因此，所涉及到的安全性问题与瘦客户端应用程序不同，后者的数据和逻辑被更多地限制在服务器中。本章讨论智能客户端应用程序中的数据安全性、身份验证、授权以及代码访问安全性的作用。</P>
<P><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter6UsingMultThr.mspx">第 6 章：使用多个线程</A></P>
<P>本章讨论与在智能客户端应用程序中使用多个线程相关的问题。要最大限度地提高智能客户端应用程序的响应性，您需要认真考虑如何以及何时使用多个线程。线程可以显著改善应用程序的可用性和性能，但当您确定线程将如何与用户界面交互时，需要进行非常认真的考虑。</P>
<P><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter7DeployUpdSCApp.mspx">第 7 章：部署和更新智能客户端</A></P>
<P>智能客户端不会受到传统上与胖客户端应用程序相关联的部署和更新问题的困扰。.NET 框架和 Windows 平台所提供的功能可帮助您避免许多与传统胖客户端部署相关联的问题。本章介绍如何最好地使用这些功能，以及如何对可用的部署和更新机制进行取舍。</P>
<DIV style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 10px"><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter1Introduction.mspx#top"><IMG height=9 alt=返回页首 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_up.gif" width=7 border=0></A><A class=topOfPage href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter1Introduction.mspx#top">返回页首</A></DIV><A name=EDAA></A>
<H2>小结</H2>
<P>瘦客户端和智能客户端都可用来向您的组织提供 LOB 应用程序。但是，每种类型的客户端都具有其优点和缺点。在设计应用程序时，您需要认真考虑您所处环境的具体细节，然后才能确定哪种客户端更为合适。本章已经介绍了智能客户端的发展历史以及与它们相关联的功能。现在，您可以使用本指南的其余部分来帮助您确定如何在您自己的组织中设计和实现智能客户端。</P>
<DIV style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 10px"><A href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter1Introduction.mspx#top"><IMG height=9 alt=返回页首 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_up.gif" width=7 border=0></A><A class=topOfPage href="http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter1Introduction.mspx#top">返回页首</A></DIV><A name=ECAA></A>
<H2>更多信息</H2>
<P>下列资源提供了有关 patterns &amp; practices、智能客户端以及您可以用于查找特定指导的其他应用程序块的更多信息。 </P>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>patterns &amp; practices Web 站点，位于： <A href="http://www.microsoft.com/resources/practices/default.mspx" target=_blank>http://www.microsoft.com/resources/practices/default.mspx</A></P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><A href="http://www.microsoft.com/resources/practices/completelist.asp" target=_blank>Patterns and Practices 库 </A></P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>MSDN® 上的“Overview of Smart Client Applications in the Microsoft Office System”，位于：<A href="http://msdn.microsoft.com/library/en-us/odc_ip2003_ta/html/odc_IPOffice2003SmartClient.asp" target=_blank>http://msdn.microsoft.com/library/en-us/odc_ip2003_ta/html/odc_IPOffice2003SmartClient.asp</A></P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>MSDN 上的“Application Architecture for .NET:Designing Applications and Services”，位于：<A href="http://msdn.microsoft.com/library/en-us/dnbda/html/distapp.asp" target=_blank>http://msdn.microsoft.com/library/en-us/dnbda/html/distapp.asp</A></P></TD></TR></TBODY></TABLE>
<P><A href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnpag/html/SCAG-CH01.asp" target=_blank>转到原英文页面</A></P><img src ="http://www.blogjava.net/TrampEagle/aggbug/30139.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/TrampEagle/" target="_blank">TrampEagle</a> 2006-02-10 14:49 <a href="http://www.blogjava.net/TrampEagle/articles/30139.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>SmartClient初探</title><link>http://www.blogjava.net/TrampEagle/articles/30137.html</link><dc:creator>TrampEagle</dc:creator><author>TrampEagle</author><pubDate>Fri, 10 Feb 2006 06:46:00 GMT</pubDate><guid>http://www.blogjava.net/TrampEagle/articles/30137.html</guid><wfw:comment>http://www.blogjava.net/TrampEagle/comments/30137.html</wfw:comment><comments>http://www.blogjava.net/TrampEagle/articles/30137.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/TrampEagle/comments/commentRss/30137.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/TrampEagle/services/trackbacks/30137.html</trackback:ping><description><![CDATA[<H3 21pt; TEXT-INDENT: -21pt; mso-list: l1 level1 lfo1; tab-stops: list 21.0pt><SPAN lang=EN-US 173%; mso-bidi-font-size: 10.5pt; mso-bidi-font-family: 宋体><SPAN style="mso-list: " Ignore>引自：<A href="http://delia.org.ru/ArticleView/2005-9-7/Article_View_129438.Htm">http://delia.org.ru/ArticleView/2005-9-7/Article_View_129438.Htm</A></SPAN></SPAN></H3>
<H3 21pt; TEXT-INDENT: -21pt; mso-list: l1 level1 lfo1; tab-stops: list 21.0pt><SPAN lang=EN-US 173%; mso-bidi-font-size: 10.5pt; mso-bidi-font-family: 宋体><SPAN style="mso-list: " Ignore>一、<SPAN 7pt &#39;Times New Roman&#39;>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN></SPAN></SPAN><SPAN 173%; mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; FONT-FAMILY: 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>解释一下智能客户端技术</SPAN><SPAN lang=EN-US 173%; mso-bidi-font-size: 10.5pt><?XML:NAMESPACE PREFIX = O /><O:P></O:P></SPAN></H3>
<P class=MsoNormal><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt><SPAN style="mso-spacerun: " yes>&nbsp;&nbsp;&nbsp; </SPAN></SPAN><SPAN style="FONT-FAMILY: " mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>智能客户端</SPAN><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt>(Smart Client)</SPAN><SPAN style="FONT-FAMILY: " mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>，结合了瘦客户端</SPAN><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt>(B/S</SPAN><SPAN style="FONT-FAMILY: " mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>模式</SPAN><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt>)</SPAN><SPAN style="FONT-FAMILY: " mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>和胖客户端</SPAN><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt>(C/S</SPAN><SPAN style="FONT-FAMILY: " mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>模式</SPAN><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt>)</SPAN><SPAN style="FONT-FAMILY: " mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>的长处，是下一代的客户端软件技术。</SPAN><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt><O:P></O:P></SPAN> </P>
<P class=MsoNormal><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt><SPAN style="mso-spacerun: " yes>&nbsp;&nbsp;&nbsp; </SPAN></SPAN><SPAN style="FONT-FAMILY: " mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>要了解智能客户端，首先要认识瘦客户端技术和胖客户端技术各自的优缺点。</SPAN><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt><O:P></O:P></SPAN> </P>
<P class=MsoNormal><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt><SPAN style="mso-spacerun: " yes>&nbsp;&nbsp;&nbsp; </SPAN></SPAN><SPAN style="FONT-FAMILY: " mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>对于前者，典型的应用就是使用浏览器，通过输入</SPAN><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt>URL</SPAN><SPAN style="FONT-FAMILY: " mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>远程访问服务端，并向服务端发送命令，获取服务端的资源，然后在客户端的浏览器上显示出来。由于这种技术数据库存放在服务端，客户端应用界面的也是由服务端的文件生成，因此在客户端上占用资源少，对客户端的设备要求不高，只需一个浏览器软件和可用的网络便能开始工作，另外，如果系统需要升级修改，只需要在服务端更新文件，当客户再次访问时，就可以使用新的应用系统了，因而部署和升级重点都放在了服务端，实现起来比较简单。但是，这种</SPAN><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt>B/S</SPAN><SPAN style="FONT-FAMILY: " mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>模式依赖网络，当网络不可用时或出现性能不稳定的情况时就会导致客户端变成“死界面”——既不能将数据发送回服务端进行保存，又不能从服务端获取数据拿到客户端操作，一切的工作将要在网络恢复后才能得以继续。</SPAN><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt><O:P></O:P></SPAN> </P>
<P class=MsoNormal><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt><SPAN style="mso-spacerun: " yes>&nbsp;&nbsp;&nbsp; </SPAN></SPAN><SPAN style="FONT-FAMILY: " mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>对于胖客户端技术，用户在使用这种软件时获得的最大的感官体验就是——它首先有自己独特的应用程序界面，而非通过浏览器，用户甚至还可以根据自己的喜好调整软件的布局，进行丰富的界面元素的设置，这些都是</SPAN><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt>B/S</SPAN><SPAN style="FONT-FAMILY: " mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>模式的瘦客户端技术所不能媲美的。另外，用户还能获得较快的反应速度，程序可以充分利用本地机器的资源，在不使用网络访问远程资源时，本地资源的访问在正常情况下都能得到很快的处理。同样的，胖客户端技术也有着不尽人意的地方——在客户端进行部署时，由于客户端可能出现各种各样的情况，所以需要进行必要的设置，部署起来比较困难，如果对软件的版本进行升级，使用传统的</SPAN><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt>DLL</SPAN><SPAN style="FONT-FAMILY: " mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>技术的那将更是一个大的挑战，因为在</SPAN><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt>.NET</SPAN><SPAN style="FONT-FAMILY: " mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>之前，标准</SPAN><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt>Windows DLL</SPAN><SPAN style="FONT-FAMILY: " mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>或</SPAN><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt>COM</SPAN><SPAN style="FONT-FAMILY: " mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>组件可能出现“</SPAN><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt>DLL Hell</SPAN><SPAN style="FONT-FAMILY: " mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>”——注册和更新软件中的</SPAN><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt>DLL</SPAN><SPAN style="FONT-FAMILY: " mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>时，发现共享的</SPAN><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt>DLL</SPAN><SPAN style="FONT-FAMILY: " mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>被最新版本改写了，并使该机器上的其他软件也因此不能运行。胖客户端有可能需要在客户端实现数据库支持，数据库放在本地有可能导致一些安全问题，因为相对于更重视安全的服务端，客户端相对而言还是比较脆弱的。</SPAN><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt><O:P></O:P></SPAN> </P>
<P class=MsoNormal><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt><SPAN style="mso-spacerun: " yes>&nbsp;&nbsp;&nbsp; </SPAN></SPAN><SPAN style="FONT-FAMILY: " mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>那么智能客户端技术便出现了，除了包括了胖</SPAN><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt>/</SPAN><SPAN style="FONT-FAMILY: " mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>瘦客户端各自的优点外，它还具有如下四个最大的优点——</SPAN><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt><O:P></O:P></SPAN> </P>
<P class=MsoNormal TEXT-INDENT: -21pt; mso-list: l1 lfo1; tab-stops: list 42pt; level2 42.0pt><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt; &#39;Times New Roman&#39; mso-fareast-font-family:><SPAN style="mso-list: " Ignore>1)<SPAN 7pt &#39;Times New Roman&#39;>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN></SPAN></SPAN><SPAN style="FONT-FAMILY: " mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>充分利用终端设备的优势</SPAN><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt> (full PC, PDA, phone</SPAN><SPAN style="FONT-FAMILY: " mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>都可以满足</SPAN><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt>)</SPAN><SPAN style="FONT-FAMILY: " mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>，因为核心部分在服务端（可能</SPAN><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt>Web Service</SPAN><SPAN style="FONT-FAMILY: " mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>），所以终端只需实现表示层和一些简单逻辑；</SPAN><SPAN style="mso-bidi-font-size: " 10.5pt> <SPAN lang=EN-US><O:P></O:P></SPAN></SPAN></P>
<P class=MsoNormal TEXT-INDENT: -21pt; mso-list: l1 lfo1; tab-stops: list 42pt; level2 42.0pt><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt; &#39;Times New Roman&#39; mso-fareast-font-family:><SPAN style="mso-list: " Ignore>2)<SPAN 7pt &#39;Times New Roman&#39;>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN></SPAN></SPAN><SPAN style="FONT-FAMILY: " mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>能够调用</SPAN><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt> web services</SPAN><SPAN style="FONT-FAMILY: " mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>，在</SPAN><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt>server</SPAN><SPAN style="FONT-FAMILY: " mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>端用</SPAN><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt>web</SPAN><SPAN style="FONT-FAMILY: " mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>服务实现业务逻辑，处理各种请求，需要说明的是，由于业务逻辑实现放在客户端，因此一方面为客户端瘦身，另一方面也加强了软件的隐蔽性和安全性；</SPAN><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt><O:P></O:P></SPAN> </P>
<P class=MsoNormal TEXT-INDENT: -21pt; mso-list: l1 lfo1; tab-stops: list 42pt; level2 42.0pt><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt; &#39;Times New Roman&#39; mso-fareast-font-family:><SPAN style="mso-list: " Ignore>3)<SPAN 7pt &#39;Times New Roman&#39;>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN></SPAN></SPAN><SPAN style="FONT-FAMILY: " mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>支持在线和离线两种状态，用户可以在网络不可用时继续工作，并将数据临时存放在本地，当网络再次可用，数据便可传上服务器；</SPAN><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt><O:P></O:P></SPAN> </P>
<P class=MsoNormal TEXT-INDENT: -21pt; mso-list: l1 lfo1; tab-stops: list 42pt; level2 42.0pt><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt; &#39;Times New Roman&#39; mso-fareast-font-family:><SPAN style="mso-list: " Ignore>4)<SPAN 7pt &#39;Times New Roman&#39;>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN></SPAN></SPAN><SPAN style="FONT-FAMILY: " mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>能够如同</SPAN><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt>Web</SPAN><SPAN style="FONT-FAMILY: " mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>应用程序一般简单方便的部署，</SPAN><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt>.NET</SPAN><SPAN style="FONT-FAMILY: " mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>使用程序集技术，同一软件的不同版本可以共存于统一客户端。版本的升级也非常简单，软件访问服务端，能自动检测版本号，从而更新关键组件，实现升级。</SPAN><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt><O:P></O:P></SPAN> </P>
<P class=MsoNormal 63pt><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt><O:P>&nbsp;</O:P></SPAN> </P>
<H3 21pt; TEXT-INDENT: -21pt; mso-list: l1 level1 lfo1; tab-stops: list 21.0pt><SPAN lang=EN-US 173%; mso-bidi-font-size: 10.5pt; mso-bidi-font-family: 宋体><SPAN style="mso-list: " Ignore>二、<SPAN 7pt &#39;Times New Roman&#39;>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN></SPAN></SPAN><SPAN style="FONT-FAMILY: " &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>这种技术用途是什么？前景如何？</SPAN><SPAN lang=EN-US 173%; mso-bidi-font-size: 10.5pt><O:P></O:P></SPAN></H3>
<P class=MsoNormal><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt><SPAN style="mso-spacerun: " yes>&nbsp;&nbsp;&nbsp; </SPAN></SPAN><SPAN style="FONT-FAMILY: " mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>其实</SPAN><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt>Smart Client</SPAN><SPAN style="FONT-FAMILY: " mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>的观点在一些传统的软件技术中也可以看到一些影子，随后</SPAN><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt>.NET</SPAN><SPAN style="FONT-FAMILY: " mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>的出现，才使这种技术的各个环节（客户端显示，数据连接，在线离线的操作和部署）得以无缝的实现。所以，这种技术是一种新型的客户端技术的解决方案，是一种技术方法，它可以在各种终端上去实现。</SPAN><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt><O:P></O:P></SPAN> </P>
<P class=MsoNormal><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt><SPAN style="mso-spacerun: " yes>&nbsp;&nbsp;&nbsp; </SPAN></SPAN><SPAN style="FONT-FAMILY: " mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>将桌面级的软件做成智能客户端软件，可以增强其功能，因为网络无限，跳出桌面，就能获得更多的信息。</SPAN><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt><O:P></O:P></SPAN> </P>
<P class=MsoNormal 21.75pt><SPAN style="FONT-FAMILY: " mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>基于</SPAN><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt>Internet</SPAN><SPAN style="FONT-FAMILY: " mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>或</SPAN><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt>intranet</SPAN><SPAN style="FONT-FAMILY: " mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>及浏览器的</SPAN><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt>B/S</SPAN><SPAN style="FONT-FAMILY: " mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>模式的系统，将其实现成智能客户端软件，可以扩大其工作范围，不用再依赖网络，还能充分利用本地资源，加快工作效率。易于部署的优势在企业级应用中，更有发挥的余地，开发人员只需简单的在服务端发布和部署，就能使客户端同步更新。</SPAN><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt><O:P></O:P></SPAN> </P>
<P class=MsoNormal 21.75pt><SPAN style="FONT-FAMILY: " mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>举些可以使用这种技术的应用——</SPAN><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt><O:P></O:P></SPAN> </P>
<P class=MsoNormal 21.75pt><SPAN style="FONT-FAMILY: " mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>“产品售后服务系统”：产品售后服务人员允许以脱机的形式在本地创建送修工单、装箱单等，这样可以加快本地的工作效率，当网络可用时，再将这些数据传上服务器。并可从服务器获得需要的信息。如果本地软件的版本低于服务端的最高版本，将提示用户进行在线无缝地升级，大大减轻了开发人员的部署指导工作。</SPAN><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt><O:P></O:P></SPAN> </P>
<P class=MsoNormal><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt><SPAN style="mso-spacerun: " yes>&nbsp;&nbsp;&nbsp; </SPAN></SPAN><SPAN style="FONT-FAMILY: " mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>随着</SPAN><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt>.NET</SPAN><SPAN style="FONT-FAMILY: " mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>技术的进一步成熟，尤其是</SPAN><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt>Web Service</SPAN><SPAN style="FONT-FAMILY: " mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>技术的更广泛应用，乃至微软将来的系统全面支持</SPAN><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt>.NET</SPAN><SPAN style="FONT-FAMILY: " mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>，我相信智能客户端技术将会成为首选的解决方案，应用到各种软件技术中。</SPAN><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt><O:P></O:P></SPAN> </P>
<P class=MsoNormal><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt><O:P>&nbsp;</O:P></SPAN> </P>
<H3 21pt; TEXT-INDENT: -21pt; mso-list: l1 level1 lfo1; tab-stops: list 21.0pt><SPAN lang=EN-US 173%; mso-bidi-font-size: 10.5pt; mso-bidi-font-family: 宋体><SPAN style="mso-list: " Ignore>三、<SPAN 7pt &#39;Times New Roman&#39;>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN></SPAN></SPAN><SPAN 173%; mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; FONT-FAMILY: 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>怎么运用这种技术（通过案例）</SPAN><SPAN lang=EN-US 173%; mso-bidi-font-size: 10.5pt><O:P></O:P></SPAN></H3>
<P class=MsoNormal 21.75pt><SPAN style="FONT-FAMILY: " mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>智能客户端程序一般都具有偶尔性连接的特征，所以我着重讲述偶尔连接的智能客户端应用程序。同时，其与网路的通讯又有四种方法——</SPAN><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt>Enterprise Services</SPAN><SPAN style="FONT-FAMILY: " mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>，</SPAN><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt>.NET remoting</SPAN><SPAN style="FONT-FAMILY: " mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>，</SPAN><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt>Message Queuing(</SPAN><SPAN style="FONT-FAMILY: " mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>消息队列</SPAN><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt>)</SPAN><SPAN style="FONT-FAMILY: " mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>和</SPAN><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt>Web services</SPAN><SPAN style="FONT-FAMILY: " mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>。基于普遍的观点——</SPAN><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt>Web </SPAN><SPAN style="FONT-FAMILY: " mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>服务是生成大多数智能客户端应用程序的最佳方法。故在针对面向服务的方法和面向数据为中心的方法的选择中，我决定选择前者，因此，我重点讲述<B style="mso-bidi-font-weight: " normal>以</B></SPAN><B style="mso-bidi-font-weight: " normal><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt>Web Services</SPAN></B><B style="mso-bidi-font-weight: " normal><SPAN style="FONT-FAMILY: " mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>作为首选通讯方式，面向服务的智能客户端技术</SPAN></B><SPAN style="FONT-FAMILY: " mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>。</SPAN><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt><O:P></O:P></SPAN> </P>
<P class=MsoNormal 21.75pt><SPAN style="FONT-FAMILY: " mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>设计面向服务的智能客户端技术，关键要解决如下几个问题，为使讲述清晰，我将以一个案例作为例子。</SPAN><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt><O:P></O:P></SPAN> </P>
<P class=MsoNormal 21.75pt><SPAN style="FONT-FAMILY: " mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>我们要实现这样一个<B style="mso-bidi-font-weight: " normal>购书软件（姑且命名为</B></SPAN><B style="mso-bidi-font-weight: " normal><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt>BuyBook</SPAN></B><B style="mso-bidi-font-weight: " normal><SPAN style="FONT-FAMILY: " mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>）</SPAN></B><SPAN style="FONT-FAMILY: " mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>，服务端有数据库，包含两张数据表。一张表简单的描述了书籍的价格，这些价格是变动的，管理员可以通过工具对里面的数据进行改动；另外一张表则是书籍的订单，记录着订购者</SPAN><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt>ID</SPAN><SPAN style="FONT-FAMILY: " mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>，订购书的</SPAN><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt>ID</SPAN><SPAN style="FONT-FAMILY: " mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>和订购数量。两表只有书的</SPAN><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt>ID</SPAN><SPAN style="FONT-FAMILY: " mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>作为主外键关联着。服务端还创建了必要功能的</SPAN><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt>web</SPAN><SPAN style="FONT-FAMILY: " mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>服务，以供客户端调用。</SPAN><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt><O:P></O:P></SPAN> </P>
<P class=MsoNormal 21.75pt><SPAN style="FONT-FAMILY: " mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>下面继续接着讲面向服务的</SPAN><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt>Smart Client</SPAN><SPAN style="FONT-FAMILY: " mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>技术关键解决的几个问题。</SPAN><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt><O:P></O:P></SPAN> </P>
<P class=MsoNormal 21.75pt><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt><O:P>&nbsp;</O:P></SPAN> </P>
<P class=MsoNormal TEXT-INDENT: -21pt; mso-list: level1 tab-stops: list 42pt; 42.0pt l0 lfo2;><B style="mso-bidi-font-weight: " normal><SPAN lang=EN-US &#39;Times New Roman&#39; mso-fareast-font-family: 12pt;><SPAN style="mso-list: " Ignore>1)<SPAN 7pt &#39;Times New Roman&#39;>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN></SPAN></SPAN></B><B style="mso-bidi-font-weight: " normal><SPAN &#39;Times New Roman&#39; FONT-FAMILY: 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family: 12pt;>连接的管理</SPAN></B><B style="mso-bidi-font-weight: " normal><SPAN lang=EN-US 12pt><O:P></O:P></SPAN></B> </P>
<P class=MsoNormal 21pt><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt><O:P>&nbsp;</O:P></SPAN> </P>
<P class=MsoNormal 21pt><SPAN style="FONT-FAMILY: " mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>智能客户端软件当然不能过于频繁地访问网络上的服务端，因为这样会严重影响软件的性能，另外，对于连接发生的更改（包含如手动连接、自动连接、连接意外中断和连接长期不用等情况）软件也要作出相应的反应，以体现其智能的特点。</SPAN><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt><O:P></O:P></SPAN> </P>
<P class=MsoNormal 21pt><SPAN style="FONT-FAMILY: " mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>那么关于连接的管理有些什么适合的方案呢，我将以我举的范例为例，设计其在这方面的处理方法。</SPAN><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt><O:P></O:P></SPAN> </P>
<P class=MsoNormal 21pt><B style="mso-bidi-font-weight: " normal><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt>BuyBook</SPAN></B><SPAN style="FONT-FAMILY: " mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>应该尽量避免和网络上的服务端进行交互，即使网络连接可用。可以优先<B style="mso-bidi-font-weight: " normal>假设为离线操作</B>，在机器本地进行事务处理，当然这样会造成一定的问题，有可能系统数据没有和服务端数据同步，导致本地操作无效。如服务端书本的价格作为系统参数保留在本地，当系统的数据变化时，用户在客户端看到的书本的价格就不是真实的行情了。所以虽然优先离线操作，也要考虑到数据出现不一致的情况，在数据冲突处理会得到详细解答。</SPAN><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt><O:P></O:P></SPAN> </P>
<P class=MsoNormal 21pt><SPAN style="FONT-FAMILY: " mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>在一些特殊的例子中，如购买股票的情况，由于股票的行情是不断变化的，所以，为使本地数据能体现真实的情况，网络连接应该采用隔时便来一次与服务端交互的动作，这个“隔时”的时间长度，可以用户自定义，也可以是系统默认。</SPAN><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt><SPAN style="mso-spacerun: " yes>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&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="mso-spacerun: " yes>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&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><O:P></O:P></SPAN> </P>
<P class=MsoNormal 21pt><SPAN style="FONT-FAMILY: " mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>我们还可以提供给用户这么一个功能，他只需点一个按钮，发送出一个访问服务端的命令，这时连接建立，并保持这个连接，直到手动断开，或网络不可用。我们称之为“<B style="mso-bidi-font-weight: " normal>手动连接</B>”，与之相对应的是“<B style="mso-bidi-font-weight: " normal>自动连接</B>”，当连接可用时，保持连接状态，将缓存中的数据处理，发送到服务端，并获取服务端最新的一些共用系统参数。</SPAN><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt><O:P></O:P></SPAN> </P>
<P class=MsoNormal 21pt><SPAN style="FONT-FAMILY: " mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>总而言之，对于连接，在设计系统时要把它看作是奢侈品，优雅的对待网络，无论网络处于什么状态，用户的数据操作都可以放在本地缓存。</SPAN><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt><O:P></O:P></SPAN> </P>
<P class=MsoNormal TEXT-INDENT: 225.75pt; -183.75pt; mso-para-margin-left: 4.0gd; mso-char-indent-count: -17.5><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt><SPAN style="mso-spacerun: " yes>&nbsp;</SPAN><O:P></O:P></SPAN> </P>
<P class=MsoNormal TEXT-INDENT: -21pt; mso-list: level1 tab-stops: list 42pt; 42.0pt l0 lfo2;><B style="mso-bidi-font-weight: " normal><SPAN lang=EN-US &#39;Times New Roman&#39; mso-fareast-font-family: 12pt;><SPAN style="mso-list: " Ignore>2)<SPAN 7pt &#39;Times New Roman&#39;>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN></SPAN></SPAN></B><B style="mso-bidi-font-weight: " normal><SPAN lang=EN-US 12pt>WEB</SPAN></B><B style="mso-bidi-font-weight: " normal><SPAN &#39;Times New Roman&#39; FONT-FAMILY: 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family: 12pt;>服务的交互</SPAN></B><B style="mso-bidi-font-weight: " normal><SPAN lang=EN-US 12pt><O:P></O:P></SPAN></B> </P>
<P class=MsoNormal 21pt><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt><O:P>&nbsp;</O:P></SPAN> </P>
<P class=MsoNormal 21pt><SPAN style="FONT-FAMILY: " mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>面向服务的智能客户端应用程序，通过网络与服务端的交互工作重点就在于</SPAN><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt>web service</SPAN><SPAN style="FONT-FAMILY: " mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>上。按照前面所言，应该减少这种服务端上的远程交互，可以将本地操作缓存，并且在与</SPAN><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt>web</SPAN><SPAN style="FONT-FAMILY: " mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>服务交互过程中，不必等待返回信息，在这延迟中可以进行其他的操作。那么要实现这种延迟不影响工作的功能，最有效的办法就是使用异步通讯的方式，可以考虑使用多线程。</SPAN><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt><O:P></O:P></SPAN> </P>
<P class=MsoNormal 21pt><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt>WEB</SPAN><SPAN style="FONT-FAMILY: " mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>服务使用</SPAN><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt>XML</SPAN><SPAN style="FONT-FAMILY: " mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>技术，</SPAN><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt>CRUD</SPAN><SPAN style="FONT-FAMILY: " mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>（</SPAN><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt>Creat Read Update Delete</SPAN><SPAN style="FONT-FAMILY: " mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>）类型的数据库操作，可以都通过</SPAN><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt>Web Service</SPAN><SPAN style="FONT-FAMILY: " mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>。那么，就要讲究</SPAN><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt>Web Service</SPAN><SPAN style="FONT-FAMILY: " mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>里交互方法的定义了。当客户进行一个</SPAN><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt>Create</SPAN><SPAN style="FONT-FAMILY: " mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>操作时，可能关联到系统参数，例如</SPAN><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt>BuyBook</SPAN><SPAN style="FONT-FAMILY: " mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>应用程序，当客户提交订购单时，</SPAN><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt>Web</SPAN><SPAN style="FONT-FAMILY: " mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>服务应该先检查本地的商品价格与服务端的价格是否一致，如果价格已经不同，应该提示客户更新最新的价格，然后再作订购行为的判断。</SPAN><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt>Upate</SPAN><SPAN style="FONT-FAMILY: " mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>和</SPAN><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt>Delete</SPAN><SPAN style="FONT-FAMILY: " mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>操作，很可能导致数据冲突。例如删除动作，应该在客户端上将相关记录标记为暂时删除，然后在服务器上将删除请求排队。服务端进行删除时一定要检测是否有数据冲突，如果出现冲突，还要进行数据冲突的处理。</SPAN><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt><O:P></O:P></SPAN> </P>
<P class=MsoNormal><B style="mso-bidi-font-weight: " normal><SPAN lang=EN-US 12pt><O:P>&nbsp;</O:P></SPAN></B> </P>
<P class=MsoNormal TEXT-INDENT: -21pt; mso-list: level1 tab-stops: list 42pt; 42.0pt l0 lfo2;><B style="mso-bidi-font-weight: " normal><SPAN lang=EN-US &#39;Times New Roman&#39; mso-fareast-font-family: 12pt;><SPAN style="mso-list: " Ignore>3)<SPAN 7pt &#39;Times New Roman&#39;>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN></SPAN></SPAN></B><B style="mso-bidi-font-weight: " normal><SPAN &#39;Times New Roman&#39; FONT-FAMILY: 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family: 12pt;>本地数据缓存</SPAN></B><B style="mso-bidi-font-weight: " normal><SPAN lang=EN-US 12pt><O:P></O:P></SPAN></B> </P>
<P class=MsoNormal 21pt><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt><O:P>&nbsp;</O:P></SPAN> </P>
<P class=MsoNormal 21pt><SPAN style="FONT-FAMILY: " mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>智能客户端为了能及时地响应用户的操作，同时也是为了满足脱机离线工作的需要，就必须将常用的固定的服务端数据缓存到本地。</SPAN><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt><O:P></O:P></SPAN> </P>
<P class=MsoNormal 21pt><SPAN style="FONT-FAMILY: " mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>如果连接是处于在线状态的，本地数据可以暂时保存在内存中，</SPAN><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt>ADO.NET</SPAN><SPAN style="FONT-FAMILY: " mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>里的</SPAN><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt>DataSet</SPAN><SPAN style="FONT-FAMILY: " mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>本身就是可以用内存临时存储的数据对象，数据在内存中存储只是一个临时过渡，当数据需要经过操作后保存回远程数据库时，方法可以使用</SPAN><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt>DataSet</SPAN><SPAN style="FONT-FAMILY: " mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>的数据适配器</SPAN><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt>DataAdapter</SPAN><SPAN style="FONT-FAMILY: " mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>将变化的量返回数据库操作，这样既加快了本地的反应又节省了带宽。</SPAN><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt><O:P></O:P></SPAN> </P>
<P class=MsoNormal 21pt><SPAN style="FONT-FAMILY: " mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>如果，应用程序在断线的状态下工作，则将数据保存回本地的数据存储结构，待再次连线时，装载进</SPAN><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt>DataSet</SPAN><SPAN style="FONT-FAMILY: " mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>，进行处理。</SPAN><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt><O:P></O:P></SPAN> </P>
<P class=MsoNormal><B style="mso-bidi-font-weight: " normal><SPAN lang=EN-US 12pt><O:P>&nbsp;</O:P></SPAN></B> </P>
<P class=MsoNormal TEXT-INDENT: -21pt; mso-list: level1 tab-stops: list 42pt; 42.0pt l0 lfo2;><B style="mso-bidi-font-weight: " normal><SPAN lang=EN-US &#39;Times New Roman&#39; mso-fareast-font-family: 12pt;><SPAN style="mso-list: " Ignore>4)<SPAN 7pt &#39;Times New Roman&#39;>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN></SPAN></SPAN></B><B style="mso-bidi-font-weight: " normal><SPAN &#39;Times New Roman&#39; FONT-FAMILY: 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family: 12pt;>数据冲突处理</SPAN></B><B style="mso-bidi-font-weight: " normal><SPAN lang=EN-US 12pt><O:P></O:P></SPAN></B> </P>
<P class=MsoNormal 21.75pt><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt><O:P>&nbsp;</O:P></SPAN> </P>
<P class=MsoNormal 21.75pt><SPAN style="FONT-FAMILY: " mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>造成数据冲突最常见的原因包括用户在执行更新或删除数据的操作时，有可能该数据已经被删除掉了，这样应用程序找不到更新或删除的项，自然就会引发异常而出错。处理这样的情况，可以采用一个简单的办法，数据适配器</SPAN><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt>DataAdapter</SPAN><SPAN style="FONT-FAMILY: " mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>能对</SPAN><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt>DataSet</SPAN><SPAN style="FONT-FAMILY: " mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>变化进行判断——当数据往数据库返回时，</SPAN><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt>DataAdapter</SPAN><SPAN style="FONT-FAMILY: " mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>的</SPAN><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt>Update</SPAN><SPAN style="FONT-FAMILY: " mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>方法可以检查</SPAN><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt>DataSet</SPAN><SPAN style="FONT-FAMILY: " mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>里每个</SPAN><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt>DataRow</SPAN><SPAN style="FONT-FAMILY: " mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>的</SPAN><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt>RowState</SPAN><SPAN style="FONT-FAMILY: " mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>，从而可以判断该</SPAN><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt>DataRow</SPAN><SPAN style="FONT-FAMILY: " mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>是最新的，还是已修改或已经删除的，然后执行适合的数据库操作，像找不到数据的那种情况，</SPAN><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt>DataAdapter</SPAN><SPAN style="FONT-FAMILY: " mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>将得知数据库受影响的行数不大于零，并产生异常，从而停止更新。然后，应用程序就可以通过这个异常来处理数据冲突。</SPAN><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt><O:P></O:P></SPAN> </P>
<P class=MsoNormal 21.75pt><SPAN style="FONT-FAMILY: " mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>处理办法是，如果服务端已经不存在用户需要删除的原始数据，那么，只要将客户端的该条数据删除。如果用户是对该数据进行更改，可以先检测服务端的数据，看是否存在，倘若不存在，则通知用户，并视用户的操作无效，同时将本地的那条过期数据删除。</SPAN><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt><O:P></O:P></SPAN> </P>
<P class=MsoNormal><SPAN lang=EN-US style="mso-bidi-font-size: " 10.5pt><O:P>&nbsp;</O:P></SPAN> </P>
<H3 21pt; TEXT-INDENT: -21pt; mso-list: l1 level1 lfo1; tab-stops: list 21.0pt><SPAN lang=EN-US 173%; mso-bidi-font-size: 10.5pt; mso-bidi-font-family: 宋体><SPAN style="mso-list: " Ignore>四、<SPAN 7pt &#39;Times New Roman&#39;>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN></SPAN></SPAN><SPAN 173%; mso-bidi-font-size: 10.5pt; &#39;Times New Roman&#39; FONT-FAMILY: 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>总结</SPAN><SPAN lang=EN-US 173%; mso-bidi-font-size: 10.5pt><O:P></O:P></SPAN></H3>
<P class=MsoNormal 21pt><SPAN style="FONT-FAMILY: " &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>智能客户端技术是颇有前途的下一代客户端技术，它能够在有网络连接和网络断开的情况下灵活地工作。对用户而言，这将是新的一种软件使用体验，能同时拥有</SPAN><SPAN lang=EN-US>C/S</SPAN><SPAN style="FONT-FAMILY: " &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>模式软件快速的反应、丰富的用户界面体验和瘦客户端模式那样简单的部署，升级。对开发者而言，开发的难度变大了，考虑的方面多了，但还是有灵活的方案可供选择，还可以结合</SPAN><SPAN lang=EN-US>.NET</SPAN><SPAN style="FONT-FAMILY: " &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>技术，使用面向服务或面向数据的解决方案，在开发中，要着重解决“连接的管理”，“</SPAN><SPAN lang=EN-US>WEB</SPAN><SPAN style="FONT-FAMILY: " &#39;Times New Roman&#39; 宋体; mso-ascii-font-family: Roman&#39;; mso-hansi-font-family:>服务的交互”，“本地数据缓存”和“数据冲突处理”的技术点。</SPAN></P></SPAN><img src ="http://www.blogjava.net/TrampEagle/aggbug/30137.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/TrampEagle/" target="_blank">TrampEagle</a> 2006-02-10 14:46 <a href="http://www.blogjava.net/TrampEagle/articles/30137.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>brian的Perl问题之万能指南</title><link>http://www.blogjava.net/TrampEagle/articles/28868.html</link><dc:creator>TrampEagle</dc:creator><author>TrampEagle</author><pubDate>Sat, 21 Jan 2006 04:39:00 GMT</pubDate><guid>http://www.blogjava.net/TrampEagle/articles/28868.html</guid><wfw:comment>http://www.blogjava.net/TrampEagle/comments/28868.html</wfw:comment><comments>http://www.blogjava.net/TrampEagle/articles/28868.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/TrampEagle/comments/commentRss/28868.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/TrampEagle/services/trackbacks/28868.html</trackback:ping><description><![CDATA[<P>引自：<A href="http://wiki.perlchina.org/main/show/brian's%20Guide%20to%20Solving%20Any%20Perl%20Problem">http://wiki.perlchina.org/main/show/brian's%20Guide%20to%20Solving%20Any%20Perl%20Problem</A><BR><BR></P>
<DIV id=revision>
<P>原 名：Brian’s Guide to Solving Any Perl Problem<BR>中 文: Brian 的 Perl 问题之万能指南 <BR>作 者：brian d foy<BR>原 文：<A href="http://www.panix.com/~comdog/brian's_guide.html"><FONT color=#0066cc>http://www.panix.com/~comdog/brian's_guide.html</FONT></A><BR>发 表：2004 七 月 20 日 <BR>翻 译：<A class=existingWikiWord href="http://wiki.perlchina.org/main/show/klaus"><FONT color=#0066cc>klaus</FONT></A><BR>审 校：<A class=existingWikiWord href="http://wiki.perlchina.org/main/show/qiang"><FONT color=#0066cc>qiang</FONT></A><BR>出 处：中国 Perl协会 <SPAN class=caps>FPC </SPAN>- <A href="http://perlchina.org/"><FONT color=#0066cc>PerlChina.org</FONT></A></P>
<P>标题 <STRONG>brian的Perl问题之万能指南</STRONG></P>
<P>纲要 <STRONG>阅读这份指南并保持明智的头脑</STRONG></P>
<P>描述 <STRONG>我的调试哲学</STRONG><BR>我相信三件事情：</P>
<P><STRONG>这不是个人问题</STRONG></P>
<P>别老想着是你的代码。你可能觉得自己是个艺术家，但实际上就算是经验丰富的大师也会写出很多垃圾。每个人的代码都是垃圾，我的也是你的也是。要学着去喜欢它。当你碰到问题的时候，你应该想：“噢，我写的垃圾代码出了点问题。”这说明你不再去责怪 Perl。不应该变成个人性的问题。</P>
<P>忘记你以前怎么做的。如果不是你做事的方法有点问题，你也不会来读这个。这并不是坏事，只是到了该有点长进的时间。我们都经历过的。</P>
<P><STRONG>个人责任感</STRONG></P>
<P>如果你的代码出了问题那仅仅是——你的问题。你应该尽最大的力量自己解决。记住，每个人都有自己的代码，每个人都有自己的问题。自己的作业自己做，在麻烦别人之前先尽自己最大的努力。如果你老老实实地按照这个指南做了所有能做的事之后，依然不能解决问题，那么你已经尽力了，应该找别人来看看。</P>
<P><STRONG>改变你做事的方法</STRONG></P>
<P>改正之后不要再犯同样的错误。很可能是你写代码的方法错了，而不是你写的代码错了。改变你以前做事的方法，让生活更容易些。不要指望Perl来习惯你，因为这是不可能的。你要习惯 Perl。它只是种语言，而不是种生活方式。</P>
<P>我的方法： <STRONG>你用strictures编译代码吗？</STRONG></P>
<P>如果你不用 strictures，请把它打开。Perl 高手们之所以是高手，因为他们使用严格模式，使得他们有更多的时间解决其他问题，学新的东西，以及上载模块倒 <SPAN class=caps>CPAN</SPAN>。</P>
<P>你可以使用 strict pragma 在代码中打开 strictures<BR><PRE>use strict;
</PRE><BR>也可以用perl的 -M开关在命令行中打开它：<BR><PRE>perl -Mstrict script.pl
</PRE><BR>你可能会被它搞怒，但是坚持用上它几个礼拜之后，你会写出更好的代码，花更少的时间来检查低级错误，而且可能就不再需要读这个指南了。 
<P></P>
<P><STRONG>什么是warning?</STRONG></P>
<P>Perl 会对一些有问题的结构给你警告提示。把 warning 打开，让 Perl 来帮你。<BR>你可以在第一行用 perl的 -w 开关打开它：<BR><PRE>#!/usr/bin/perl -w
</PRE><BR>也可以在命令行打开warning：<BR><PRE>perl -w script.pl
</PRE><BR>你也可以使用词汇警告，它带有许多有趣的特性。更多的信息参看warnings 的帮助文档。<BR><PRE>use warnings;
</PRE><BR>如果你不明白某个警告的意思，你可以使用 warning 的详细模式，或者在你的代码中使用诊断 pragma：<BR><PRE>use diagnostics;
</PRE>
<P></P>
<P><STRONG>解决第一个问题先！</STRONG></P>
<P>你从 Perl 中得到警告或者错误信息之后，先解决第一个，然后看Perl是否依旧报出其他错误。因为后续的错误很可能是由于第一个错误衍生而来的。</P>
<P><STRONG>检查错误信息行号之前的代码！</STRONG></P>
<P>Perl 在错误已经发生的时候才报错，而不是在此之前。因此当 Perl 报出行号时错误已经发生了，而出错的地方是在这之前。看看错误行号之前的代码和表达式是否有问题。</P>
<P><STRONG>那个变量值是你想的那样吗？</STRONG></P>
<P>不要乱猜！在表达式中使用某个值的时候先检查它是否正确。世界上最好的调试器就是 print。<BR><PRE>print STDERR "The value is [$value]\n";
</PRE><BR>我用括号括住 $value 的原因是因为这样我可以看见开头和尾巴上是否有空格或者换行。 
<P></P>
<P>如果这个值不是标量，那么我使用 Data::Dumper 来打印这些数据结构。<BR><PRE>require Data::Dumper;
print STDERR "The hash is ", Data::Dumper::Dumper( %hash ), "\n";
</PRE>
<P></P>
<P>如果打印出来的结果不是你所期望的，那么移到前面几句，再来！找到这个值最后正确的位置。也可以用 perl -d 开关打开内建的 Perl 调试器。更多信息请参考perldebug。<BR><PRE>perl -d script.pl
</PRE><BR>你也可以使用其他调试器或者开发环境，想 ptkdb（一个基于Tk的图形调试器）或者是 Komodo（ <A class=existingWikiWord href="http://wiki.perlchina.org/main/show/ActiveStates"><FONT color=#0066cc>Active States</FONT></A> 基于 Mozilla 的 Perl <SPAN class=caps>IDE</SPAN>） 
<P></P>
<P><STRONG>你用的函数是正确的吗？</STRONG></P>
<P>我写 perl 程序的时间已经不短了，可我还是几乎每天都要查 perlfunc。有些东西我就是吃不准，而有时候我太缺乏睡眠了以至于没了常识，然后总搞不懂为什么 sprintf() 不打印到屏幕上。</P>
<P>你可以用perldoc命令和它的-f开关来查询某个特定的函数。<BR><PRE>perldoc -f function_name
</PRE>
<P></P>
<P>如果你在使用一个模块，查询它的文档，看看你是不是在用正确的方式使用它。你可以用 perldoc 查询它的文档。<BR><PRE>perldoc Module::Name
</PRE>
<P></P>
<P><STRONG>你用的特殊变量是正确的吗？</STRONG></P>
<P>同样，我经常去查 perlvar。不过，当我发现 Perl 快速参考这本书（The Perl Pocket Reference）更加方便之后，我就很少查perlvar了。</P>
<P><STRONG>你用模块版本正确吗？</STRONG></P>
<P>有些模块在升级版本的时候会有不少改变。你知道你用的模块是什么版本吗？你可以用一个一行的 perl 语句检查你的模块版本：<BR><PRE>perl -MModule::Name -le 'print Module::Name-&gt;VERSION';</PRE><BR>如果你读的文档不是你机器上的本地文档，而是像 http://www.perldoc.com或者<A href="http://search.cpan.org/"><FONT color=#0066cc>http://search.cpan.org</FONT></A> 上的，那你就比较有可能碰到文档版本差异的问题。 
<P></P>
<P><STRONG>你用小脚本测试过了吗？</STRONG></P>
<P>如果你在尝试新的东西，或者觉得某一小段代码很奇怪，你可以写一个最短的程序运行一下这一个片断。这个方法把所有其他的因素都排除在外。如果测试没有问题，那说明问题可能不在这段代码里面。如果测试结果不对，那你大概就找到了你的问题所在。</P>
<P><STRONG>你检查环境了吗？</STRONG></P>
<P>有些东西是依赖环境变量的。你确定你的环境变量都是对的吗？程序运行的时候用到的环境变量是你现在看到的环境变量吗？记住有些 <SPAN class=caps>CGI </SPAN>程序或cron 可能用到的环境变量和 shell 里的不一样，尤其是在不同的机器上的时候。</P>
<P>Perl 讲环境变量存储在 %ENV 里 。如果你需要某个环境变量，就算是在测试的时候，也记住先提供一个默认值，如果它原来不存在的话。</P>
<P>如果还有问题，查看你的环境。<BR><PRE>require Data::Dumper;
print STDERR Data::Dumper::Dumper( \%ENV );
</PRE>
<P></P>
<P><STRONG>你试过Google了吗？</STRONG></P>
<P>其他人也许碰到过和你同样的问题。用 Google Groups（http://groups.google.com）搜索看看是不是有人在 comp.lang.perl.misc 上发过类似帖子, 没准还能发现其他人给出的解决方法。在新闻组里问问题的人和回答问题的人的差别在于，他们使用Google Groups 的能力高底不同。</P>
<P><STRONG>你对程序做过性能测试吗？</STRONG></P>
<P>如果你想知道是哪些部分让你的程序变慢，试过性能测试吗？可以让Devel::<SPAN class=newWikiWord>Small Prof<A href="http://wiki.perlchina.org/main/show/SmallProf"><FONT color=#0066cc>?</FONT></A></SPAN> 帮你做这件事。它可以计算 perl 执行每一行代码的次数和花费的时间，然后打印一份漂亮的报告。</P>
<P><STRONG>到底是那个测试没通过？</STRONG></P>
<P>如果你有一套测试，到底是哪个测试失败了呢？你可以很容易的找到错误所在，因为每个小测试只执行一小段代码。</P>
<P>如果你没有，为什么不写一个呢？如果你的代码很短很短，或者只是一个一次性的程序，那我不会建议你专门写一套测试出来。但如果不是这样的情况，那写一些测试代码是很有帮助的。Test::Harness让这件事变得太容易了，以至于你都找不到理由不做。如果你说你没时间，那大概是因为你不用测试而在脚本除错上浪费了太多时间。</P>
<P><STRONG>你和小熊说话了吗？</STRONG></P>
<P>把你的问题大声说出来。把它变成语言。有几年我很愉快地和一个很优秀的程序员一起工作，他几乎能解决任何问题。当我被什么问题堵住的时候，我总去请教他，跟他解释我的问题。几乎每次都是这样的情况：我说不到第三句，就停下来，说：“噢我明白了，没问题了。”他每次也都是这样。</P>
<P>你可能需要做太多次这样的事情，所以我推荐拿一个长毛绒玩具做为你的Perl 临床诊断家，这样你就不会惹怒你的同事了。我的桌子旁边就有一只小熊，我每次都把我的问题解释给他听。每次当我自言自语的时候，我女朋友跟本都不会注意，她习惯了。</P>
<P><STRONG>这问题在纸上看起来有点不一样了吗？</STRONG></P>
<P>因为你老是看着电脑屏幕，所以说不定一种新的媒介可以让你从新的角度看这个问题。把程序打印到纸上试试看。</P>
<P><STRONG>你看 Jon Stewart 的节目吗？</STRONG></P>
<P>说真的。可能你不太喜欢 Jon Stewart，那换一个其他的。休息一下，停一会，让你的大脑放松放松。当你回来的时候说不定问题忽然就很容易解决了。</P>
<P><STRONG>你认真检查自己了吗？</STRONG></P>
<P>如果到了这一步你还没有解决的话，这说不定是个心理问题。可能你对某段代码有特别的感情，所以不想改掉它。说不定你觉得只有你是对的，别人都错了。当你有这种感觉的时候，你该考虑一下问题的来源—你自己。不要过于自负而不愿认清自己的错误。</P>
<P>作者<BR>brian d foy, <BDFOY@CPAN.ORG></P>
<P><SPAN class=caps>COPYRIGHT</SPAN><BR>Copyright 2002, Perl Documentation Project, All Rights Reserved</P></DIV><img src ="http://www.blogjava.net/TrampEagle/aggbug/28868.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/TrampEagle/" target="_blank">TrampEagle</a> 2006-01-21 12:39 <a href="http://www.blogjava.net/TrampEagle/articles/28868.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>用面向方面的编程方式分离软件关注点</title><link>http://www.blogjava.net/TrampEagle/articles/27702.html</link><dc:creator>TrampEagle</dc:creator><author>TrampEagle</author><pubDate>Thu, 12 Jan 2006 03:51:00 GMT</pubDate><guid>http://www.blogjava.net/TrampEagle/articles/27702.html</guid><wfw:comment>http://www.blogjava.net/TrampEagle/comments/27702.html</wfw:comment><comments>http://www.blogjava.net/TrampEagle/articles/27702.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/TrampEagle/comments/commentRss/27702.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/TrampEagle/services/trackbacks/27702.html</trackback:ping><description><![CDATA[<P>&nbsp; 引自：<A href="http://spring.jactiongroup.net/viewtopic.php?t=67">http://spring.jactiongroup.net/viewtopic.php?t=67</A><BR><SPAN class=postbody><FONT size=2>原文:http://www.javaworld.com/javaworld/jw-01-2002/jw-0118-aspect.html <BR><BR><SPAN style="COLOR: blue"><SPAN style="FONT-WEIGHT: bold">了解AOP（第一部分）--用面向方面的编程方式分离软件关注点</SPAN></SPAN> </FONT><BR></SPAN><BR><FONT size=2><BR><SPAN style="FONT-WEIGHT: bold">摘要</SPAN> <BR><BR>多数软件系统都包含几个跨越多个模块的关注点。用面向对象技术实现这些关注点会使系统难以实现，难以理解，并且不利于软件的演进。新的AOP（面向角度的编程方法）利用模块化来分离软件中横切多模块的关注点。使用AOP，你可以建立容易设计，易于理解和维护的系统。此外，AOP可以带来更高的产出，更好的质量，更好的扩展性，这篇文章是这个系列里三篇文章中的第一章，介绍AOP的概念和它所解决的问题。 <BR><BR><SPAN style="FONT-STYLE: italic">作者：Ramnivas Laddad </SPAN><BR><BR>一个关注点就是一个特定的目的、一块我们感兴趣的的区域。从技术的角度来说，一个典型的软件系统包含一些核心的关注点和系统级的关注点。举个例子来说，一个信用卡处理系统的核心关注点是借贷/存入处理，而系统级的关注点则是日志，事务完整性，授权，安全性及性能问题等，许多关注点——我们叫它横切关注点——会在多个模块中出现，使用现有的编程方法，横切关注点会横越多个模块，结果是使系统难以设计、理解、实现和演进。 <BR><BR>AOP（面向角度的编程方式）能够比上述方法更好的分离系统关注点，从而提供模块化的横切关注点。 <BR><BR>在这篇文章里——关于AOP的三篇文章的第一章，我首先会 解释横切关注点在一些即使是中等复杂度的软件系统中也会引起的问题，接着我会介绍AOP的核心概念并演示AOP是怎样解决横切关注点问题的。 <BR><BR><SPAN style="FONT-WEIGHT: bold">软件编程方法的演进</SPAN> <BR><BR>在计算机科学的早期阶段，开发人员使用直接的机器级代码来编程，不幸的是，程序员得花费更多时间来考虑一种特定机器的指令集而不是手中需要解决的问题本身。慢慢的我们转而使用允许对底层机器做某种抽象的高级语言。然后是结构化语言，我们可以把问题分解成一些必要的过程来完成任务。但是，随着复杂程度的增加，我们又需要更适合的技术。面向对象的编程方式（OOP）使我们可以把系统看作是一批相互合作的对象。类允许我们把实现细节隐藏在接口下。多态性为相关概念提供公共的行为和接口，并允许特定的组件在无需访问基础实现的前提下改变特定行为。 <BR><BR>编程方法和语言决定了我们和计算机交流的方式。每一种新的方法学都提出一种新的分解问题的方法：机器码、伪代码、过程和类等。每种新的方法学都使得从系统需求到编程概念的映射更加自然。编程方法学的发展让我们可以建立更加复杂的系统，这句话反过来说也对，我们能够建立更加复杂的系统是因为这些技术允许我们处理这种复杂度。 <BR><BR>现在，大多数软件项目都选择OOP的编程方式。确实，OOP已经表明了它处理一般行为的能力，但是，我们一会儿会看到（或许你已经感觉到了），OOP不能很好的处理横越多个——经常是不相关的——模块的行为，相比之下，AOP填补了这个空白，它很可能会是编程方法学发展的下一个里程碑。 <BR><BR><SPAN style="FONT-WEIGHT: bold">把系统看作一批关注点</SPAN> <BR><BR>我们可以把一个复杂的系统看作是由多个关注点来组合实现的，一个典型的系统可能会包括几个方面的关注点，如业务逻辑，性能，数据存储，日志和调试信息，授权，安全，线程，错误检查等，还有开发过程中的关注点，如易懂，易维护，易追查，易扩展等，图一演示了由不同模块实现的一批关注点组成了一个系统。 <BR><BR><IMG src="http://www.javaworld.com/javaworld/jw-01-2002/images/jw-0118-aspectf1.gif" border=0> <BR><BR>图 1. 把模块作为一批关注点来实现 <BR><BR>　 <BR>图二把需求比作一束穿过三棱镜的光，我们让需求之光通过关注点鉴别三棱镜，就会区别出每个关注点，同样的方法也适用于开发阶段的关注点。 <BR><BR><IMG src="http://www.javaworld.com/javaworld/jw-01-2002/images/jw-0118-aspectf2.gif" border=0> <BR>图 2. 关注点分解: 三棱镜法则 <BR><BR>开发人员建立一个系统以满足多个需求，我们可以大致的把这些需求分类为核心模块级需求和系统级需求。很多系统级需求一般来说是相互独立的，但它们一般都会横切许多核心模块。举个例子来说，一个典型的企业应用包含许多横切关注点，如验证，日志，资源池，系统管理，性能及存储管理等，每一个关注点都牵涉到几个子系统，如存储管理关注点会影响到所有的有状态业务对象。 <BR><BR>让我们来看一个简单，但是具体的例子，考虑一个封装了业务逻辑的类的实现框架： <BR><BR>
<TABLE cellSpacing=1 cellPadding=3 width="90%" align=center border=0>
<TBODY>
<TR>
<TD><SPAN class=genmed><B><FONT size=2>代码:</FONT></B></SPAN></TD></TR>
<TR>
<TD class=code>public class SomeBusinessClass extends OtherBusinessClass { <BR>&nbsp; &nbsp; // 核心数据成员 <BR><BR>&nbsp; &nbsp; // 其它数据成员：日志流，保证数据完整性的标志位等 <BR><BR>&nbsp; &nbsp; // 重载基类的方法 <BR><BR>&nbsp; &nbsp; public void performSomeOperation(OperationInformation info) { <BR>&nbsp; &nbsp; &nbsp; &nbsp; // 安全性验证 <BR><BR>&nbsp; &nbsp; &nbsp; &nbsp; // 检查传入数据是否满足协议 <BR><BR>&nbsp; &nbsp; &nbsp; &nbsp; // 锁定对象以保证当其他线程访问时的数据完整性 <BR><BR>&nbsp; &nbsp; &nbsp; &nbsp; // 检查缓存中是否为最新信息 <BR><BR>&nbsp; &nbsp; &nbsp; &nbsp; // 纪录操作开始执行时间 <BR><BR>&nbsp; &nbsp; &nbsp; &nbsp; // 执行核心操作 <BR><BR>&nbsp; &nbsp; &nbsp; &nbsp; // 纪录操作完成时间 <BR><BR>&nbsp; &nbsp; &nbsp; &nbsp; // 给对象解锁 <BR>&nbsp; &nbsp; } <BR><BR>&nbsp; &nbsp; // 一些类似操作 <BR><BR>&nbsp; &nbsp; public void save(PersitanceStorage ps) { <BR>&nbsp; &nbsp; } <BR><BR>&nbsp; &nbsp; public void load(PersitanceStorage ps) { <BR>&nbsp; &nbsp; } <BR>}</TD></TR></TBODY></TABLE><SPAN class=postbody><BR><BR>在上面的代码中，我们注意到三个问题，首先，其它数据成员不是这个类的核心关注点，第二，performSomeOperation()的实现做了许多核心操作之外的事，它要处理日志，验证，线程安全，协议验证和缓存管理等一些外围操作，而且这些外围操作同样也会应用于其他类，第三，save()和load()执行的持久化操作是否构成这个类的核心清楚的。 <BR><BR><SPAN style="FONT-WEIGHT: bold">横切关注点的问题</SPAN> <BR><BR>虽然横切关注点会跨越多个模块，但当前的技术倾向于使用一维的方法学来处理这种需求，把对应需求的实现强行限制在一维的空间里。这个一维空间就是核心模块级实现，其他需求的实现被嵌入在这个占统治地位的空间，换句话说，需求空间是一个n维空间，而实现空间是一维空间，这种不匹配导致了糟糕的需求到实现的映射 <BR><BR><SPAN style="FONT-WEIGHT: bold">表现</SPAN> <BR>用当前方法学实现横切关注点是不好的，它会带来一些问题，我们可以大致把这些问题分为两类: <BR></P>
<UL><BR>
<LI>代码混乱：软件系统中的模块可能要同时兼顾几个方面的需要。举例来说，开发者经常要同时考虑业务逻辑，性能，同步，日志和安全等问题，兼顾各方面的需要导致相应关注点的实现元素同时出现，引起代码混乱。 <BR>
<LI>代码分散：由于横切关注点，本来就涉及到多个模块，相关实现也就得遍布在这些模块里，如在一个使用了数据库的系统里，性能问题就会影响所有访问数据库的模块。这导致代码分散在各处 <BR></LI></UL>
<P><BR><SPAN style="FONT-WEIGHT: bold">结果</SPAN> <BR><BR>混乱和分散的代码会在多个方面影响系统的设计和开发： <BR></P>
<UL><BR>
<LI>可读性差：同时实现几个关注点模糊了不同关注点的实现，使得关注点与其实现之间的对应关系不明显。 <BR>
<LI>低产出：同时实现几个关注点把开发人员的注意力从主要的转移到外围关注点，导致产能降低。 <BR>
<LI>低代码重用率：由于这种情况下，一个模块实现多个关注点，其他需要类似功能的系统不能马上使用该模块，进一步降低了产能。 <BR>
<LI>代码质量差：混乱的代码掩盖了代码中隐藏的问题。而且，由于同时要处理多个关注点，应该特别注意的关注点得不到应有的关注 <BR>
<LI>难以扩展：狭窄的视角和有限的资源总是产生仅注意当前关注点的设计。新的需求导致从新实现。由于实现不是模块化的，就是说实现牵涉到多个模块，为了新需求修改子系统可能会带来数据的不一致，而且还需相当规模测试来保证这些修改不会带来bug。 <BR><BR><SPAN style="FONT-WEIGHT: bold">当前解决方法</SPAN> <BR><BR>由于多数系统中都包含横切关注点，自然的已经形成了一些技术来模块化横切关注点的实现，这些技术包括：混入类，设计模式和面向特定问题域的解决方式 <BR><BR>使用混入类，你可以推迟关注点的最终实现。基本类包含一个混入类的实例，允许系统的其他部分设置这个实例，举个例子来说，实现业务逻辑的类包含一个混入的logger，系统的其他部分可以设置这个logger已得到合适的日志类型，比如logger可能被设置为使用文件系统或是消息中间件.在这种方式下，虽然日志的具体实现被推迟啦，基本类还是得包含在所有的写日志的点调用日志操作和控制日志信息的代码。 <BR><BR>行为型设计模式，如Visitor和Template模式，也允许你推迟具体实现。但是也就像混入类一样，操作的控制——调用visitor或template的逻辑——仍然留给了基本类 <BR><BR>面向特定问题域的解决方式，如框架和应用服务器，允许开发者用更模块化的方式处理某些横切关注点。比如EJB(Enterprise JavaBean，企业级javabean)架构,可以处理安全，系统管理，性能和容器管理的持久化（container-managed persistence）等横切关注点。Bean的开发者仅需关心业务逻辑，而部署者仅需关心部署问题，如bean与数据库的映射。但是大多数情况下，开发者还是要了解存储结构。这种方式下，你用基于XML的映射关系描述器来实现于数据持久化相关的横切关注点。 <BR><BR>面向特定问题域的解决方式提供了解决特定问题的专门机制，它的缺点是对于每一种这样的解决方式开发人员都必须重新学习，另外，由于这种方式是特定问题域相关的，属于特定问题域之外的横切关注点需要特殊的对待 <BR><BR><SPAN style="FONT-WEIGHT: bold">设计师的两难局面</SPAN> <BR><BR>好的系统设计师不仅会考虑当前需求，还会考虑到可能会有的需求以避免到处打补丁。这样就存在一个问题，预知将来是很困难的，如果你漏过了将来可能会有的横切关注点的需求，你将会需要修改或甚至是重新实现系统的许多部分；从另一个角度来说，太过于关注不一定需要的需求会导致过分设计（overdesigned）的，难以理解的，臃肿的系统。所以系统设计师处在这么一个两难局面中：怎么设计算是过分设计？应该宁可设计不足还是宁可过分设计？ <BR><BR>举个例子来说，设计师是否应该在系统中包含现在并不需要的日志机制？如果是的话，哪里是应该写日志的点？日志应该记录那些信息？相似的例子还有关于性能的优化问题，我们很少能预先知道瓶颈的所在。常用的方法是建立系统，profile它，然后翻新系统以提高性能，这种方式可能会依照profiling修改系统的很多部分，此外，随着时间的流逝，由于使用方式的变化，可能还会产生新的瓶颈，类库设计师的任务更困难，因为他很难设想出所有对类库的使用方式。 <BR><BR>总而言之，设计师很难顾及到系统可能需要处理的所有关注点。即使是在已经知道了需求的前提下，某些建立系统时需要的细节也可能不能全部得到。整体设计就面临着设计不足/过分设计的两难局面。 <BR><BR><SPAN style="FONT-WEIGHT: bold">AOP基础</SPAN> <BR><BR>到目前为止的讨论说明模块化横切关注点是有好处的。研究人员已经尝试了多种方法来实现这个任务，这些方法有一个共同的主题：分离关注点。AOP是这些方法中的一种，它的目的是清晰的分离关注点来解决以上提到的问题。 <BR><BR>AOP，从其本质上讲，使你可以用一种松散耦合的方式来实现独立的关注点，然后，组合这些实现来建立最终系统。用它所建立的系统是使用松散耦合的，模块化实现的横切关注点来搭建的。与之对照，用OOP建立的系统则是用松散耦合的模块化实现的一般关注点来实现的。在AOP终，这些模块化单元叫方面（aspect），而在OOP中，这些一般关注点的实现单元叫做类。 <BR><BR>AOP包括三个清晰的开发步骤： <BR>
<UL><BR>
<LI>方面分解：分解需求提取出横切关注点和一般关注点。在这一步里，你把核心模块级关注点和系统级的横切关注点分离开来。就前面所提到的信用卡例子来说，你可以分解出三个关注点：核心的信用卡处理，日志和验证。 <BR>
<LI>关注点实现：各自独立的实现这些关注点，还用上面信用卡的例子，你要实现信用卡处理单元，日志单元和验证单元。 <BR>
<LI>方面的重新组合：在这一步里，方面集成器通过创建一个模块单元——方面来指定重组的规则。重组过程——也叫织入或结合——则使用这些信息来构建最终系统，还拿信用卡的那个例子，你可以指定（用某种AOP的实现所提供的语言）每个操作的开始和结束需要纪录，并且每个操作在涉及到业务逻辑之前必须通过验证。 <BR></LI></UL><BR><BR><IMG src="http://www.javaworld.com/javaworld/jw-01-2002/images/jw-0118-aspectf3.gif" border=0> <BR><BR>图 3. AOP 开发的步骤 <BR><BR>AOP与OOP的不同关键在于它处理横切关注点的方式，在AOP中，每个关注点的实现都不知道其它关注点是否会‘关注’它，如信用卡处理模块并不知道其它的关注点实现正在为它做日志和验证操作。它展示了一个从OOP转化来的强大的开发范型。 <BR><BR>注意：一个AOP实现可以借助其它编程范型作为它的基础，从而原封不动的保留其基础范型的优点。例如，AOP可以选择OOP作为它的基础范型，从而把OOP善于处理一般关注点的好处直接带过来。用这样一种实现，独立的一般关注点可以使用OOP技术。这就像过程型语言是许多OOP语言的基础一样。 <BR><BR><SPAN style="FONT-WEIGHT: bold">织入举例</SPAN> <BR>织入器——一个处理器——组装一个个关注点（这个过程叫做织入）。就是说，它依照提供给它的规则把不同的执行逻辑段混编起来。 <BR><BR>为了说明代码织入，让我们回到信用卡处理的例子，为了简单起见，我们只考虑两个操作：存入和取出，并且我们假设已经有了一个合适的logger. <BR><BR>来看一下下面的信用卡模块： <BR></SPAN>
<TABLE cellSpacing=1 cellPadding=3 width="90%" align=center border=0>
<TBODY>
<TR>
<TD><SPAN class=genmed><B><FONT size=2>代码:</FONT></B></SPAN></TD></TR>
<TR>
<TD class=code><BR>public class CreditCardProcessor { <BR>&nbsp; &nbsp; public void debit(CreditCard card, Currency amount) <BR>&nbsp; &nbsp; &nbsp; &nbsp;throws InvalidCardException, NotEnoughAmountException, <BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; CardExpiredException { <BR>&nbsp; &nbsp; &nbsp; &nbsp; // 取出逻辑 <BR>&nbsp; &nbsp; } <BR>&nbsp; &nbsp; <BR>&nbsp; &nbsp; public void credit(CreditCard card, Currency amount) <BR>&nbsp; &nbsp; &nbsp; &nbsp; throws InvalidCardException { <BR>&nbsp; &nbsp; &nbsp; &nbsp; // 存入逻辑 <BR><BR>&nbsp; &nbsp; } <BR>}</TD></TR></TBODY></TABLE><SPAN class=postbody><BR><BR>下面是日志接口 <BR></SPAN>
<TABLE cellSpacing=1 cellPadding=3 width="90%" align=center border=0>
<TBODY>
<TR>
<TD><SPAN class=genmed><B><FONT size=2>代码:</FONT></B></SPAN></TD></TR>
<TR>
<TD class=code><BR>public interface Logger { <BR>&nbsp; &nbsp; public void log(String message); <BR>}</TD></TR></TBODY></TABLE><SPAN class=postbody><BR><BR>所需组合需要如下织入规则，这里用自然语言来表达（本文的后面会提供这些织入规则的程序版本）： <BR>[list=a] <BR>
<LI>纪录每个公共操作的开始 <BR>
<LI>纪录每个公共操作的结束 <BR>
<LI>纪录所有公共方法抛出的异常 <BR></LI></UL>
<P><BR><BR>织入器就会使用这些织入规则和关注点实现来产生与如下代码有相同效果的代码： <BR><BR></SPAN>
<TABLE cellSpacing=1 cellPadding=3 width="90%" align=center border=0>
<TBODY>
<TR>
<TD><SPAN class=genmed><B><FONT size=2>代码:</FONT></B></SPAN></TD></TR>
<TR>
<TD class=code>public class CreditCardProcessorWithLogging { <BR>&nbsp; &nbsp; Logger _logger; <BR><BR>&nbsp; &nbsp; public void debit(CreditCard card, Money amount) <BR>&nbsp; &nbsp; &nbsp; &nbsp; throws InvalidCardException, NotEnoughAmountException, <BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;CardExpiredException { <BR>&nbsp; &nbsp; &nbsp; &nbsp; _logger.log("Starting CreditCardProcessor.credit(CreditCard, <BR>Money) " <BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; + "Card: " + card + " Amount: " + amount); <BR>&nbsp; &nbsp; &nbsp; &nbsp; // 取出逻辑 <BR>&nbsp; &nbsp; &nbsp; &nbsp; _logger.log("Completing CreditCardProcessor.credit(CreditCard, <BR>Money) " <BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; + "Card: " + card + " Amount: " + amount); <BR>&nbsp; &nbsp; } <BR>&nbsp; &nbsp; <BR>&nbsp; &nbsp; public void credit(CreditCard card, Money amount) <BR>&nbsp; &nbsp; &nbsp; &nbsp; throws InvalidCardException { <BR>&nbsp; &nbsp; &nbsp; &nbsp; System.out.println("Debiting"); <BR>&nbsp; &nbsp; &nbsp; &nbsp; _logger.log("Starting CreditCardProcessor.debit(CreditCard, <BR>Money) " <BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; + "Card: " + card + " Amount: " + amount); <BR>&nbsp; &nbsp; &nbsp; &nbsp; // 存入逻辑 <BR>&nbsp; &nbsp; &nbsp; &nbsp; _logger.log("Completing CreditCardProcessor.credit(CreditCard, <BR>Money) " <BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; + "Card: " + card + " Amount: " + amount); <BR><BR>&nbsp; &nbsp; } <BR>}</TD></TR></TBODY></TABLE><SPAN class=postbody><BR><BR><SPAN style="FONT-WEIGHT: bold">AOP语言剖析</SPAN> <BR><BR>就像其他编程范型的实现一样，AOP的实现有两部分组成：语言规范和实现。语言规范描述了语言的基础单元和语法。语言实现则按照语言规范来验证代码的正确性并把代码转成目标机器的可执行形式。这一节，我来解释一下AOP组成部分。 <BR><BR><SPAN style="FONT-WEIGHT: bold">AOP语言规范</SPAN> <BR>从抽象的角度看来，一种AOP语言要说明下面两个方面： <BR></P>
<UL><BR>
<LI>关注点的实现：把每个需求映射为代码，然后，编译器把它翻译成可执行代码，由于关注点的实现以指定过程的形式出现，你可以使用传统语言如C，C++，Java等。 <BR>
<LI>织入规则规范：怎样把独立实现的关注点组合起来形成最终系统呢？为了这个目的，需要建立一种语言来指定组合不同的实现单元以形成最终系统的规则，这种指定织入规则的语言可以是实现语言的扩展，也可以是一种完全不同的语言。 <BR></LI></UL>
<P><BR><BR><SPAN style="FONT-WEIGHT: bold">AOP语言的实现</SPAN> <BR><BR>AOP的编译器执行两步操作： <BR></P>
<OL type=a><BR>
<LI>组装关注点。 <BR>
<LI>把组装结果转成可执行代码 <BR></LI></OL>
<P><BR>AOP实现可以用多种方式实现织入，包括源码到源码的转换。它预处理每个方面的源码产生织入过的源码，然后把织入过的源码交给基础语言的编译器产生最终可执行代码。比如，使用这种方式，一个基于Java的AOP实现可以先把不同的方面转化成Java源代码，然后让Java编译器把它转化成字节码。也可以直接在字节码级别执行织入；毕竟，字节码本身也是一种源码。此外，下面的执行系统——Java虚拟机——也可以是方面认知的，基于Java的AOP实现如果使用这种方式的话，虚拟机可以先装入织入规则，然后对后来装入的类都应用这种规则，也就是说，它可以执行just-in-time的方面织入。 <BR><BR><SPAN style="FONT-WEIGHT: bold">AOP的好处</SPAN> <BR><BR>AOP可帮助我们解决上面提到的代码混乱和代码分散所带来的问题，它还有一些别的好处： <BR></P>
<UL><BR>
<LI>块化横切关注点：AOP用最小的耦合处理每个关注点，使得即使是横切关注点也是模块化的。这样的实现产生的系统，其代码的冗余小。模块化的实现还使得系统容易理解和维护 <BR>
<LI>系统容易扩展：由于方面模块根本不知道横切关注点，所以很容易通过建立新的方面加入新的功能，另外，当你往系统中加入新的模块时，已有的方面自动的横切进来，使系统的易于扩展 <BR>
<LI>设计决定的迟绑定：还记得设计师的两难局面吗？使用AOP,设计师可以推迟为将来的需求作决定，因为它可以把这种需求作为独立的方面很容易的实现。 <BR>
<LI>更好的代码重用性：由于AOP把每个方面实现为独立的模块，模块之间是松散耦合的，举例来说，你可以用另外一个独立的日志写入器方面（替换当前的）把日志写入数据库，以满足不同的日志写入要求。 <BR>总的来说，松散耦合的实现意味着更好的代码重用性， AOP在使系统实现松散耦合这一点上比OOP做得更好。 <BR></LI></UL>
<P><BR><BR><SPAN style="FONT-WEIGHT: bold">AspectJ:一个Java的AOP实现</SPAN> <BR><BR>AspectJ是一个可免费获得的由施乐公司帕洛阿尔托研究中心（Xerox PARC）开发Java的AOP实现，它是一个多功能的面向方面的Java扩展。它使用Java作为单个关注点的实现语言，并扩展Java以指定织入规则。这些规则是用切入点（pointcuts）、联结点（join points），通知（advice）和方面（aspect）来说明的。联结点是定义在程序执行过程之间的点，切入点由用来指定联结点的语言构造，通知定义了要在切入点上执行的代码片，而方面则是这些基础元素的组合。 <BR><BR>另外，AspectJ允许以多种方式用方面和类建立新的方面，你可以引入新的数据成员和方法，或是声明一个新的类来继承和实现另外的类或接口。 <BR><BR>AspectJ的织入器——AspectJ的编译器——负责把不同的方面组合在一起，由于由AspectJ编译器建立的最终系统是纯Java字节码，它可以运行在任何符合Java标准的虚拟机上。而且，AspectJ还提供了一些工具如调试器和Java IDE集成等，我将会在本系列的第二、三部分详细讲解这些。 <BR><BR>下面是我在上面用自然语言描述的日志方面的织入规则的AspectJ实现，由于我将会在第二部分详细介绍AspectJ，所以如果你不能透彻的看懂它的话也不必担心。关键是你应该注意到信用卡处理过程本身一点都不知道日志的事。 <BR><BR></SPAN>
<TABLE cellSpacing=1 cellPadding=3 width="90%" align=center border=0>
<TBODY>
<TR>
<TD><SPAN class=genmed><B><FONT size=2>代码:</FONT></B></SPAN></TD></TR>
<TR>
<TD class=code><BR>public aspect LogCreditCardProcessorOperations { <BR>&nbsp; &nbsp; Logger logger = new StdoutLogger(); <BR><BR>&nbsp; &nbsp; pointcut publicOperation(): <BR>&nbsp; &nbsp; &nbsp; &nbsp; execution(public * CreditCardProcessor.*(..)); <BR><BR>&nbsp; &nbsp; pointcut publicOperationCardAmountArgs(CreditCard card, <BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;Money amount): <BR>&nbsp; &nbsp; &nbsp; &nbsp; publicOperation() &amp;&amp; args(card, amount); <BR><BR>&nbsp; &nbsp; before(CreditCard card, Money amount): <BR>&nbsp; &nbsp; &nbsp; &nbsp; publicOperationCardAmountArgs(card, amount) { <BR>&nbsp; &nbsp; &nbsp; &nbsp; logOperation("Starting", <BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;thisjoin point.getSignature().toString(), card, amount); <BR>&nbsp; &nbsp; } <BR><BR>&nbsp; &nbsp; after(CreditCard card, Money amount) returning: <BR>&nbsp; &nbsp; &nbsp; &nbsp; publicOperationCardAmountArgs(card, amount) { <BR>&nbsp; &nbsp; &nbsp; &nbsp; logOperation("Completing", <BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; thisjoin point.getSignature().toString(), card, amount); <BR>&nbsp; &nbsp; } <BR><BR>&nbsp; &nbsp; after (CreditCard card, Money amount) throwing (Exception e): <BR>&nbsp; &nbsp; &nbsp; &nbsp; publicOperationCardAmountArgs(card, amount) { <BR>&nbsp; &nbsp; &nbsp; &nbsp; logOperation("Exception " + e, <BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; thisjoin point.getSignature().toString(), card, amount); <BR>&nbsp; &nbsp; } <BR><BR>&nbsp; &nbsp; private void logOperation(String status, String operation, <BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; CreditCard card, Money amount) { <BR>&nbsp; &nbsp; &nbsp; &nbsp; logger.log(status + " " + operation + <BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;" Card: " + card + " Amount: " + amount); <BR>&nbsp; &nbsp; } <BR>} <BR><BR><BR></TD></TR></TBODY></TABLE><SPAN class=postbody><BR><BR><SPAN style="FONT-WEIGHT: bold">我需要AOP吗？</SPAN> <BR><BR>AOP仅仅是解决设计上的缺点吗？在AOP里，每个关注点的实现的并不知道是否有其它关注点关注它，这是AOP和OOP的主要区别，在AOP里，组合的流向是从横切关注点到主关注点，而OOP则相反，但是，OOP可以和AOP很好的共存。比如，你可以使用一个混入类来做组合，既可以用AOP实现，也可以用OOP实现，这取决你对AOP的接受程度。在这两种情况下，实现横切关注点的混入类实现都无需知道它自己是被用在类中还是被用在方面中。举个例子来说，你可以把一个日志写入器接口用作某些类的混入类或是用作一个日志方面。因而，从OOP到AOP是渐进的。 <BR><BR><SPAN style="FONT-WEIGHT: bold">了解AOP</SPAN> <BR><BR>在这篇文章里，你看到了横切关系带来的问题，这些问题的当前解决方法，以及这些方法的缺点。你也看到了AOP是怎样克服这些缺点的。AOP的编程方式试图模块化横切关注点的实现，提供了一个更好更快的软件开发方式。 <BR><BR>如果你的系统中涉及到多个横切关注点，你可以考虑进一步了解AOP,它的实现，它的好处。AOP很可能会是编程方式的下一个里程碑。请继续关注本系列的第二、第三部分。 <BR><BR>,yanger(y-ge@263.net) Nov 14 ,2003<BR>_________________<BR>HelloSpring</SPAN><SPAN class=gensmall><BR></SPAN></FONT></P><img src ="http://www.blogjava.net/TrampEagle/aggbug/27702.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/TrampEagle/" target="_blank">TrampEagle</a> 2006-01-12 11:51 <a href="http://www.blogjava.net/TrampEagle/articles/27702.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>