[转载]减少ASP.NET运行时的工作

mikel阅读(965)

[转载]减少ASP.NET运行时的工作 – 无风 – 博客园.

在底层,.NET和ASP.NET基础结构为了让开发的工作更轻松,做了大量的基础工作。虽然这个体系结构作为一个黑盒很不错,但有时了解一切如故无缝地 运行的实现细节也是很有益的,可以有效地使用运行时获得性能上的优势。

1、视图状态的优化

可以从不同的粒度控制网站的视图状态:在web.config设置EnableViewState为false禁用所有页面的 ViewState,Page的EnableViewState属性继承web.config的设置,如果要在单独的页面可以设置 EnableViewState。另外可以单独设置控件的EnableViewState。

web.config: <pages enableViewState=“false”/>
page:<%@ Page Language=”C#” CodeBehind=”Default.aspx.cs” Inherits=”Default” EnableViewState=”false”%>
控件:<asp:Label ID=“lbInfo” runat=“server” EnableViewState=“false” />

2、尽量减少服务器控件的数量

页面上服务器端的控件应该保持最低的数量上,服务器端的控件不仅生成视图状态,它们也需要额外的运行时处理来绑定到成员变量,呈现,然后卸载。能不用服务 器控件的尽量不用。

3、会话状态的考虑

在创建一个Web应用程序是一个重大的问题就是页面请求直接维护状态,因为http请求时无状态的。例如,知道客户在购物车中放置了什么产品。有很多机制 可以配合使用以取得状态的持久性:cookies,视图状态、QueryString,Session。

ASP.NET提供三种不同的会话状态提供者(In-Proc、状态服务器、数据库),极大地提高了会话功能。进程间会话状态提供者是这三者中最快的,状 态服务器虽然不如进程间的快,但可以在多个服务器器直接共享会话状态。数据库存储时最健壮的但最慢。

无论使用哪种会话状态提供者,都不要滥用会话功能,存储大量的数据或对象,“大量”的定义将取决于需要支持的用户的数量和应用程序的硬件。如果在有些页面 不使用Session可以禁用页面的Session,设置EnableSessionState为False,也可以稍稍减少页面出来的开销。

4、把异常减到最少

异常是通知程序执行期间发生的错误的一个非常有用的机制。和返回代码不同,程序不能忽略异常——它们将会改变程序的执行流。在性能和内存都很关键的应用程 序中经常避开以免额外的开销。在.NET运行时的托管环境中,用try和catch设置异常处理代码只对性能有可以忽略不计的影响,但如果有抛出异常性能 的损失就比较大。

5、使用早期绑定

前期绑定是在编译的时候就确定了要绑定的数据,前期绑定如果失败,会在编译时报编译错误,可以避免在运行时编译错误。而后期绑定是在运行的时候才填充数 据,后期绑定失败只有在运行时的时候才发生。

[转载]ASP.NET数据和事件回发机制

mikel阅读(1074)

[转载]数据和事件回发机制 – 清风飘过 – 博客园.

其实这块知识在ASP.NET 页面揭秘之页面生命周期都有提到过,但是并没有明确提出这个概念,在ASP.NET中通过视图机制实现两次页面请求之间的关联,视图区域信息存储 在页面上的一个隐藏字段,里面存储每次需要视图机制保存的一些信息,每次提交时,都会以“客户端ßà服务器端”形式来回一次,当处理完成后,最后会以处理 后的新结果作为新的视图信息存储到页面的隐藏字段,并与页面一起返回到客户端。

数据回发机制就是完成处理视图信息的功能。数据回发具体过程为,服务器端控件实现了IPostBackDataHandler接口,则当客户端提交 后,就会有机会利用IPostBackDataHandler接口的LoadPostData方法,在该方法内部处理子控件的新旧值逻辑,这时视图信息数 据以一个集合对象形式作为LoadPostData参数,并可以决定是否触发控件值变化后的事件。

IPostBackDataHandler接口包含LoadPostData(string postDataKey,NameValueCollection postCollection)和RaisePostDataChangeEvent()两个方法。LoadPostData用来检测提交给服务器数据, 根据控件状态数据和回发数据是否发生更改而判断是否调用RaisePostDataChangeEvent方法,如果为true,.NET Fromewrok会自动调用RaisePostDataChangeEvent方法,就是将新值和旧值比较,如果不一样就需要数据回发了。

事件回发

要控件捕获回发事件,则它必须实现IPostBackEventHandler接口。此接口可以使服务器控件上触发的事件响应来自客户端的回发。该 接口包含一个RaisePostBackEvent(string eventArgument)方法。

这里引用事 件揭秘里出现过的一个例子

//声明一个委托 [SerializableAttribute] [ComVisibleAttribute(true)] public delegate void EventHandler (Object sender,EventArgs e) [DefaultEvent("Click")] [toolboxData("<{0}:PostBackEventControl runat=server></{0}:PostBackEventControl>")] public class PostBackEventControl:Control,IPostBackEventHandler { //事件名称 编译器其实这里实行3个构造 public event EventHanlder Click; //定义一个负责引发事件的方法来通知已订阅事件的对象事件已经发生 protected virtual void OnClick(EventArgs e) { if(Click!=null) { //是通知订阅对象(其实这里就是调用了服务器一个方法) Click(this.e); } } //触发事件 public void RaisePostBackEvent(string eventArgument) { //因为这里没有额外的信息传递所以赋空值 OnClick(EventArgs.Empty); } Protected overrrid void Render(HtmlTextWriter output) { output.Write("<INPUT TYPE=submit name="+this.UniqueID+"Value='单击我'/>"); } }

在来看看该控件,按钮类型为:submit,其本身就可以提交事件到服务器,但是该控件还是不能捕捉到该按钮事件。它必须具备两个条件1.继承 IPostBackEventHandler接口并实现接口中的方法。2。name值设置为 UniqueID。

“[DefaultEvent(“Click”)]”的功能是定义Click事件为默认事件。定义了默认事件,在设计器中双击控件就会从.aspx 页切到.cs页,并可以自动注册默认事件

将事件与处理事件的方法相关联

This.button1.Clcik+=new EventHandler(Button1_Click);

这里可以参考委 托揭秘

将对于不引起回发的HTML元素启动回发可以有以下几种方法

1.使用GetPostBackEventReference和GetPostBackClientHyperlink

将上面代码中TYPE=submit修改为button那么此按钮就不在具有回发到服务器的功能。那么此时如果使用 GetPostBackEventReference就可以回发到服务器了。

这里介绍其中一个重载方法

public String GetPostBackEventReference(Control control,String argument)
前一个参数为一个控件,后一个可以是附加信息

修正以后给出代码

Output.Write(“<INPUT name=”+this.UniqueID+”Value=’单击我’ onclick=”+Page.ClientScript.GetPostBackEventReference(this,””));

这里ClientScript暂不做介绍。

从上面最终输出地HTML代码可以看到多了这么一个_doPostBack方法. eventTarget就是GetPostBackEventReference的第一参数,这里指当前控件,eventArgument就是 GetPostBackEventReference的第二参数,为空。注意到最后提交窗体 “theForm.submit();”就实现了我们想要的回发。

