简而言之,Smart Client智能客户端就是这样一种一个可扩展的能集成不同应用的桌面应用程序:它可以无接触部署、即需即装、动态加载,XCopy即可运行而无须修改注册表,可以动态升级、自动更新,可以方便的经Web运行而不用担心防火墙问题并可以方便的离线运用,方便的连接WebServices的Windows应用程序
应用程序的各个构件之间的相互调用并不采用直接引用的方式,而是采用动态加载,即需即装的方式,有效地降低了对系统资源的消耗。应用软件开发商可根据企业应用系统的公共接口进行开发,然后将应用组件发布在企业的服务器上,客户端应用程序将自动发现并加载该应用组件。
由于上面第一点所言构件之间的相互调用并不采用直接引用方式,这样系统实现的更松散的耦合,为应用程序升级更新提供了方便。
由于应用程序的松散耦合特性,使得系统的进一步模块化成为了可能,新功能、新特性的加入只需要开发出符合接口定义的新模块并添加连接即可。而无须修改重编译现有的程序。
安装时只要将一个主程序文件下载到本地,直接运行即可,无须改变注册表或共享的系统组件,其他应用组件将在第一次运行时自动下载。
Smart Client的应用程序可以很方便的从网络服务器加载应用程序,而且因为程序及加载是从80端口实现,故无须考虑防火墙问题,这样为企业系统的集中管理提供了方便。
只需将新版本的程序发布在服务器上,由客户端自动发现最新版本的程序和应用组件,并自动下载和更新。
Smart Client应用程序尽管使用网络加载程序集,但一旦加载之后,程序集便被缓存到了本地。当用户至少启动了一次应用程序后,其装配就被下载和缓存到本地内存中了,所以用户就可以离线运行你的智能客户端了(通过转换浏览器到离线工作状态),假设应用程序不需要永久访问Web services或一个共享的数据库就可以运行。
构建智能客户端的最大的好处就是可以离线使用。尽管业务之间的联系越来越紧密,但我们仍不能给企业应用程序提供始终连续的连接。离线式工作方式可以在你重新在线时,自动接收数据和应用程序更新,这种特征是人们很想得到的,但在.NET前,这是很难实现的。同胖客户端一样,智能客户端给客户端分布大量的处理,这就为服务器免除了它在一个基于Web的应用程序中需要承担的负荷。最后,智能客户端采取一种用户希望应用程序采取的工作方式——允许快速数据存取和管理,而不需要不必要的屏幕更新。
用户可根据喜好自行设置客户端应用程序,配置信息将被保存到服务器上。
Smart Client应用程序可以与WebServices方便的集成应用,这样便可以轻松享受C/S应用程序的完美用户体验而不需担心防火墙等等的一系列问题。
尽管有大量的广告,但瘦Web解决方案并没必要成为所有企业应用程序的未来。不要丢弃用WinForms来构建企业应用程序这种想法,因为企业应用需要集中的分布。下面的这张表格描述了Smart Client和瘦Web解决房按之间的对比:
功能 |
智能客户端 |
Web应用程序 |
可以离线工作 |
可以 |
不容易 |
集中的部署 |
可以 |
可以 |
高级的GUI特征 |
有 |
有 |
高性能的GUI |
可以 |
不可以 |
卸载处理到客户端 |
可以 |
有限 |
通过HTTP更新 |
可以 |
可以 |
安全 |
是 |
是 |
不接触客户端安装 |
不是;需要构建用来改变.NET的安全配置的一个加载应用程序(除非你用的是Microsoft Transaction Server或Citrix,但是那样的话,应用程序就只能在线使用) |
是 |
大部分时间只传输数据 |
是 |
不是;结合内容的传输,.NET控件显示在IE中 |
客户端需要.NET Framework |
是 |
不是 |
平台和客户端是独立的 |
不是 |
是 |
适合电子商务或Internet应用程序 |
不是;最适合企业内部应用程序 |
是 |
充分利用客户端丰富的资源 |
可以 |
不能 |
表 1 比较两者
通过将智能客户端的功能和Web应用程序的功能进行比较,可以简化你的决策过程。
图1. 智能客户端的工作模型
应用程序加载器用HTTP从Web服务器上的一个虚拟目录来访问和下载装配。下载后,装配被缓存起来,只有需要的时候才执行它们。
这种运行方式看起来感觉像是一个本地的应用程序,它的好处是可以帮助你控制安装目录和相关图标的创建,并可以提供简洁的卸载。坏处是您需要制作一个MSI安装文件并在客户机上Setup。
只是一种完全的网络运行方式,运行起来和你上网打开网页差不多——在浏览器地址栏里输入一个URL或者在网页上点击相应的连接。这种运行方式的好处是实现了真正的零接触部署,您不需要再客户机上安装任何东西,当然坏处也是显而易见的:它的URL路径经常可能会被诸如FlashGet之类的软件拦截而变成了下载而不是直接运行。
一种组件的加载方式是使用.NET框架的程序集加载器直接从网络加载,这种方式的一个好处就是简单,并且能够实现全自动的程序集更新,见如下一段代码:
using System.Reflection;
//网络运行方式,加载的程序集路径为服务器上的url地址
assemblyFileName = "http://SmartClient/AppComponent1.dll";
Assembly asm = Assembly.LoadFrom(assemblyFileName);
寥寥数句,便实现了程序集的网络加载,其关键的一句就是Assembly.LoadFrom(assemblyFileName)。
当然这种方式虽然简单但也有其坏处,那就是这种方式非常地依赖于浏览器的缓存机制,一旦缓存被清空,程序便无法再离线工作,这是一个很麻烦的问题。
动态加载组件的另外一种方式就是先将程序集下载到本地文件夹中然后再从本地加载运行,这种方式不依赖于浏览器缓存,也就是说应用程序组件只要在运行一次之后(如果是设定为组件需要时从网络下载的话),即使离线并清空了缓存,应用程序仍然能够正常运行。其缺点是:不能自动将程序集从网络下载到本地,需要用到Windows Background Intelligent Transfer Service,不能全自动的更新程序集,需要手动编写程序集加载器,这个东东可以使用MicrosoftUpdaterApplicationBlock,并提供了全部的源代码。
这里谈到的组件当然是指界面组件,如果不涉及到用户界面,当然也就谈不上什么呈现方式了。
作为一种很常见的组件呈现方式,程序集作为窗体来实例化,然后使用Form.Show()方法呈现,而将引导窗体以Form.Hide()方式隐藏。
而在将组件作为子窗体的呈现方式中,是将承载平台设计为MDI窗体,然后将装载的组件的ParentForm属性设置为承载平台来显示。
在这种方式中,组件不以窗体的方式来实例化,而是以用户控件的方式实例化,然后将其动态添加到承载窗体的某一个托盘中(通常是Panel)。
说到Web方式直接运行,就不能不谈到代码访问安全性(Code Access Security)问题,如果一个黑客能够说服或者诱使你下载一段代码,而你在很高的权限账户下运行它,那操作系统就对各种危害门户打开。这种情形就是导致著名的宣言“在最小权限下运行”的原因,每个人都应该注意这一点。
但是,.NET Framework提供了比这种单纯的基于身份的安全性更高级的功能。由CLR强制执行的CAS机制允许为代码(实际上是assembliy,.NET Framework中调度和保护的单位)指定基于属性的不同的可信度等级,例如数字证书或源站点或URL。在这种方式下,.NET Framework只允许已知的或受信的源代码执行。
为了让你的智能应用程序运转,你需要改变一些客户端的安全设置——实质上就是通知客户端运行时间相信你的应用程序的装配。一种方法就是将带有你的装配的站点添加到IE中可信任站点清单中,然后用安装在你的管理工具目录下的Microsoft .NET Framework Configuration工具来修改.NET Framework安全设置。打开Framework Configuration工具,右击Runtime Security Policy,然后选择Adjust Security。对于Trusted Sites中指定的所有站点,将信任级别调整到Full Trust。作为选择,你也可以用Framework Configuaration工具来修改安全策略,使它信任你的应用程序的个别装配。右击Runtime Security Policy,选择Trust Assembly。
这里的服务器端的身份验证,实际上就是WebServices的身份验证,虽然说从理论上Forms身份验证也是可行的,但是WebServices既然是一个A
使用用户的Windows帐号如域帐号进行身份验证。
使用与用户相关联的.NET Passport帐号进行身份验证。
使用Soap来给Web服务传递账户信息是一个相对使用URL传递装户信息而言比较安全的方式,同时不必为每一个WebMethod传递证书(作为参数的一部份),如果仍然觉得不够安全则可以对Soap头进行加密,或者干脆使用Soap传递安全证书,这里只介绍使用Soap不加密传递用户名和密码进行身份验证的方式当一个服务请求发送到服务端,请求会被WebServiceAuthenticationModule截获,WebServiceAuthenticationModule就开始分析该请求的Soap头,从中解析出客户端发送的用户名和密码,并进行身份验证。如果身份验证通过,WebServiceAuthenticationModule便会给用户签发身份验证票;如果身份验证失败,WebServiceAuthenticationModule便会抛出一个消息为”Access Deny!”的SOAPException,随后服务端和客户端均可截获该异常并进行处理。
首先生成一个自定义的http模块(这里用我做的WebServiceAuthenticationModule),并在Webconfig中注册; 在Web服务类中添加一个WebServiceAuthenticationModule定义的SOAPAuthHeader字段类型,并将SOAPAuthHeader属性应用于我们的Web服务方法中,然后使用赋给这个字段类型的名称sHeader定义这个属性的构造函数; 对于客户端来说,每一次将请求发送到WebServices都需要填充Soap头,在WebServices定义了Soap头之后,客户端的代理类也会生成相应的类代理,使用时,首先创建一个服务的对象实例代表我们的Web服务,然后创建一个Soap头对象实例,并填充Soap头,接下来将Soap头与该代理程序相关联,以便使用我们的Web服务。
开发WebServices应做到:粗粒度、较少的接口,从而交付与网络请求的延迟成本相称的价值。
这样做的目的是为了尽量减小客户端组件的大小,使用WebServices的应用程序时要生成代理类的,如果一个.asmx文件公开太多的功能和太多的程序集,势必会使客户端组件过于臃肿,而实际上它用到的可能只是其中一小部分代码而已。一般而言,一个完整的功能主要的可能只有一两张数据库表,其他的只是辅助而已。这种情况下,主表使用数据集,而辅助表甚至只需要使用数组即可。
实现这一个原则则是为了实现模块化,一个功能的实现不需要太多依赖于相关模块。仍然以上一点为例,一个.asmx文件中,除了公开主表的数据集并提供完全的操作外,一般而言,还应当提供辅助表的简单化描述(如数组描述)的只读的数据实现,这样客户端组件只需要引用一个.asmx服务便可以实现全部功能了。
由于Smart Client通过网络加载,所以单个程序集应当尽量的小,以便快速加载。
既然是一套系统,各组件之间免不了要互相调用。程序集间的调用原则是尽量不要直接引用,而应当是通过接口来通信。
当然上面说的接口是一个广义的概念,应该说还是有两种实现方式:抽像类和接口。
使用抽象类的好处是,在派生类中不需要去重写所有的实现,抽象类可以完全实现,但更常见的是部分实现或者根本不实现,从而封装继承类的通用功能,但问题在于一个派生类只能从一个类中去继承。如果预计要创建组件的多个版本,则创建抽象类。抽象类提供简单易行的方法来控制组件版本。通过更新基类,所有继承类都随更改自动更新。如果要设计大的功能单元,则使用抽象类。如果要在组件的所有实现间提供通用的已实现功能,则使用抽象类。
而使用接口的一个最大的好处就是,你的类可以去实现多个接口,这样在系统开发中可以定义多个小而不同接口,开发组件时可以根据需要实现其中的一个或几个接口,就像搭积木似的,从而得到所需要的功能,但是“接口”是完全抽象的成员集合,可以被看作是为操作定义合同。接口的实现完全留给开发者去做。还有更重要的一点是:接口一旦被定义和接受,就必须保持不变,以保护为使用该接口而编写的应用程序。接口发布后,就不能对其进行更改。如果创建的功能将在大范围的全异对象间使用,则使用接口。抽象类应主要用于关系密切的对象,而接口最适合为不相关的类提供通用功能。如果要设计小而简练的功能块,则使用接口。抽象类允许部分实现类,而接口不包含任何成员的实现。
这里专门提一下开发的关键点,接口的定义,本文看到这里,不难得出结论,Smart Client诸多优越性实现的必要条件是定义并实现一系列定义良好的接口标准。所以在系统开发时首先要做的是仔细分析并研究系统的各方面特性,制定出一套完整而规范的接口来。
创建一个接口就是在创建一个定义,接口定义发布后则永远不能更改。“接口不变性”是组件设计的一个重要原则,是为了保护为使用接口而编写的现有系统。设计优良的接口往往很小且相互独立,减少了性能问题发生的可能。一般情况下,应在一个接口中将紧密相关的几个功能聚合为一组。功能太多会使接口不便于运行,而过于细分功能将导致额外的系统开销并降低使用的简易性。通过添加接口来改进系统的能力使您能够获得面向对象的编程所提供的好处。
Smart Client是一个不同于以往常见的应用程序的比较复杂的系统,所以它的调试技术也比以前复杂的多,至今仍然是一个难点。因为,开发的代码大都不是一个可执行的应用程序,更多的是dll库文件,这样就无法直接调试,必须得写出一个调试程序来进行间接调试。同时因为松耦合和网络加载的原因,使得本地紧耦合调试和实际运行情况多少有些不同。这一点还没有有效的解决办法。
Smart Client是一个基于浏览器缓存机制和在线离线均可运行的应用程序,那么从理论上讲应用程序所访问过的数据也应当会在本地缓存,至于事实是否如此,还没有看到相关资料,需要进一步实验和论证。如果并没有自动缓存,那么就需要考虑编写代码实现常用数据本地缓存,毕竟一些不是常常更新的数据每次读取都到服务器上去获取一次是没有太大必要的啦(这样也体现不出Smart Client的优势来),那么采用什么方式缓存到本地,什么时候需要到服务器上更新是值得考虑一下的问题。
前面谈到了定义规范化接口的重要性,那么显然一个相当重要而且必须要得到解决的问题就是:如何定义我们的通用而规范的接口?
这个问题就涉及到了前面谈到的组件的加载方式的问题,如果是采用先将组件下载到本地然后从本地加载的方式,那么就需要解决升级程序的开发这个问题,虽然说有MicrosoftUpdaterApplicationBlock,但至少我们要把它读懂并学会如何去用吧。
俗话说,磨刀不误砍柴工,要开发出一个精良的Smart Client应用程序,少走弯路并提高以后开发时的开发效率,我建议在进行Smart Client应用程序开发之前先做好以下一些准备工作:
制定出一套通用而规范的应用程序组件接口来。
然后还应当开发好如下一些通用构件:
组件间通信的接口类,根据实际需要,可以将若干个接口放在一起开发一个程序集,分门别类开发出若干个接口集来。
引导程序差不多是Smart Client中唯一的一个EXE可执行程序,它负责了整个系统的引导和维护,但是其实实现的功能很简单。
针对Smart Client应用的特点开发的读写客户端本地文件的通用类,其核心代码如下:
//按用户、域、程序集获取独立存储区
IsolatedStorageFile isoStore = IsolatedStorageFile.GetStore(IsolatedStorageScope.User
| IsolatedStorageScope.Domain | IsolatedStorageScope.Assembly, null, null);
//创建目录
isoStore.CreateDirectory("TestDir");
//创建文件
IsolatedStorageFileStream isoStream1 = new IsolatedStorageFileStream
("TestDir//test.txt", FileMode.Create, isoStore);
//写入文件
StreamWriter writer = null;
writer = new StreamWriter(isoStream1);
writer.WriteLine("Hello Isolated Storage");
writer.Close();
isoStream1.Close();
针对SmartClient的特点开发的异常处组件,其中要多一些对网络异常状况(如网络不通等等)的处理。
针对Smart Client的特点开发的权限控制模块,应该说分为两个部分,服务器端和客户端。服务器端主要是开发一个WebServices身份验证模块,客户端则要处理与服务器端身份验证模块的通信和自身的权限控制功能(如在离线工作时)。
本文地址:http://com.8s8s.com/it/it45263.htm