体验.net 2.0 的优雅
2008-11-28 22:03 宝宝合凤凰 阅读(270) 评论(0) 收藏 举报体验.net 2.0 的优雅(1) -- 异步WebService调用
在.net1.x中,异步WebService异步调用的一般方式为调用方法XX对应的BeginXX方法来完成,其过程类似于异步委托的使用。详情请点此了解。
在.net2.0中(准确的说是vs 2005中),异步WebService异步调用的方式的例子:
服务器端代码
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class Service : System.Web.Services.WebService
{
public Service () {
}
[WebMethod]
public string HelloWorld() {
return "Hello World";
}
}很简单,没有了AsyncCallback、IAsyncResult 这两个烦人的东西,调用的代码变得简洁、优雅了,而且可以从e.Result得到强类型的返回值(上例为"Hello World")。但是,有兴趣的话,可以看看vs 2005生成的Referance.cs文件,那可比2003中的复杂很多。其中可以看到System.ComponentModel.AsyncCompletedEventArgs 、 System.Threading.SendOrPostCallback(delegate)这两个在 .net 1.x 中没有的“怪物”,估计用到的地方还不止WebService客户端。有时间再研究看看。
体验 .net2.0 的优雅(2) -- ASP.net 主题和皮肤
示例站点的截图
black skin
white skin
看了一下visual web developer 中自带的个人站点,发现aspnet2.0里面的很多技术使用起来确实很优雅,比较典型的有 login控件和用户系统、MasterPage、主题和皮肤、SiteMap等等。这些特性,甚至不需要教程就可以很快上手。本文要介绍的是主题和皮肤的应用。
(一) 如何使用
1) 在你的应用里面添加 App_Themes 文件夹
2) 将主题相关的文件(包括.css,、.skin文件、其他资源文件包括图片、Flash等)组织到一个以主题名命名的文件夹中,将主题文件夹放于App_Themes目录。如果有多个主题,重复上述步骤。
Example :
Css 文件(x.css)
.column {background-image: url(images/content-shim.gif);}
.gradient { background-image: url(images/content-shim-photo.gif);}
.solid {background-image: url(images/content-shim-none.gif);}
.header {
background-image: url(Images/header.gif);
background-repeat: no-repeat;
position: relative;
width: 789px;
height: 76px;
margin: 0 auto 5px auto;
} 
Skin 文件(x.skin)
<asp:imagebutton runat="server" Imageurl="Images/button-login.gif" skinid="login" />
<asp:image runat="server" Imageurl="Images/button-create.gif" skinid="create" />
<asp:image runat="server" ImageUrl="Images/button-download.gif" skinid="download"/>
理论上上来说,不同的主题中的样式表和SKIN文件的结构都应该是完全相同的。
3) 设置你要应用主题的控件的css样式名为css中定义的样式名(比如column、gradient)
设置你要应用主题的服务器控件的SkinId为Skin文件中定义的skinid(比如login、create等),这样在Skin中定义的控件属性会自动附加到当前的控件上(内部也是使用CSS实现的)
4) 应用主题,有三种办法
全局主题:在Web.config中<system.web>中添加<pages StyleSheetTheme ="ThemeName"/>节,这样在整个应用中都会自动应用名为ThemeName 的主题
页面主题:在ASPX文件顶部加入:<%@ Page Language="C#" StylesheetTheme=" ThemeName " %>,这样当前页面都会应用名为ThemeName 的主题,或者在ASPX_CS中加(Page_Load方法)入如下代码 Page. StyleSheetTheme = “ThemeName”;
角色主题:需要使用masterpage 来辅助实现 ,通过User.IsInRole(“RoleName”) 来判断用户是否属于某个角色,然后决定使用何种主题(Page. StyleSheetTheme = “ThemeName”);当然,使用这种办法还可以应用用户选择的主题
如果你仍然感觉迷惑,请查看 Quick Start 。
(二) 工作原理
不同的主题和皮肤之间的不同主要是页面和页面中的控件的样式(包括字体、段落、背景、边框等等)、部分图片的不同。
传统的完全使用CSS来表现不同的主题的方案,在处理图片和图片按钮的地方往往心有余而力不足。而在aspnet2.0里面一切都已经变得简单。
主题和皮肤是 dotnet framework2.0内建支持的,服务器控件添加了SkinId属性,Page类也添加了Theme 和 StyleSheetTheme 属性,其目的就是优雅的支持Skin。在应用指定了主题之后,相关的页面会自动链接位于主题目录下的css文件和skin文件,css的用法跟传统的用法没有什么区别,而skin文件则以一种类似于css的方式工作,指定了SkinId的服务器控件会自动从skin文件中加载并附加匹配的属性或样式(最常用的是Image 和ImageButton的ImageUrl属性,这样做可以使页面在不同的主题下)---这是在服务器端完成的。由于skin文件在使用后是缓存在内存中的,所以效率不会有问题。
(三) Theme 和 StylesheetTheme 的区别
基本上,本文前面所有的 StyleSheetTheme 都可以替换为 Theme ,我猜想区别是使用 Theme 时位于主题文件夹中的 样式表(CSS)文件不会被应用到主题中,但是根据我的测试,发现两种情况下生成的页面中,只有<link href=’’>这一节的位置不同,一个出现在<Title>标记前面,一个则是后面。
(四) 优雅之处
a) 完全Framework内建的支持、不再需要复杂的编程,甚至不再需要编程;
b) 在vs2005中有非常友好的设计时支持,也就是说,在设计的时候你就可以看到某种主题的效果;
c) 学习曲线很平缓,如果你熟悉CSS的话,则更加没有难度。
体验.net2.0的优雅(3) -- 为您的 SiteMap 添加 控制转发功能
在ASP.net 页面中可以利用这个SiteMap 文件和 menu 控件配合生成网站目录,也可以用来和SiteMapPath 控件配合生成 “您现在的位置”。但是遗憾的是缺没有一个简单的办法利用此文件来进行控制转发。在 Struts Framework 里面,控制转发却是一个非常重要的功能,被人大肆的吹嘘---优雅。不过你不用急,在ASP.net里面,我们可以用几行代码完成这个操作。
以上是 FindForward, Forward , Transfer 三个方法的共六个重载,其中3个是可以给URL加参数的
FindForward : 可以根据 SiteMap 中的 title 找到对应的 URL
Forward :直接转发到 SiteMap 中 相应 title 对应的 URL 上去
Transfer :跟 Forward 功能,但是是在服务器端转发(保留了Context),也就是说浏览器看到的地址不变
实现原理很简单,我们在静态构造函数里面使用 SiteMap 提供的API把 SiteMap 读取出来存储到了一个集合里面,在需要的时候我们从集合众查找 URL ,仅此而已。
把以上代码存为 cs 文件,放在 App_Code里面,就可以安全放心的使用了 。
以下是使用实例:
打印的结果:
/MyWebSite/Details.aspx?AlbumID=1&Page=4
不过尽管如此,还是有一个小小的遗憾,因为 vs 2005 为资源文件和配置文件都提供了生成强类型访问的机制,却没有为SiteMap 提供类似的机制,也就是说如果 title 书写错误的话,仍然只能得到运行时检查。
[图中的 三个点 代表直接父类的名称]
我们以Membership为例,来看一下这么多的Provider是怎么被系统所使用的,以及使用它们会给我们带来什么样的好处。
LoginControls(包括登录、创建用户、修改密码等控件)是服务器控件,这些服务器控件通过MembershipAPI 来执行相应的操作。MembershipAPI是密封类(System.Web.Security.Membership类),其中定义了很多静态的方法,包括 CreateUser、DeleteUser等等,但是本身并不提供具体的实现,而是使用MembershipProvider提供的服务。MembershipProvider是一个抽象类,其中也定义了CreateUser、DeleteUser 等方法,SqlMembershipProvider是它的一个实现,可以将Membership的数据持久化到Sql Server 2005中。
1. Strategy
这里用到了一个非典型的策略模式(strategy)。在典型的策略模式中,Provider应该是通过构造方法注入,而在此处,Membership是一个静态类,它的Provider和ProviderCollection都是只读属性,因此我猜测Provider并非是由其他类注入的,而是由Provider自己通过应用上下文获取到的。策略模式的作用是将 MembershipAPI 对具体的Provider(比如SqlMembershipProvider)的依赖转化成了 对抽象类 MembershipProvider 的依赖 + SqlMembershipProvider对MembershipProvider的依赖,从而符合开闭原则(OCP)。这样可以保证系统的灵活性和效率。
2. IoC
这里应用的策略模式的一个附带的好处是实现了 控制反转(IoC)。通常我们设计分层应用的时候,都是自底层向上层设计,上层的组件使用下层提供的服务并直接依赖于下一层的组件。这样做的缺点是 造成底层组件很难修改,因为任意的修改(比如修改方法的功能或者名称)都可能导致上层应用崩溃。使用了策略模式模式以后,我们把MembershipAPI和MembershipProvider抽象类置于同一层(上层)中,底层的SqlMembershipProvider则依赖于上一层(实现MembershipProvider中的抽象方法)。很明显,我们将上层组件对下层组件的依赖转化成了下层组件对于上层组件的依赖,这就是控制反转。好处是给我们带来了即插即用功能---Provider是可插接的,由于Provider造成的一切系统崩溃都由 Provider 自己负责。
3. Dependency Injection
我们可以实现自己的MembershipProvider,比如说OracleMembershipProvider---使用Oracle来存储Membership和Profile数据---只要我们继承MembershipProvider类并覆写的部分方法即可。那么我们编写的 OracleMembershipProvider 或者 系统自带的 SqlMembershipProvider是如何被 MembershipAPI 使用的呢?这里有一个重要的概念---依赖注入(Dependency Injection)。依赖注入的意思是将应用程序依赖的组件在运行时注入给应用,依赖注入一般要使用反射技术。
依赖注入通常分为3种形式:
构造注入:通过构造方法将组件注入
设值注入:通过设置属性的值将组件注入
接口注入:从容器中获取指定接口的实现
注入过程一般由某种 IoC 容器(比如Spring)完成,而且特别适合由容器完成。很难说在MembershipAPI 中使用了某种典型的依赖注入方式。我们可以这么说,仅仅使用了反射工厂。因为在这里,仅仅使用反射已经足够,不需要复杂的对象装配过程。
如果不考虑内部的实现过程,我们要做的,仅仅是将我们的实现拷贝到Bin目录,并在Web.Config文件中system.web节中增加下面的节点:
其中 type 中定义的就是 我们自定义的Provider的全名称(包括名字空间)。
.Net 异步编程模式总结
使用 .NET 异步编程,在程序继续执行的同时对 .NET 类方法进行调用,直到进行指定的回调为止;或者如果没有提供回调,则直到对调用的阻塞、轮询或等待完成为止。例如,一个程序可以调用一个方法,该方法枚举一个较大的列表,同时主程序将继续执行。在完成枚举后,进行回调并且程序对它进行寻址。
异步编程是由 .NET 框架的许多区域支持的功能,这些区域包括:
- 文件 IO、流 IO、套接字 IO
using (FileStream fs = new FileStream("C:\\1.txt",FileMode.OpenOrCreate) )
{
fs.BeginWrite(
.
fs.BeginRead(
.
}
SocketInformation si = new SocketInformation();
using( Socket s = new Socket(si) )
{
s.BeginAccept(
);
s.BeginReceive(
);
s.BeginSend(
);
s.BeginConnect(
);
}
- 网络:HTTP、TCP 远程处理信道(HTTP、TCP)和代理
HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://quitgame.cnblogs.com");
{
request.BeginGetResponse(
);
}
- 使用 ASP.NET 创建的 XML Web services
在WebService里面生命的方法XXX,VS.net都会在在客户端代理类中自动生成与之对应的三个方法:XXX/BeginXXX/EndXXX,我们调用BeginXXX即可完成异步操作。
当然,如果你愿意,你也可以在服务器端实现异步调用
服务器端异步方法介绍
值得一提的是,在ASP.Net2.0里面,WebService的异步模式已经变成了事件模式了,也就是 请求>>等待结束事件。对于WEB方法XX,Vs.Net 2005会生成XXAsync方法和XXCompleted事件,这在本质上同BeginXX,EndXX模式没有太大的区别,但是对于客户端程序编写者来说,编写异步操作代码变得更简单了---不需要同IAsync接口打交道了。请点此了解。
- ASP.NET Web 窗体
目前,ASP.Net中可以通过 AJAX 和 Atals (微软的AJAX实现)来实现异步WEB操作。
- MSMQ 上的对消息处理消息队列
利用 MSMQ(Microsoft Message Queue),应用程序开发人员可以通过发送和接收消息方便地与应用程序进行快速可靠的通信。消息处理为您提供了有保障的消息传递和执行许多业务处理的可靠的防故障方法。
MSMQ与XML Web Services和.Net Remoting一样,是一种分布式开发技术。但是在使用XML Web Services或.Net Remoting组件时,Client端需要和Server端实时交换信息,Server需要保持联机。MSMQ则可以在Server离线的情况下工作,将Message临时保存在Client端的消息队列中,以后联机时再发送到Server端处理。
显然,MSMQ不适合于Client需要Server端及时响应的这种情况,MSMQ以异步的方式和Server端交互,不
用担心等待Server端的长时间处理过程。
例子
- 异步委托
委托具有Invoke,BeginInvoke,EndInvoke方法。实例化一个委托以后,可以调用委托的 BeginInvoke 方法完成异步操作。
这是一个相当有用而且简单的方法,在执行自定义的操作的时候,使用异步委托是最优雅的方式---完全不需要自己管理线程资源。异步委托可以说是DotNet异步变成模式的核心,DotNet的很多内建的异步编程模式基本上都是通过异步委托来实现的。
关于异步委托可以参考MSDN文档
总结一下,异步调用的本质是利用新的线程完成工作,而免除对当前线程(一般是界面线程或主线程)的阻塞。借助于对ISynchronizeInvoke接口的实现,.net 对异步操作提供了完美的支持,正如其对反射提供的完美支持一样。



浙公网安备 33010602011771号