function __doPostBack(eventTarget, eventArgument) {

if (!theForm.onsubmit || (theForm.onsubmit() != false)) {

theForm.__EVENTTARGET.value = eventTarget;

theForm.__EVENTARGUMENT.value = eventArgument;

theForm.submit();

}

2.GetPostBackClientHyperlink

Output.Write(“<a herf=”+ Page.ClientScript. GetPostBackClientHyperlink (this,””)+”>点击我</a>”);

也上产生与上面相同的效应。

如果我们要把服务器某些控件(必须实现IPostBackDataHandler接口)处理回发,那么可以通过 Page.RegisterRequiresPostBack实现。例

TextBox txt=new TextBox();

Protect void Page_PreRender(object sender,EventArgs e)

{

This.Page. RegisterRequiresPostBack(txt);

}

[转载]基于stratus +flex+MySQL的简易在线随机视频聊天室的开发

mikel阅读(1030)

[转载]基于stratus +flex+MySQL的简易在线随机视频聊天室的开发 – 疯狂的八神庵 – 博客园.

前段时间听说flashplayer已经开始支持p2p了,对这块非常感兴趣于是开始玩flex,一玩下来不可自拔。用Stratus搭建p2p环 境如此简单,双方只需要能连上Stratus服务器就能直接进行语音视频的聊天,不需要任何客户端。Adobe还真是NB,呵呵出了这么XX的东东,前途 无可限量啊。

闲话少说,这几天小搞了下,用MySQL和flex弄了个简易随机视频聊天室,和有共同爱好的各位一起分享下。高手可以直接飞过哈,欢迎提出意见, 毕竟刚玩没多久,问题很多。

下面正式开始,程序主体主要分为3大块:

一、p2p语音视频功能模块

这个模块网上有很多教程了,Adobe官方的那个Sample就很 好。我就是以此为基础进行开发的。可能有些朋友还不是很了解,为了每个人都能搞清楚,下面针对代码详细地进行下介绍。

由于是p2p模式,每个用户既是呼叫者又是被呼叫者。更具体点,举个例子有两个人A和B打电话。A呼叫B,此时A是呼叫者,B是被呼叫者。反之B是 呼叫者,A是被呼叫者。因此在每个p2p模块中必须要有呼叫者和被呼叫者两个部分。

被呼叫部分:

001 //被呼叫 者发布监听流,以便呼叫者连接
002 listenStream = new NetStream(netConnection,NetStream.DIRECT_CONNECTIONS);
003 listenStream.addEventListener(NetStatusEvent.NET_STATUS,statusHandler);
004 listenStream.publish(username);
005 //呼叫者一旦订阅上面的监听 流,就会触发onPeerConnect事件
006 var c:Object =new Object();
007 c.onPeerConnect = function(caller:NetStream):Boolean
008 {
009 if(callState == CallReady)
010 {
011
012 //接受呼叫者发布的视频语音流
013 callState = CallRinging;
014 idManager.change(callState);
015 busyState = busyOn;
016 incomingStream = new NetStream(netConnection,caller.farID);
017 incomingStream.addEventListener(NetStatusEvent.NET_STATUS,statusHandler);
018 video =new Video();
019 video.attachNetStream(incomingStream);
020 remoteVideoDisplay.addChild(video);
021 incomingStream.play("caller");
022
023 var st:SoundTransform = new SoundTransform(speakerVolumeSlider.value);
024 incomingStream.soundTransform = st;
025 //处理呼叫的方法
026 var i:Object = new Object();
027 i.onIncomingCall = function(caller:String):void
028 {
029 if(callState != CallRinging)
030 {
031 txtInfo.text += "onIncomingCall: Wrong call state: " + callState + "\n";
032 return;
033 }
034 send_bn.enabled=true;
035 txtInfo.text +=  caller + "已经成功与您连接上\n";
036 partnername = caller;
037
038 //outgoingStream.send("onCallAccepted", username);
039 callState = CallEstablished;
040 //idManager.change(callState);
041 }
042 i.onIm = function(caller:String,text:String):void
043 {
044 txtMessage.text += caller+": "+text+"\n";
045 }
046 i.onDisconnected = function(caller:String):void
047 {
048 txtInfo.text += caller+"和你断开连接\n";
049 send_bn.enabled=false;
050 stop();
051 }
052 incomingStream.client = i;
053 //对呼叫者发布自己的语音视频流
054 outgoingStream = new NetStream(netConnection,NetStream.DIRECT_CONNECTIONS);
055 outgoingStream.addEventListener(NetStatusEvent.NET_STATUS,callee_outgoingStreamHandler);
056 outgoingStream.attachCamera(camera);
057 outgoingStream.attachAudio(mic);
058 outgoingStream.publish("callee");
059
060 return true;
061 }
062 txtInfo.text += "onPeerConnect: all rejected due to state: " + callState + "\n";
063
064 return false;
065 }
066 listenStream.client = c;
067
068
069
070 callState = CallReady;
071
072 呼叫部分:
073
074 callState = CallCalling;
075 idManager.change(callState);
076 busyState = busyOn;
077 //播放被呼叫者的监听流
078 controlStream = new NetStream(netConnection,farPeerID);
079 controlStream.addEventListener(NetStatusEvent.NET_STATUS,statusHandler);
080 controlStream.play(partnername);
081 //对被呼叫者发布自己的视频语音流
082 outgoingStream = new NetStream(netConnection,NetStream.DIRECT_CONNECTIONS);
083 outgoingStream.addEventListener(NetStatusEvent.NET_STATUS,caller_outgoingStreamHandler);
084 outgoingStream.attachCamera(camera);
085 outgoingStream.attachAudio(mic);
086 outgoingStream.publish("caller");
087 txtInfo.text += "正在与"+partnername+"建立连接\n";
088 //播放被呼叫者的视频语音流
089 incomingStream = new NetStream(netConnection,farPeerID);
090 incomingStream.addEventListener(NetStatusEvent.NET_STATUS,statusHandler);
091 video =new Video();
092 video.attachNetStream(incomingStream);
093 remoteVideoDisplay.addChild(video);
094 incomingStream.play("callee");
095
096 var st:SoundTransform = new SoundTransform(speakerVolumeSlider.value);
097 incomingStream.soundTransform = st;
098 //处理呼叫的方法
099 var i:Object = new Object();
100 i.onIm = function(callee:String,text:String):void
101 {
102 txtMessage.text += callee+": "+text+"\n";
103 }
104 i.onCallAccepted = function(callee:String):void
105 {
106 if (callState != CallCalling)
107 {
108 txtInfo.text += "连接失 败";
109 return;
110 }
111 send_bn.enabled=true;
112 txtInfo.text += callee+"已经成功与您连接上\n";
113 callState = CallEstablished;
114 //idManager.change(callState);
115 }
116 i.onDisconnected = function(callee:String):void
117 {
118 txtInfo.text += callee+"和你断开连接\n";
119 send_bn.enabled=false;
120 stop();
121 }
122 incomingStream.client = i;

二、用户数据库及webservice接口

这个项目我使用MySQL进行用户数据库设计,用户端产生相应行为后(如登录,下线,聊天,空闲等)会在数据库中更新自己的状态,其他用户根据查询得到的 结果进行操作。数据库结构见下图:

用户 名           peerID        更新时间              在线状态    聊天状态

该项目只要进行简单的数据库操作就可以了,所以我使用了HTTPService组件和一个简单的php网页进行与数据库的交互。

HTTPService组件位于mx.rpc.http包下,当调用 HTTPService 对象的 send() 方法时,将发出对指定 URL 的 HTTP 请求,并且返回 HTTP 响应。可以选择向指定 URL 传递参数。项目中通过HTTPService组件传递用户端的参数到php网页,由php根据相应的参数操作数据库,返回相应的结果交由用户端执行。

HTTPService组件部分:

1 mHttpService = new HTTPService();
2 mHttpService.url = mWebServiceUrl;
01 //添加对结果时间的侦听
02 mHttpService.addEventListener("result", httpResult);
03 mHttpService.addEventListener("fault", httpFault);
04 //实例化request请求对象并发送
05 var request:Object = new Object();
06 var now:Date = new Date();
07 request.time = now.getTime();
08 request.username = user;
09 request.identity = id;
10 request.online = x;
11 request.busy = x;
12 mHttpService.cancel();
13 mHttpService.send(request);

php网页部分:

01 <?php
02 $output = "<?xml version=\"1.0\" encoding=\"utf-8\"?>";
03 $output .= "<result>";
04 if ( isset($_GET["username"]) || isset($_GET["friends"]) || isset($_GET["fetch"]))
05 {
06 define( "DATABASE_SERVER", "localhost" );
07 define( "DATABASE_USERNAME", "root" );
08 define( "DATABASE_PASSWORD", "root");
09 define( "DATABASE_NAME", "root" );
10 $mysql = mysql_connect( DATABASE_SERVER,
11 DATABASE_USERNAME,
12 DATABASE_PASSWORD )
13 or die( mysql_error() );
14 mysql_select_db( DATABASE_NAME );
15 }
16 //注册更新部分
17 if ( isset($_GET[ "username" ]) )
18 {
19
20 $user   = mysql_real_escape_string( $_GET[ "username" ] );
21 $identity   = mysql_real_escape_string( $_GET[ "identity" ] );
22 $online = mysql_real_escape_string( $_GET[ "online" ] );
23 $busy = mysql_real_escape_string( $_GET[ "busy" ] );
24
25 $query = "SELECT * FROM registrations WHERE m_username='$user' ;";
26 $result = mysql_query( $query );
27
28 $isNew = ($result && mysql_num_rows($result) == 0);
29 if( $isNew ) {
30 $query = "INSERT INTO registrations SET ";
31 $query .= "m_username='$user', m_identity='$identity', m_updatetime=now(), m_online='$online', m_busy='$busy';";
32 } else {
33 $query = "UPDATE registrations SET ";
34 $query .= " m_identity='$identity', m_updatetime=now(), m_online='$online', m_busy='$busy' where m_username='$user';";
35 }
36 $result = mysql_query( $query );
37 if( $isNew )
38 $output .= "<update>true</update>";
39 else
40 $output .= "<update>false</update>";
41 }
01 //查找用户部分
02 if ( isset($_GET[ "friends" ]) )
03 {
04 $friends    = mysql_real_escape_string( $_GET[ "friends" ] );
05 $output .= "<friend><user>$friends</user>";
06 $query = "select * from registrations where m_username = '$friends' and TIMEDIFF(now(),m_updatetime) >0 ;";
07
08 $result = mysql_query( $query );
09 if( $result ) {
10 while( $item = mysql_fetch_assoc( $result ) ) {
11 $output .= "<identity>".$item["m_identity"]."</identity>";
12 }
13 }
14 $output .= "</friend>";
15 }
01 //获取返回结果
02 if ( isset( $_GET["fetch"]) )
03 {
04 $time = mysql_real_escape_string( $_GET[ "time" ] );
05 $query = "select m_number from registrations order by m_username asc ;" ;
06
07 $result =mysql_query($query);
08 if( $result )
09 {
10 $num = mysql_num_rows($result);
11 $output .= "<fetch><num>".$num."</num>";
12 }
13
14 $query = "select m_username from registrations where m_online ='1' and m_busy = '0' order by m_username asc ;";
15 $result = mysql_query($query);
16
17 if( $result )
18 {
19 $onlinenum = mysql_num_rows($result);
20 $output .="<onlinenum>".$onlinenum."</onlinenum><users>";
21 $i = 1;
22 while ($row = mysql_fetch_row($result))
23 {
24 $output .= "<user".$i.">".$row[0]."</user".$i.">";
25 $i++;
26 }
27 $output .= "</users></fetch>";";
28 }
29 }
30
31
32
33
34 $output .= "</result>";
35
36 header( "Content-Type: text/xml" );
37 echo $output;
38 ?>

三、主程序部分

主程序界面实时显示在线用户状态,可以双击列表空闲用户进行聊天 也可以点击随机按钮随机选取在线空闲用户进行聊天。

退出时记得点击离开正常退出哈。

Demo:http://p2pchat.online.cm/

这个免费空间貌似有广告,不点它就是了

有问题欢迎交流-_-

[转载]Delphi组件开发教程指南目录

mikel阅读(1053)

[转载]Delphi组件开发教程指南目录 – 得闲笔记 – 博客园.

用Delphi开发的时间也不短了!刚接触Delphi的时候,就觉得组件这个东西非常方便!根本不必知道组件内部的作为,只要使用就好了!然而用的多 了,也不免会对各种delphi组件的内部实现方式产生兴趣!也不知道从什么时候开始,开始开发delphi的控件,这也得归功于我所在的公司了,由于老 板的信任,我得以开发各种公司内部需要的控件,期间也积累了一部分的开发经验。在这个Delphi日渐凋零的时刻,于是我在心里便想,用了这么长时间的 Delphi,也用来混了好几年的饭钱了,同时也不断的在网络上汲取了各种各样的高手的代码与经验,该是时刻回报一下网络的Delphi界同胞,让更多人 参与到这个行列中来!

虽说,题目是叫教程指南,其实也仅仅作为一个学习心得交流,提供给暂时只会使用托控件进行Delphi应用程序开发的,而又想学习一下这方面知识的人,一 个摸索的捷径!高手或可对内容不屑,飘过就可,各种代码仅供初学者入门,所以一定会尽量就简,同时尽量将我所熟悉的各种Windows消息以及触发条件和 消息过程都简述出来!

本系列教程完全为本人原创,本人享有示例源码的所有 权并与博客园共同享有该教程版权,请在本人许可的前提下进行相关使用,否则保留追究法律责任的权利。

教程目录:

Delphi组件开发教程指南(1)组件开发概述

Delphi 组件开发教程指南(2)简单扩充TEdit

Delphi 组件开发教程指南(3)组件注册

Delphi 组件开发教程指南(4)组件生成过程(针对TWinControl继承而来的组件)

Delphi 组件开发教程指南(5)实现类QQ的编辑框

作者:不得闲
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必 须保留此段声明,且在文章页面明显位置给出原
文连接,否则保留追究法律责任的权利。

[转载](推荐)asp.net mvc强大的分页控件MvcPager

mikel阅读(946)

[转载](推荐)asp.net mvc强大的分页控件MvcPager – longgel – 博客园.

不管使用什么来开发程序,都少不了需要做分页,目前使用ASP.NET mvc开发的web应用程序的人越来越多了,相应的辅助,提高开发效率的helper方法、控件都将会出现。今天我向大家介绍一款在ASP.NET mvc框架中使用的分页控件。本人也是在新年的时候初次使用,但是一直没有推荐给大家。利用五一休息时间也就介绍给大家。声明我与博杨计算机有限公司无任 何关系只是觉得这个分页控件很好用,并且功能强大,在这里是真的只为介绍这款分页控件。

另外下载地址:http://mvcpager.codeplex.com/releases/view/42912

另外还有相关使用的DEMO也很丰富,上面的网址也可以下载非常完整的DEMO。

DEMO里有使用各种在ASP.NET MVC中提倡的方式来实现分页功能,分别有:

1.标准的Url分页 在线演示效果请看:http://www.webdiyer.com/Controls/MvcPager/Demo

2.JQuery ajax分页 在线演示效果请看:http://www.webdiyer.com/Controls/MvcPager/AjaxDemo

3.Microsoft Ajax分页 在线演示效果请看:http://www.webdiyer.com/Controls/MvcPager/MicrosoftAjaxDemo

4.使用DataRow集合分页 在线演示效果请看:http://www.webdiyer.com/Controls/MvcPager/DataRowsPaging

5.ASP.NET MVC Pager分页控件用户自定义信息 在线演示效果请看:http://www.webdiyer.com/Controls/MvcPager/CustomInfo

6.MvcPager应用CSS样式 在线演示效果请看:http://www.webdiyer.com/Controls/MvcPager/ApplyStyles

7.保持Url参数(分页恢复Url参数) 在线演示效果请看:http://www.webdiyer.com/Controls/MvcPager/UrlParameters?year=2009&month=12&author=webdiyer

8.ajax正在加载效果分页 在线演示效果请看:http://www.webdiyer.com/Controls/MvcPager/Loading

9.使用自定义路由表实现分页 在线演示效果请看:http://www.webdiyer.com/Controls/MvcPager/CustomRouteTable/employee_1/page_1

10.MvcPager使用存储过程分页 http://www.webdiyer.com/Controls/MvcPager/StoredProcedure

11.ASP.NET MvcPager Ajax产生异常处理分页 http://www.webdiyer.com/Controls/MvcPager/AjaxEvents

12.MvcPager使用输入或选择页索引并跳转实现分页 http://www.webdiyer.com/Controls/MvcPager/PageIndexBox

大家可以根据自己的实际情况选择使用那种分页方式或者同时使用几种分页方式。

MvcPager的介绍

MvcPager分页控件是在ASP.NET MVC Web应用程序中实现分页功能的一系列扩展方法,该分页控件的最初的实现方法借鉴了网上流行的部分源代码,尤其是ScottGu的 PagedList<T>类和相关方法,经过站长不断完善和改进后推出的比较成熟而且功能强大的ASP.NET MVC分页解决方案。

MvcPager主要功能有:

  1. 实现最基本的url route分页;
  2. 支持手工输入或选择页索引并对输入的页索引进行有效性验证;
  3. 支持使用ASP.NET MVC Ajax默认的MicrosoftAjax和MicrosoftMvcAjax客户端脚本库实现Ajax分页;
  4. 支持使用JQuery实现Ajax分页,生成的Html代码更精简;
  5. 支持Ajax分页模式下,若客户端浏览器不支持或禁用JavaScript功能时安全降级为普通分页
  6. 搜索引擎友好,无论是普通分页还是Ajax分页,搜索引擎都可以直接搜索到所有页面。
  7. 支持ASP.NET MVC 1.0和最新的ASP.NET MVC 2.0 版本;
  8. 支持IE、Firefox、Opera、Chrome及Safari等常用浏览器;

MvcPager分页控件仍在不断开发完善中,更多功能将在以后的升级版本中得到增强和完善,希望您在使用过程中提出您的宝贵意见和建议!

所有中文资料都在http://www.webdiyer.com/Controls/MvcPager

[转载]WF4.0实战(十二):ASP.NET MVC2.0结合WF4.0实现用户多步注册流程

mikel阅读(831)

[转载]WF4.0实战(十二):ASP.NET MVC2.0结合WF4.0实现用户多步注册流程 – 海纳百川 – 博客园.

这篇文章结合ASP.NET MVC和WF4.0实现一个简单的用户多步注册。用户注册分了四步骤。流程图如下:

zhuche

第一步:填写个人信息:

step1

第二步:填写职位信息:

step2

第三步:填写学历信息:

step3

第四步:填写联系信息:

step4

第五步骤:完成

step5

WF4.0状态机如下图:

step6

每一步点击Next跳到下一步,点击Back回到上一步。

实现:

第一步:新建一个ASP.NET MVC Application和一个Workflow的ActivityDesignerLibrary项目,在mvc项目的Model文件夹下添加一个 User。代码如下:

1 public class User
2 { //个人信息
3 [Required(ErrorMessage = 姓名不能为空)]
4 [StringLength(20, ErrorMessage = 姓名长度不能超过20个字符)]
5 public string Name { get; set; }
6 public int? Age { get; set; }
7 //职位信息
8 [Required(ErrorMessage = 职位不能为空)]
9 public string Post { get; set; }
10 public int? Salary { get; set; }
11 //学历信息
12 [Required(ErrorMessage = 毕业院校不能为空)]
13 public string University { get; set; }
14 public int? GraduationYear { get; set; }
15 //联系信息
16 [Required(ErrorMessage = 邮件不能为空)]
17 [RegularExpression(@”^[a-z][a-z|0-9|]*([_][a-z|0-9]+)*([.][a-z| + @”0-9]+([_][a-z|0-9]+)*)?@[a-z][a-z|0-9|]*\.([a-z] + @”[a-z|0-9]*(\.[a-z][a-z|0-9]*)?)$, ErrorMessage= 邮件格式不正确)]
18 public string Email { get; set; }
19 public int? Mobile { get; set; }
20 }
21

第二步:在Controllers中添加一个控制类,代码如下:

1 public class UserController : Controller
2 {
3 //
4 // GET: /User/
5 #region Declarations
6 static WorkflowUtil wrkFlw = null;
7 string page=Step1;
8 public User userObj;
9 #endregion
10 #region Process action method
11 public ActionResult Process(string nextButton, string backButton)
12 {
13 if (wrkFlw == null )
14 {
15 wrkFlw = new WorkflowUtil();
16 }
17 if ((nextButton != null))
18 {
19 page = wrkFlw.RunWorkflow(Next);
20 return View(page, userObj);
21 }
22 else if (backButton != null)
23 {
24 ModelState.Clear();
25 page = wrkFlw.RunWorkflow(Prev);
26 return View(page, userObj);
27 }
28 else
29 return View(page, userObj);
30 }
31 #endregion
32 #region Events
33 protected override void OnActionExecuting(ActionExecutingContext filterContext)
34 {
35 userObj = (User)TempData[User];
36 if (userObj == null) userObj = new User();
37 TryUpdateModel(userObj);
38 if (TempData[CurrentPage] != null) page = TempData[CurrentPage].ToString();
39 }
40 protected override void OnResultExecuted(ResultExecutedContext filterContext)
41 {
42 TempData[User] = userObj;
43 TempData[CurrentPage] = page;
44 }
45 #endregion
46 public ActionResult Index()
47 {
48 return View();
49 }
50 }

第三步:在方法Process上选择添加视图,如下图选择:

step11

如此共生成五个view页面:step1.step2,step3,step4,step5,Final。

第四步:设计状态机工作流。这里只演示step1的设置,如下图:

step1111

step11122

总结:使用WF完美结合ASP.NET mvc实现这个功能。

代码:/Files/zhuqil/mvc.rar

作者:朱祁林
出处:http://zhuqil.cnblogs.com
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

[转载]StringTemplate.Net 学习笔记(总结)

mikel阅读(877)

[转载]StringTemplate.Net 学习笔记(总结) – 囧月 – 博客园.

这是我第一次接触模板引擎,也是第一次这么认真的去写一个系列,ST有些概念跟实际的编程语言差不多,通过对ST学 习,对编程语言也有了一点新的认识。

StringTemplate.Net 学习笔记(1):开篇

StringTemplate.Net 学习笔记(2):语法介绍(表格)

StringTemplate.Net 学习笔记(3):表达式元素语法(上)

StringTemplate.Net 学习笔记(4):表达式元素语法(下)

StringTemplate.Net 学习笔记(5):条件声明

StringTemplate.Net 学习笔记(6):自定义输出格式

StringTemplate.Net 学习笔记(7):加载模板文件

StringTemplate.Net 学习笔记(8):加载模板组文件

StringTemplate.Net 学习笔记(9):深入了解模板组文件

StringTemplate.Net 学习笔记(10):模板组继承及模板组接口

StringTemplate.Net 学习笔记(11):自定义错误处理

由于ST资料比较少,所以基本上都是参照http://www.antlr.org/wiki/display/ST/StringTemplate+Documentation以 及源代码,在对官方文档(话说官方文档也有点古老)不断印证过程中,对文章进行修正、补充。

水平所限,很多地方表达的不到位,也因此贴 了许多代码-_-。

作者:囧月
出处:http://lwme.cnblogs.com/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

[转载]用SQL做单位换算

mikel阅读(1344)

[转载][原]用SQL做单位换算 – killkill – 博客园.

pumeifen朋友在首页提出了一个问题“SQL 问题 求解”,我对这个问题延伸一下描述为“用数据库来做单位换算”,以长度单位为例,常用的长度单位 有:毫米、厘米、分米等等,而英制的长度单位有英寸、英尺、码等,而我国传统的长度单位也有寸、尺、丈等等。

小学时学习单位换算的基本 算法都是将他们转换成同一个长度单位然后再换算的,我借鉴这个算法写下此文。

首先在Oracle中构建一个长度单位的转换表:

1 create table Length
2 (
3 name varchar2(50),
4 ratio number(10,5) not null,
5 parent varchar2(50),
6 constraints pk_length primary key (name)
7 )

插入测试数据:

01 insert into Length
02 ---- 国际标准的长度单位
03 select '毫米' ,1.0 ,null from dual union all
04 select '厘米' ,10.0 ,'毫米' from dual union all
05 select '分米' ,10.0 ,'厘米' from dual union all
06 select '米' ,10.0 ,'分米' from dual union all
07 select '千米' ,1000.0 ,'米' from dual union all
08 ---- 我国传统的长度单位
09 select '寸' ,3.33,'厘米' from dual union all
10 select '尺' ,10.0 ,'寸' from dual union all
11 select '丈' ,10.0 ,'尺' from dual union all
12 ---- 少数欧美国家使用的英制长度单位
13 select '英寸' , 2.54 , '厘米' from dual union all
14 select '英尺' , 12.0  , '英寸' from dual union all
15 select '码' , 3.0  , '英尺' from dual union all
16 select '浪' , 220.0  , '码' from dual union all
17 select '英寻' , 2.0  , '码' from dual union all
18 select '英里' , 1760.0  , '码' from dual

这个Length表实际上保存的是一棵树,简单表示如下:

image

如果,我想知道1英里=?千米,Oracle可以使用connect by …. start with 分别进行查询得从千米和英里到毫米之间的路径:

01 SELECT name,ratio,parent
02 FROM length
03 CONNECT BY nocycle PRIOR parent = name
04 START WITH name = '千米'
05 union all
06 select null,null,null from dual
07 union all
08 SELECT name,ratio,parent
09 FROM length
10 CONNECT BY nocycle PRIOR parent = name
11 START WITH name = '英里'
01 NAME            RATIO PARENT
02 ---------- ---------- ----------
03 千米             1000 米
04 米                 10 分米
05 分米               10 厘米
06 厘米               10 毫米
07 毫米                1
08
09 英里             1760 码
10 码                  3 英尺
11 英尺               12 英寸
12 英寸             2.54 厘米
13 厘米               10 毫米
14 毫米                1

接下来只需要将千米和英里转换成毫米就可求比率了,SQL语句如下:

01 select
02 ( --英里转化成毫米
03 select exp(sum(ln(ratio)))
04 FROM length
05 CONNECT BY nocycle PRIOR parent = name
06 START WITH name = '英里'
07 )/( --千米转化成毫米
08 select exp(sum(ln(ratio)))
09 FROM length
10 CONNECT BY nocycle PRIOR parent = name
11 START WITH name = '千米'
12 ) "英里:千米"
13 from dual;
1 英里:千米
2 ----------
3 1.609344

这里有个小插曲,SQL中没有计算累积的聚合函数,需要变换一下才能算到累积的结果,详细可以看我写的一篇博文[原] 计算乘积的聚合函数跑哪去了呢?

在SQL Server 2005/2008中,可以使用CTE的语法,以下省略在SQL Server中创建测试表Length的过程。

01 WITH LengthTree
02 as
03 (
04 select name,ratio,parent,0 as level from Length
05 where name = '千米'
06 union all
07 select l.name,l.ratio,l.parent,level+1
08 from LengthTree t
09 inner join Length l
10 ON l.name=t.parent
11 )
12 select * from LengthTree

image

稍微变换一下,我将英里、千米到毫米之间的路径列出来:

01 ;WITH LengthTree
02 as
03 (
04 select name,ratio,parent,0 as level,name as start from Length
05 where name in ( '千米' , '英里' )
06 union all
07 select l.name,l.ratio,l.parent,level+1,t.start
08 from LengthTree t
09 inner join Length l
10 ON l.name=t.parent
11 )
12 select * from LengthTree
13 order by start,level

image

最后,就是再变换一下求解英里和千米之间的比率啦,SQL 如下:

01 ;WITH LengthTree
02 as
03 (
04 select name,ratio,parent,0 as level,name as start from Length
05 where name in ( '千米' , '英里' )
06 union all
07 select l.name,l.ratio,l.parent,level+1,t.start
08 from LengthTree t
09 inner join Length l
10 ON l.name=t.parent
11 )
12 select (
13 select exp(sum(log(ratio))) from LengthTree where start='英里'
14 )/(
15 select exp(sum(log(ratio))) from LengthTree where start='千米'
16 ) as "英里:千米"

image

看到这里,可能有朋友会问,如果我求1英里=?浪,比较好的做法是都转换成码然后再做运算,但是按照这个算法英里和浪都会最终转换成毫米再进行运 算,中间极有可能产生精度问题,而且运算量明显多很多。

是的,解决运算量的重点在于找到“千米路径”和“英里路径”的相交点,两条路径一旦相交,再往根节点“毫米”走下去的路径都是多余的(沿用上文,从 “千米”到“毫米”的路径称为“千米路径”,从“英里”到“毫米”的路径称为“英里路径”)。

虽然,通过集合的并、交、差可剔除多余的路径,但SQL变得非常长篇累赘,而且需要读取的块/页数不见减少,于是作罢,如果大家有兴趣,可以自己写 写,希望您有更好的解决方法。至于精度问题,我的意见是,不要偏太多就行了…….

希望本文对您有帮助。

长度单位主要参考百度百科的长度单位wiki 的英制单位

[转载]Delphi 组件开发教程指南(4)组件生成过程(针对TWinControl继承而来的组件)

mikel阅读(911)

[转载]Delphi 组件开发教程指南(4)组件生成过程(针对TWinControl继承而来的组件) – 得闲笔记 – 博客园.

还记得在第二章的时候,我用到了procedure CreateParams(var Params: TCreateParams);这个函数的吧!为什么我会使用这个函数来实现那个对齐的问题呢!现在就来追根底的来看看!这个过程其实是在构建窗口的时候 会调用的,当然我说的这个是针对TWincontrol继承过来的组件说的,从TGraphicontrol等继承过来的是没有这个的。这个函数的产生也 是Windows组件库所特有的,如果列为看官有Windows编程的基础,那么这个就很容易理解了,记得,在Windows编程的时,注册这个窗口类之 前,我们都会为一个窗口类指定一系列的参数,而这个CreateParams函数就是产生在这个注册过程之前,目的是用来为创建过程指定参数。

在讲CreateParams的来源之前,我们必须简略说说组件由生成到显示在用户面前的这个过程。这是个灰常纠结的问题,纠结到我不晓得怎么去说(当然 纠结的主要原因还是本人的水平有限,下面大家就简单看看吧,解说可能有错,欢迎指正)。由于组件都是依托于Form之上的,所以组件要显示出来最首要的是 要组件所依托的容器显示出来,那么最首要,我们需要看看Form的创建然后显示出来的过程。至于窗口的创建过程可以参考一下黄 叉叉的博客,在这里我在给他的细化一下,便于我们的工作的展开!这个细化应该是在他那个说明的第5步之前,也就是他说的

此处说明一下:


对 TWinControl 的 Handle 属性的读取会触发 TWinControl.GetHandle;

可以察看 Property Handle; 的声明。


5、第四步中对 Handle 进行读取,触发下述序列:(TWinControl)

Handle->GetHandle->HandleNeeded

这个HandleNeeded是在什么时候第一次调用的,其实他不是在GetHandle的时候第一次调用的,而是在窗口显示出来之前,也就是 Visible变化的过程中第一次调用的,而这个Visible的变化,是在Delphi读取Form资源文件的属性了之后触发(这个属性读取过程,可以 参考Delphi 的持续机制浅探)。我们看看Visible这个属性变化所触发的过程,这个属性定义在TControl中,属性变化对应的过程为

procedure TControl.SetVisible(Value: Boolean); begin if FVisible <> Value then begin VisibleChanging; FVisible := Value; Perform(CM_VISIBLECHANGED, Ord(Value), 0); RequestAlign; end; end;

由此可以看到在属性变化的时候发送了一个CM_VISIBLECHANGE的消息出去,然后我们再去这个消息的触发过程

procedure TWinControl.CMVisibleChanged(var Message: TMessage); begin if not FVisible and (Parent <> nil) then RemoveFocus(False); if not (csDesigning in ComponentState) or (csNoDesignVisible in ControlStyle) then UpdateControlState; end;

本过程在TControl中也有,但是在TWinControl中被重写了,所以我这里只列出了TWinControl的,在Visible变化的时候会 调用UpdateControlState函数来更新控件状态,然后这个更新过程中调用了另外一个更新控件显示的函数UpdateShowing,我们来 看看UpdateShowing这个过程

procedure TWinControl.UpdateShowing; var ShowControl: Boolean; I: Integer; begin ShowControl := (FVisible and (not (csDesigning in ComponentState) or not (csDesignerHide in ControlState)) or ((csDesigning in ComponentState) and not (csDesignerHide in ControlState)) and not (csNoDesignVisible in ControlStyle)) and not (csReadingState in ControlState) and not (csDestroying in ComponentState); if ShowControl then begin //这个时候如果是第一次显示,FHandle为0,就会调用CreateHandle来创建一个窗口句柄了,也就是说 //在这个时候才真真实实的创建Windows的标准控件! if FHandle = 0 then CreateHandle; if FWinControls <> nil then for I := 0 to FWinControls.Count - 1 do TWinControl(FWinControls[I]).UpdateShowing;//之后会更新属于这个控件容器的所有子控件显示 end; if FHandle <> 0 then if FShowing <> ShowControl then begin FShowing := ShowControl; try SetPerformingShowingChanged(Self); try Perform(CM_SHOWINGCHANGED, 0, 0); finally ClearPerformingShowingChanged(Self); end; except FShowing := not ShowControl; raise; end; end; end;

CreateHandle过程中调用了CreateWnd,然后CreateWnd得时候就调用我们上面声明的CreateParams来为标准控件传递 参数。上面说了控件的最终容器Form的创建到显示过程,那么我们现在再来说说一般控件的创建显示过程,其实也就和TForm的创建显示过程一样!只是 TForm的显示从读取了属性之后触发,而一般控件由他所在的容器触发,也就是上面的UpdateShowing过程中的实现过程,后面会遍历子控件,然 后更新他们的显示,第一次显示的时候都会触发CreateHandle的过程,所以Windows组件的真实创建过程实际上应该是在组件的第一次显示的过 程中创建,而不是我们调用Create的时候,在Delphi中,我们Create的时候,仅仅是为这个组件提供了一些初始化信息以及各种参数而已。说到 这里,那么,第二章中的CreateParams的实现方法也就相当顺其自然了,因为在CreateParams中为Edit指定其他的扩展样式时,实际 上Windows的真实Edit控件实际上还没有创建出来。那么当指定了新样式,当他创建出来的时候,就自然具备了我们指定的扩展样式了。然后,我在设置 新样式的时候,调用了一个RecreateWnd的方法,这个方法的目的是重建句柄,也就是重建Windows组件,这个函数的实现过程相当简单,仅仅就 是发送了一个组件重建的消息CM_RECREATEWND,然后我们看看这个消息过程的实现方法

procedure TWinControl.CMRecreateWnd(var Message: TMessage); var WasFocused: Boolean; begin WasFocused := Focused;//先保存控件是否是焦点状态 UpdateRecreatingFlag(True);//这个函数,我就不贴他的代码了,从他的代码中,我们可以看出来,这个函数 //的目的是为所有的子控件打上重建的标记 try DestroyHandle;//释放句柄,同时释放所有子控件的句柄 UpdateControlState;//更新控件状态,这个函数上面已经分析,会建立句柄,同时子控件句柄。 finally UpdateRecreatingFlag(False);//重建状态完成 end; if WasFocused and (FHandle <> 0) then Windows.SetFocus(FHandle);//如果重建成功,并且原先有焦点,就恢复原先的焦点状态 end;

可见,这个重建的过程,如果你是一个容器控件,内部有很多子控件的话,使用这个方式来实现某些效果,效率是灰常低下的,所以容器类不建议频繁使用重建方 法!

至此为止,组件的生成过程就讲解完毕,欢迎专家指正!

组 件教程指南目录

作者:不得闲
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必 须保留此段声明,且在文章页面明显位置给出原
文连接,否则保留追究法律责任的权利。

[转载]Quest.Central.For.Databases——SQL Tuning for SQL Server

mikel阅读(1149)

[转载]【推荐】[SQL优化工具]Quest.Central.For.Databases——SQL Tuning for SQL Server – Beyond the dream——飛雪飄寒 – 博客园.

随着企业数据库的急剧膨 胀和日益复杂,DBA为保证数据库性能所付出的努力与日俱增,手工或使用 多种无法集成的管理工具,都会给日常管理和维护带来不必要的困难。 Quest Central for Databases 是一种集成化、图形化、跨平台的数据库管理解决方案,可以管理异构环境下的 OracleDB2 SQL server 数据库。Quest Central for Databases 消除了企业IT人员管理多种数据库时面临的技术障碍,提高了IT人员工作效率,改善了数据库性能和数据库应用的可用性。为灵活满足用户的不同需求,Quest Central for Databases 采用了模块化产品架构。

该产品包括下列功能模块:

1. 数据库管理(DBA

2. 数据库监控(Monitoring Pack

3. 数据库诊断 (Spotlight Diagnostics)

4. 数据库分析 (Database Analysis)

5. SQL优化 (SQL Tuning)

6. 空间管理 (Space Management)

7. 压力测试 (Load Generator)

8. 数据生成 (Data Generator)

9. PL/SQL 开发 (TOAD)

10. 专家建议 (Knowledge Expert)

今天主要介绍其中的SQL优化工具—— SQL Tuning for SQL Server

一、SQL Tuning for SQL Server简介

SQL语句的优化对发挥 数据库的最佳性能非常关键。然而不幸的是,应用优化通常由于时间和资源的因素而被忽略。SQL Tuning (SQL优化)模块可以对比 和评测特定应用中SQL语句的运行性能,提出智能化的优化建议,帮助用户 改善应用的响应时间。SQL优化模块具有非介入式SQL采集、自动优化和专家建议等功能,全面改善SQL优化工作。

二、SQL Tuning for SQL Server的使用

1、打开Quest Database Management Solutions弹出窗口如图1所示

1

2、在红色标记处打开SQL Tuning 优化SQL

1)建立连接。

Quest Central主界面上的“Database”树上选择“SQL Server”,然后在下方出现的“Tools”框中选择“SQL Tuning”选项,打开“Lanch SQL Tuning for SQL Server Connections”对话框(图2、图3)。我们在这 里建立数据库服务器的连接,以后的分析工作都会在它上面完成。

2 “建立连接”对话框

3

双击“New Connection”图标,在弹出窗口中输入数据库的信息,单击“OK”,然后单击“Connect”即可。

2)分析原始SQL语句 ,在单击“Connect”后将弹出一个新窗口,如图4

4

在打开窗口的“Oriangal SQL”文本框内输入需要分析的原始SQL语句,红色标记处选择对应的数据库名,SQL语句代码如下:

原始SQL语句

SELECT dbo.Person_BasicInfo.*, dbo.Graduater_GraduaterRegist.RegistNO AS RegistNO,
dbo.Graduater_GraduaterRegist.RegistTime
AS BaoDaoTime,
dbo.Graduater_GraduaterRegist.RegistMan
AS RegistMan,
dbo.Graduater_Business.ComeFrom
AS ComeFrom,
dbo.Graduater_Business.Code
AS Code, dbo.Graduater_Business.Status AS Status,
dbo.Graduater_Business.ApproveResult
AS ApproveResult,
dbo.Graduater_Business.NewCorp
AS NewCorp,
dbo.Graduater_Business.CommendNumber
AS CommendNumber,
dbo.Graduater_Business.EmployStatus
AS EmployStatus,
dbo.Graduater_Business.NewCommendTime
AS NewCommendTime,
dbo.Graduater_Business.GetSource
AS GetSource,
dbo.Graduater_Business.EmployTime
AS EmployTime,
dbo.Graduater_Business.Job
AS Job, dbo.Graduater_Business.FillMan AS FillMan,
dbo.Graduater_Business.FillTime
AS FillTime,
dbo.Graduater_Business.IsCommendOK
AS IsCommendOK,
dbo.Graduater_Business.ApproveUser
AS ApproveUser,
dbo.Graduater_Business.ApproveTime
AS ApproveTime,
dbo.Graduater_Business.RegistTime
AS RegistTime,
dbo.Graduater_Business.EmployCorp
AS EmployCorp,
dbo.Graduater_Business.JobRemark
AS JobRemark,
CASE WHEN dbo.Graduater_Business.ComeFrom = WS THEN 网上登记 WHEN dbo.Graduater_Business.ComeFrom
= HP THEN 华普大厦 WHEN dbo.Graduater_Business.ComeFrom = JD THEN 精典大厦
WHEN dbo.Graduater_Business.ComeFrom = MC THEN 赛马场 WHEN ComeFrom =
ZX THEN 高指中心 END AS ComeFromName,
dbo.Person_Contact.Address
AS Address, dbo.Person_Contact.Zip AS Zip,
dbo.Person_Contact.Telephone
AS Telephone, dbo.Person_Contact.Mobile AS Mobile,
dbo.Person_Contact.Email
AS Email, dbo.Person_Contact.IM AS IM,
dbo.Person_Skill.ForeignLanguage
AS ForeignLanguage,
dbo.Person_Skill.ForeignLanguageLevel
AS ForeignLanguageLevel,
dbo.Person_Skill.CantoneseLevel
AS CantoneseLevel,
dbo.Person_Skill.MandarinLevel
AS MandarinLevel,
dbo.Person_Skill.Language
AS Language,
dbo.Person_Skill.TechnicalTitle
AS TechnicalTitle,
dbo.Person_Skill.ComputerLevel
AS ComputerLevel,
dbo.Person_EmployPurpose.JobType
AS JobType,
dbo.Person_EmployPurpose.Vocation
AS Vocation,
dbo.Person_EmployPurpose.JobPlace
AS JobPlace,
dbo.Person_EmployPurpose.Salary
AS Salary,
dbo.Person_EmployPurpose.OnJobDate
AS OnJobDate,
dbo.Person_EmployPurpose.CorpType
AS CorpType,
dbo.Person_EmployPurpose.Job
AS RequireJob, YEAR(GETDATE())
YEAR(dbo.Person_BasicInfo.Birthday) AS Age,
dbo.Graduater_Business.EmployType
AS EmployType,
dbo.Graduater_Business.EmployTypeCode
AS EmployTypeCode,
dbo.Graduater_Business.EmployCorpType
AS EmployCorpType,
CASE WHEN dbo.Graduater_Business.PrintStatus = 已打印 THEN 已打印 ELSE 未打印
END AS PrintStatus, dbo.Graduater_Business.PrintTime AS PrintTime,
CASE WHEN dbo.Graduater_Business.EmployStatus = THEN 已就业 ELSE 未就业
END AS EmployStatusView
FROM dbo.Person_BasicInfo INNER JOIN
dbo.Graduater_Business
ON
dbo.Person_BasicInfo.PersonID
= dbo.Graduater_Business.PersonID LEFT OUTER JOIN
dbo.Graduater_GraduaterRegist
ON
dbo.Graduater_Business.GradBusinessID
= dbo.Graduater_GraduaterRegist.GraduaterGUID
INNER JOIN
dbo.Person_Contact
ON
dbo.Person_BasicInfo.PersonID
= dbo.Person_Contact.PersonID INNER JOIN
dbo.Person_Skill
ON
dbo.Person_BasicInfo.PersonID
= dbo.Person_Skill.PersonID INNER JOIN
dbo.Person_EmployPurpose
ON
dbo.Person_BasicInfo.PersonID
= dbo.Person_EmployPurpose.PersonID

然后点击工具栏上的“Execute”按钮,执行原始的SQL语句,SQL Tuning会自动分析SQL的执行计划,并把分析结果显示到界面上(图5)。

5 分析原始SQL语句

3)优化SQL

现在我们点击工具栏上的“Optimize Statement”按钮,SQL Tuning开始优化SQL,完成后,可以看到SQL Tuning产生了19条与原始SQL等价 的优化方案(图6)。

6 SQL优化 方案

4)获得最优SQL

接下来,我们来执行上面产生的优化方案,以 选出性能最佳的等效SQL语句。在列表中选择需要执行的优化方案(默认已全部选中), 然后点击工具栏上的“Execute”按钮旁边的下拉菜单,选择“Execute Selected”。等到所有SQL运行完成后,点击界 面左方的“Tuning Resolution”按钮,
可以看到 最优的
SQL已经出来啦,运行时间竟然可以提高21%!(图7

7 Tuning Resolution”界面

最优的SQL语句如下:

优化后的SQL语句

SELECT dbo.Person_BasicInfo.*,
dbo.Graduater_GraduaterRegist.RegistNO
AS RegistNO,
dbo.Graduater_GraduaterRegist.RegistTime
AS BaoDaoTime,
dbo.Graduater_GraduaterRegist.RegistMan
AS RegistMan,
dbo.Graduater_Business.ComeFrom
AS ComeFrom,
dbo.Graduater_Business.Code
AS Code,
dbo.Graduater_Business.Status
AS Status,
dbo.Graduater_Business.ApproveResult
AS ApproveResult,
dbo.Graduater_Business.NewCorp
AS NewCorp,
dbo.Graduater_Business.CommendNumber
AS CommendNumber,
dbo.Graduater_Business.EmployStatus
AS EmployStatus,
dbo.Graduater_Business.NewCommendTime
AS NewCommendTime,
dbo.Graduater_Business.GetSource
AS GetSource,
dbo.Graduater_Business.EmployTime
AS EmployTime,
dbo.Graduater_Business.Job
AS Job,
dbo.Graduater_Business.FillMan
AS FillMan,
dbo.Graduater_Business.FillTime
AS FillTime,
dbo.Graduater_Business.IsCommendOK
AS IsCommendOK,
dbo.Graduater_Business.ApproveUser
AS ApproveUser,
dbo.Graduater_Business.ApproveTime
AS ApproveTime,
dbo.Graduater_Business.RegistTime
AS RegistTime,
dbo.Graduater_Business.EmployCorp
AS EmployCorp,
dbo.Graduater_Business.JobRemark
AS JobRemark,
CASE WHEN dbo.Graduater_Business.ComeFrom = WS THEN 网上登记
WHEN dbo.Graduater_Business.ComeFrom = HP THEN 华普大厦
WHEN dbo.Graduater_Business.ComeFrom = JD THEN 精典大厦
WHEN dbo.Graduater_Business.ComeFrom = MC THEN 赛马场
WHEN ComeFrom = ZX THEN 高指中心 END AS ComeFromName,
dbo.Person_Contact.Address
AS Address,
dbo.Person_Contact.Zip
AS Zip,
dbo.Person_Contact.Telephone
AS Telephone,
dbo.Person_Contact.Mobile
AS Mobile,
dbo.Person_Contact.Email
AS Email,
dbo.Person_Contact.IM
AS IM,
dbo.Person_Skill.ForeignLanguage
AS ForeignLanguage,
dbo.Person_Skill.ForeignLanguageLevel
AS ForeignLanguageLevel,
dbo.Person_Skill.CantoneseLevel
AS CantoneseLevel,
dbo.Person_Skill.MandarinLevel
AS MandarinLevel,
dbo.Person_Skill.Language
AS Language,
dbo.Person_Skill.TechnicalTitle
AS TechnicalTitle,
dbo.Person_Skill.ComputerLevel
AS ComputerLevel,
dbo.Person_EmployPurpose.JobType
AS JobType,
dbo.Person_EmployPurpose.Vocation
AS Vocation,
dbo.Person_EmployPurpose.JobPlace
AS JobPlace,
dbo.Person_EmployPurpose.Salary
AS Salary,
dbo.Person_EmployPurpose.OnJobDate
AS OnJobDate,
dbo.Person_EmployPurpose.CorpType
AS CorpType,
dbo.Person_EmployPurpose.Job
AS RequireJob,
YEAR(GETDATE()) YEAR(dbo.Person_BasicInfo.Birthday) AS Age,
dbo.Graduater_Business.EmployType
AS EmployType,
dbo.Graduater_Business.EmployTypeCode
AS EmployTypeCode,
dbo.Graduater_Business.EmployCorpType
AS EmployCorpType,
CASE WHEN dbo.Graduater_Business.PrintStatus = 已打印 THEN 已打印
ELSE 未打印 END AS PrintStatus,
dbo.Graduater_Business.PrintTime
AS PrintTime,
CASE WHEN dbo.Graduater_Business.EmployStatus = THEN 已就业
ELSE 未就业 END AS EmployStatusView
FROM dbo.Person_BasicInfo
INNER JOIN dbo.Graduater_Business
ON dbo.Person_BasicInfo.PersonID = dbo.Graduater_Business.PersonID
LEFT OUTER JOIN dbo.Graduater_GraduaterRegist
ON dbo.Graduater_Business.GradBusinessID = dbo.Graduater_GraduaterRegist.GraduaterGUID
INNER JOIN dbo.Person_Contact
ON dbo.Person_BasicInfo.PersonID = dbo.Person_Contact.PersonID
INNER JOIN dbo.Person_Skill
ON dbo.Person_BasicInfo.PersonID = dbo.Person_Skill.PersonID
INNER JOIN dbo.Person_EmployPurpose
ON dbo.Person_BasicInfo.PersonID = dbo.Person_EmployPurpose.PersonID
OPTION (FORCE ORDER)

(5)学习书写专家级的SQL语句

通过上面的步骤,我们已经可以实现自动优化SQL语句,但更重要的是,我们还可以学习如何书写这样高性能的SQL语句。点击界面左方的“Compare Scenarios”按钮,我们可以比较优化方案和原始SQL中的任意2SQL语句,SQL Tuning会将它们之间的不同之处以不同颜色表示出来,
还可以在下方的“执行计划”中,通过比较两条
SQL语句的执行计划的不同,来了解其中的差异(图8)。

8 Compare Scenarios”界面

三、总结

SQL Tuning等人工智能自动SQL优化工具的出现,为我们节省出大量的时间和精力。借助这些工具的帮助,书写专家级的SQL语句将不再是难事。

Quest.Central.For.Databases下载地址:
http://58.251.57.206/down?cid=1669258673&t=14&fmt=&usrinput=Quest.Central&dt=0&ps=0_0&rt=0kbs&plt=0&spd=9

SQL Tuning操作手册英文版:

http://files.cnblogs.com/dreamof/SQL.Tuning.rar