[文档]介绍几个开源协议

mikel阅读(836)

下面列出了几个比较常见的开源协议,如果想要了解其他的协议和详细了解这些协议,我个人推荐这个网址:http://www.opensource.org/licenses/


Mozilla Public License

MPL License,允许免费重发布、免费修改,但要求修改后的代码版权归软件的发起者。这种授权维护了商业软件的利益,,它要求基于这种软件得修改无偿贡献 版权给该软件。这样,围绕该软件得所有代码得版权都集中在发起开发人得手中。但MPL是允许修改,无偿使用得。MPL软件对链接没有要求。

BSD开源协议

BSD开源协议是一个给于使用者很大自由的协议。可以自由的使用,修改源代码,也可以将修改后的代码作为开源或者专有软件再发布。 当你发布使用了BSD协议的代码,或则以BSD协议代码为基础做二次开发自己的产品时,需要满足三个条件:
1. 如果再发布的产品中包含源代码,则在源代码中必须带有原来代码中的BSD协议。
2. 如果再发布的只是二进制类库/软件,则需要在类库/软件的文档和版权声明中包含原来代码中的BSD协议。
3. 不可以用开源代码的作者/机构名字和原来产品的名字做市场推广。
BSD代码鼓励代码共享,但需要尊重代码作者的著作权。BSD由于允许使用者修改和重新发布代码,也允许使用或在BSD代码上开发商业软件发布和销售,因 此是对商业集成很友好的协议。而很多的公司企业在选用开源产品的时候都首选BSD协议,因为可以完全控制这些第三方的代码,在必要的时候可以修改或者二次 开发。

Apache Licence 2.0

Apache Licence是著名的非盈利开源组织Apache采用的协议。该协议和BSD类似,同样鼓励代码共享和尊重原作者的著作权,同样允许代码修改,再发布(作为开源或商业软件)。需要满足的条件:
1. 需要给代码的用户一份Apache Licence
2. 如果你修改了代码,需要再被修改的文件中说明。
3. 在延伸的代码中(修改和有源代码衍生的代码中)需要带有原来代码中的协议,商标,专利声明和其他原来作者规定需要包含的说明。
4. 如果再发布的产品中包含一个Notice文件,则在Notice文件中需要带有Apache Licence。你可以在Notice中增加自己的许可,但不可以表现为对Apache Licence构成更改。
Apache Licence也是对商业应用友好的许可。使用者也可以在需要的时候修改代码来满足需要并作为开源或商业产品发布/销售。

GPL

GPL许可证是自由软件的应用最广泛的软件许可证,人们可以修改程式的一个或几个副本或程式的任何部分,以此形成基於这些程式的衍生作品。必须在修 改过的档案中附有明显的说明:您修改了此一档案及任何修改的日期。您必须让您发布或出版的作品,包括本程式的全部或一部分,或内含本程式的全部或部分所衍 生的作品,允许第三方在此许可证条款下使用,并且不得因为此项授权行为而收费。
LGPL

Linux就是采用了GPL。GPL协议和BSD, Apache Licence等鼓励代码重用的许可很不一样。GPL的出发点是代码的开源/免费使用和引用/修改/衍生代码的开源/免费使用,但不允许修改后和衍生的代 码做为闭源的商业软件发布和销售。这也就是为什么我们能用免费的各种linux,包括商业公司的linux和linux上各种各样的由个人,组织,以及商 业软件公司开发的免费软件了。
GPL协议的主要内容是只要在一个软件中使用(“使用”指类库引用,修改后的代码或者衍生代码)GPL协议的产品,则该软件产品必须也采用GPL协议,既 必须也是开源和免费。这就是所谓的”传染性”。GPL协议的产品作为一个单独的产品使用没有任何问题,还可以享受免费的优势。
由于GPL严格要求使用了GPL类库的软件产品必须使用GPL协议,对于使用GPL协议的开源代码,商业软件或者对代码有保密要求的部门就不适合集成/采用作为类库和二次开发的基础。
其它细节如再发布的时候需要伴随GPL协议等和BSD/Apache等类似

Public Domain

公共域授权。将软件授权为公共域,这些软件包没有授权协议,任何人都可以随意使用它。

Artistic许可

使作者保持对进一步开发的控制。

[Asp.Net]ASP.Net获得新浪天气预报几种方式总结

mikel阅读(666)

刚赶工写了一个获取新浪天气预报的功能,顺便把代码分享了出来,以后再有这样的工作,大家可以直接把代码拿来使用。

1.利用新浪提供给的iframe直接嵌入,这种方式非常的简单,但是却没有交互性。代码如下:

<iframe frameborder="0" src="http://php.weather.sina.com.cn/widget/weather.php" scrolling="no" width="246" height="360"></iframe>

 

2.抓取当天的天气,以指定格式输出。

涉及的核心代码如下:


public static ArrayList GetWeather(string code)
        {
            
/*
            [0] "北京 "string
            [1] "雷阵雨 "string
            [2] "9℃" string
            [3] "29℃"string
            [4] "小于3级"string
            
*/
            
string html = "";
            
try
            {
                HttpWebRequest request 
= (HttpWebRequest)WebRequest.Create("http://weather.sina.com.cn/iframe/weather/" + code + "_w.html ");
                request.Method 
= "Get";
                
//request.Timeout   =   1;
                request.ContentType = "application/x-www-form-urlencoded ";
                WebResponse response 
= request.GetResponse();
                Stream s 
= response.GetResponseStream();
                StreamReader sr 
= new StreamReader(s, System.Text.Encoding.GetEncoding("GB2312"));
                html 
= sr.ReadToEnd();
                s.Close();
                sr.Close();
            }
            
catch (Exception err)
            {
                
throw new Exception("访问地址出错~~~ ");
            }
            
int count = html.Length;
            
int starIndex = html.IndexOf("<table "0, count);
            
int endIndex = html.IndexOf("</table>", starIndex, count  starIndex);
            html 
= html.Substring(starIndex, endIndex  starIndex + 8);
            
//得到城市
            int cityStartIndex = html.IndexOf("<b>"0, html.Length);
            
int cityEndIndex = html.IndexOf("</b>"0, html.Length);
            
string City = html.Substring(cityStartIndex + 3, cityEndIndex  cityStartIndex  3);
            
//得到天气
            int weatherStartIndex = html.IndexOf("<b>", cityEndIndex);
            
int weatherEndIndex = html.IndexOf("</b>", weatherStartIndex);
            
string Weather = html.Substring(weatherStartIndex + 3, weatherEndIndex  weatherStartIndex  3);
            
//得到温度

            
int temperatureStartIndex = html.IndexOf("<b", weatherEndIndex);
            
int temperatureEndIndex = html.IndexOf("</b>", weatherEndIndex + 3);
            
string Temperature = html.Substring(temperatureStartIndex + 21, temperatureEndIndex  temperatureStartIndex  21);
            
int int1 = Temperature.IndexOf(""0);
            
int int2 = Temperature.IndexOf(""0);
            
int int3 = Temperature.IndexOf("", int2);
            
string MinTemperature = Temperature.Substring(int2 + 1, int3  int2);
            
string MaxTemperature = Temperature.Substring(0, int2  int1 + 2);
            
//得到风力
            int windforceStartIndex = html.IndexOf("风力:", temperatureEndIndex);
            
int windforceEndIndex = html.IndexOf("<br>", windforceStartIndex);
            
string Windforce = html.Substring(windforceStartIndex + 3, windforceEndIndex  windforceStartIndex  3);
            
if (Windforce.Contains("小于"&& (!Windforce.Contains("等于")))                  //判断风力是否含有"小于"或"小于等于"字样将,如果有的话,将其替换为2-
            {
                
//Windforce = Windforce.Replace("小于", "2-");
                string strWindforce = Windforce.Substring(2, Windforce.Length  3);
                
int minWindforce = Int32.Parse(strWindforce)  1;
                Windforce 
= Windforce.Replace("小于", minWindforce.ToString() + "");
            }
            
else if (Windforce.Contains("小于等于"))
            {
                
string strWindforce = Windforce.Substring(4, Windforce.Length  5);
                
int minWindforce = Int32.Parse(strWindforce)  1;
                Windforce 
= Windforce.Replace("小于等于", minWindforce.ToString() + "");
            }
            ArrayList al 
= new ArrayList();
            al.Add(City);
            al.Add(Weather);
            al.Add(MinTemperature);
            al.Add(MaxTemperature);
            al.Add(Windforce);
            
return al;
        }

 这里涉及到一个ConvertCode类,它的作用是用于把城市转换为对应的全国统一的编码,代码如下:


using System;
using System.Collections.Generic;
using System.Web;
namespace WebApplication8
{
    
public class ConvertCode
    {
        
public static string ConvertCodeByCity(string City)
        {
            
string Code = "";
            
switch (City)
            {
                
case "北京":
                    Code 
= "110100";
                    
break;
                
case "天津":
                    Code 
= "120100";
                    
break;
                
case "石家庄":
                    Code 
= "130101";
                    
break;
                
case "唐山":
                    Code 
= "130201";
                    
break;
                
case "秦皇岛":
                    Code 
= "130301";
                    
break;
                
case "张家口":
                    Code 
= "130701";
                    
break;
                
case "承德":
                    Code 
= "130801";
                    
break;
                
case "廊坊":
                    Code 
= "131001";
                    
break;
                
case "邯郸":
                    Code 
= "130401";
                    
break;
                
case "邢台":
                    Code 
= "130501";
                    
break;
                
case "保定":
                    Code 
= "130601";
                    
break;
                
case "沧州":
                    Code 
= "130901";
                    
break;
                
case "衡水":
                    Code 
= "133001";
                    
break;
                
case "太原":
                    Code 
= "140101";
                    
break;
                
case "大同":
                    Code 
= "140201";
                    
break;
                
case "阳泉":
                    Code 
= "140301";
                    
break;
                
case "晋城":
                    Code 
= "140501";
                    
break;
                
case "朔州":
                    Code 
= "140601";
                    
break;
                
case "忻州":
                    Code 
= "142201";
                    
break;
                
case "离石":
                    Code 
= "142331";
                    
break;
                
case "榆次":
                    Code 
= "142401";
                    
break;
                
case "临汾":
                    Code 
= "142601";
                    
break;
                
case "运城":
                    Code 
= "142701";
                    
break;
                
case "长治":
                    Code 
= "140401";
                    
break;
                
case "呼和浩特":
                    Code 
= "150101";
                    
break;
                
case "包头":
                    Code 
= "150201";
                    
break;
                
case "乌海":
                    Code 
= "150301";
                    
break;
                
case "集宁":
                    Code 
= "152601";
                    
break;
                
case "巴彦浩特":                           //这个城市搜索不到
                    Code = "152701";
                    
break;
                
case "临河":
                    Code 
= "152801";
                    
break;
                
case "鄂尔多斯":                           //这个城市搜索不到
                    Code = "152921";
                    
break;
                
case "赤峰":
                    Code 
= "150401";
                    
break;
                
case "通辽":
                    Code 
= "152301";
                    
break;
                
case "锡林浩特":
                    Code 
= "152502";
                    
break;
                
case "海拉尔":                            //这个城市搜索不到
                    Code = "152101";
                    
break;
                
case "乌兰浩特":
                    Code 
= "152201";
                    
break;
                
case "沈阳":
                    Code 
= "210101";
                    
break;
                
case "大连":
                    Code 
= "210201";
                    
break;
                
case "鞍山":
                    Code 
= "210301";
                    
break;
                
case "抚顺":
                    Code 
= "210401";
                    
break;
                
case "本溪":
                    Code 
= "210501";
                    
break;
                
case "锦州":
                    Code 
= "210701";
                    
break;
                
case "营口":
                    Code 
= "210801";
                    
break;
                
case "阜新":
                    Code 
= "210901";
                    
break;
                
case "盘锦":
                    Code 
= "211101";
                    
break;
                
case "铁岭":
                    Code 
= "211201";
                    
break;
                
case "朝阳":
                    Code 
= "211301";
                    
break;
                
case "葫芦岛":
                    Code 
= "211401";
                    
break;
                
case "丹东":
                    Code 
= "210601";
                    
break;
                
case "长春":
                    Code 
= "220101";
                    
break;
                
case "吉林":
                    Code 
= "220201";
                    
break;
                
case "四平":
                    Code 
= "220301";
                    
break;
                
case "辽源":
                    Code 
= "220401";
                    
break;
                
case "松原":
                    Code 
= "220601";
                    
break;
                
case "白城":
                    Code 
= "222301";
                    
break;
                
case "延边":
                    Code 
= "222401";
                    
break;
                
case "通化":
                    Code 
= "220501";
                    
break;
                
case "哈尔滨":
                    Code 
= "230101";
                    
break;
                
case "鸡西":
                    Code 
= "230301";
                    
break;
                
case "鹤岗":
                    Code 
= "230401";
                    
break;
                
case "双鸭山":
                    Code 
= "230501";
                    
break;
                
case "伊春":
                    Code 
= "230701";
                    
break;
                
case "佳木斯":
                    Code 
= "230801";
                    
break;
                
case "七台河":
                    Code 
= "230901";
                    
break;
                
case "牡丹江":
                    Code 
= "231001";
                    
break;
                
case "绥化":
                    Code 
= "232301";
                    
break;
                
case "齐齐哈尔":
                    Code 
= "230201";
                    
break;
                
case "大庆":
                    Code 
= "230601";
                    
break;
                
case "黑河":
                    Code 
= "232601";
                    
break;
                
case "大兴安岭":
                    Code 
= "232700";
                    
break;
                
case "上海":
                    Code 
= "310100";
                    
break;
                
case "南京":
                    Code 
= "320101";
                    
break;
                
case "无锡":
                    Code 
= "320201";
                    
break;
                
case "徐州":
                    Code 
= "320301";
                    
break;
                
case "常州":
                    Code 
= "320401";
                    
break;
                
case "苏州":
                    Code 
= "320501";
                    
break;
                
case "南通":
                    Code 
= "320600";
                    
break;
                
case "连云港":
                    Code 
= "320701";
                    
break;
                
case "淮阴":
                    Code 
= "320801";
                    
break;
                
case "盐城":
                    Code 
= "320901";
                    
break;
                
case "扬州":
                    Code 
= "321001";
                    
break;
                
case "镇江":
                    Code 
= "321101";
                    
break;
                
case "泰州":
                    Code 
= "321102";
                    
break;
                
case "宿迁":
                    Code 
= "321103";
                    
break;
                
case "杭州":
                    Code 
= "330101";
                    
break;
                
case "宁波":
                    Code 
= "330201";
                    
break;
                
case "温州":
                    Code 
= "330301";
                    
break;
                
case "嘉兴":
                    Code 
= "330401";
                    
break;
                
case "湖州":
                    Code 
= "330501";
                    
break;
                
case "绍兴":
                    Code 
= "330601";
                    
break;
                
case "金华":
                    Code 
= "330701";
                    
break;
                
case "衢州":
                    Code 
= "330801";
                    
break;
                
case "舟山":
                    Code 
= "330901";
                    
break;
                
case "丽水":
                    Code 
= "332501";
                    
break;
                
case "台州":
                    Code 
= "332602";
                    
break;
                
case "合肥":
                    Code 
= "340101";
                    
break;
                
case "芜湖":
                    Code 
= "340201";
                    
break;
                
case "蚌埠":
                    Code 
= "340301";
                    
break;
                
case "淮南":
                    Code 
= "340401";
                    
break;
                
case "马鞍山":
                    Code 
= "340501";
                    
break;
                
case "淮北":
                    Code 
= "340601";
                    
break;
                
case "铜陵":
                    Code 
= "340701";
                    
break;
                
case "安庆":
                    Code 
= "340801";
                    
break;
                
case "黄山市":
                    Code 
= "341001";
                    
break;
                
case "阜阳":
                    Code 
= "342101";
                    
break;
                
case "宿州":
                    Code 
= "342201";
                    
break;
                
case "滁州":
                    Code 
= "342301";
                    
break;
                
case "六安":
                    Code 
= "342401";
                    
break;
                
case "宣城":
                    Code 
= "342501";
                    
break;
                
case "巢湖":
                    Code 
= "342601";
                    
break;
                
case "池州":
                    Code 
= "342901";
                    
break;
                
case "福州":
                    Code 
= "350101";
                    
break;
                
case "厦门":
                    Code 
= "350201";
                    
break;
                
case "莆田":
                    Code 
= "350301";
                    
break;
                
case "三明":
                    Code 
= "350401";
                    
break;
                
case "泉州":
                    Code 
= "350501";
                    
break;
                
case "漳州":
                    Code 
= "350601";
                    
break;
                
case "南平":
                    Code 
= "352101";
                    
break;
                
case "宁德":
                    Code 
= "352201";
                    
break;
                
case "龙岩":
                    Code 
= "352601";
                    
break;
                
case "陇南":
                    Code 
= "352602";
                    
break;
                
case "庆阳":
                    Code 
= "352603";
                    
break;
                
case "南昌":
                    Code 
= "360101";
                    
break;
                
case "景德镇":
                    Code 
= "360201";
                    
break;
                
case "赣州":
                    Code 
= "362101";
                    
break;
                
case "萍乡":
                    Code 
= "360301";
                    
break;
                
case "九江":
                    Code 
= "360401";
                    
break;
                
case "新余":
                    Code 
= "360501";
                    
break;
                
case "鹰潭":
                    Code 
= "360601";
                    
break;
                
case "宜春":
                    Code 
= "362201";
                    
break;
                
case "上饶":
                    Code 
= "362301";
                    
break;
                
case "吉安":
                    Code 
= "362401";
                    
break;
                
case "济南":
                    Code 
= "370101";
                    
break;
                
case "青岛":
                    Code 
= "370201";
                    
break;
                
case "淄博":
                    Code 
= "370301";
                    
break;
                
case "枣庄":
                    Code 
= "370401";
                    
break;
                
case "东营":
                    Code 
= "370501";
                    
break;
                
case "烟台":
                    Code 
= "370601";
                    
break;
                
case "潍坊":
                    Code 
= "370701";
                    
break;
                
case "济宁":
                    Code 
= "370801";
                    
break;
                
case "泰安":
                    Code 
= "370901";
                    
break;
                
case "威海":
                    Code 
= "371001";
                    
break;
                
case "日照":
                    Code 
= "371100";
                    
break;
                
case "滨州":
                    Code 
= "372301";
                    
break;
                
case "德州":
                    Code 
= "372401";
                    
break;
                
case "聊城":
                    Code 
= "372501";
                    
break;
                
case "临沂":
                    Code 
= "372801";
                    
break;
                
case "菏泽":
                    Code 
= "372901";
                    
break;
                
case "莱芜":
                    Code 
= "372902";
                    
break;
                
case "郑州":
                    Code 
= "410101";
                    
break;
                
case "开封":
                    Code 
= "410201";
                    
break;
                
case "洛阳":
                    Code 
= "410301";
                    
break;
                
case "平顶山":
                    Code 
= "410401";
                    
break;
                
case "安阳":
                    Code 
= "410501";
                    
break;
                
case "鹤壁":
                    Code 
= "410601";
                    
break;
                
case "新乡":
                    Code 
= "410701";
                    
break;
                
case "焦作":
                    Code 
= "410801";
                    
break;
                
case "濮阳":
                    Code 
= "410901";
                    
break;
                
case "许昌":
                    Code 
= "411001";
                    
break;
                
case "漯河":
                    Code 
= "411101";
                    
break;
                
case "三门峡":
                    Code 
= "411201";
                    
break;
                
case "商丘":
                    Code 
= "412301";
                    
break;
                
case "周口":
                    Code 
= "412701";
                    
break;
                
case "驻马店":
                    Code 
= "412801";
                    
break;
                
case "南阳":
                    Code 
= "412901";
                    
break;
                
case "信阳":
                    Code 
= "413001";
                    
break;
                
case "武汉":
                    Code 
= "420101";
                    
break;
                
case "黄石":
                    Code 
= "420201";
                    
break;
                
case "十堰":
                    Code 
= "420301";
                    
break;
                
case "随州":
                    Code 
= "420400";
                    
break;
                
case "宜昌":
                    Code 
= "420501";
                    
break;
                
case "襄樊":
                    Code 
= "420601";
                    
break;
                
case "鄂州":
                    Code 
= "420701";
                    
break;
                
case "荆门":
                    Code 
= "420801";
                    
break;
                
case "黄冈":
                    Code 
= "422103";
                    
break;
                
case "孝感":
                    Code 
= "422201";
                    
break;
                
case "咸宁":
                    Code 
= "422301";
                    
break;
                
case "荆州":
                    Code 
= "422421";
                    
break;
                
case "恩施":
                    Code 
= "422801";
                    
break;
                
case "长沙":
                    Code 
= "430101";
                    
break;
                
case "衡阳":
                    Code 
= "430401";
                    
break;
                
case "邵阳":
                    Code 
= "430501";
                    
break;
                
case "郴州":
                    Code 
= "432801";
                    
break;
                
case "永州":
                    Code 
= "432901";
                    
break;
                
case "韶山":
                    Code 
= "430801";
                    
break;
                
case "张家界":
                    Code 
= "430802";
                    
break;
                
case "怀化":
                    Code 
= "433001";
                    
break;
                
case "吉首":
                    Code 
= "433101";
                    
break;
                
case "株洲":
                    Code 
= "430201";
                    
break;
                
case "湘潭":
                    Code 
= "430301";
                    
break;
                
case "岳阳":
                    Code 
= "430601";
                    
break;
                
case "常德":
                    Code 
= "430701";
                    
break;
                
case "益阳":
                    Code 
= "432301";
                    
break;
                
case "娄底":
                    Code 
= "432501";
                    
break;
                
case "广州":
                    Code 
= "440101";
                    
break;
                
case "佛山":
                    Code 
= "440601";
                    
break;
                
case "深圳":
                    Code 
= "440301";
                    
break;
                
case "汕尾":
                    Code 
= "441501";
                    
break;
                
case "惠州":
                    Code 
= "441301";
                    
break;
                
case "河源":
                    Code 
= "441601";
                    
break;
                
case "清远":
                    Code 
= "441801";
                    
break;
                
case "东莞":
                    Code 
= "441901";
                    
break;
                
case "珠海":
                    Code 
= "440401";
                    
break;
                
case "江门":
                    Code 
= "440701";
                    
break;
                
case "肇庆":
                    Code 
= "441201";
                    
break;
                
case "中山":
                    Code 
= "442001";
                    
break;
                
case "湛江":
                    Code 
= "440801";
                    
break;
                
case "茂名":
                    Code 
= "440901";
                    
break;
                
case "韶关":
                    Code 
= "440201";
                    
break;
                
case "汕头":
                    Code 
= "440501";
                    
break;
                
case "梅州":
                    Code 
= "441401";
                    
break;
                
case "阳江":
                    Code 
= "441701";
                    
break;
                
case "潮州":
                    Code 
= "441702";
                    
break;
                
case "顺德":
                    Code 
= "441703";
                    
break;
                
case "揭阳":
                    Code 
= "441704";
                    
break;
                
case "云浮":
                    Code 
= "441705";
                    
break;
                
case "南宁":
                    Code 
= "450101";
                    
break;
                
case "梧州":
                    Code 
= "450401";
                    
break;
                
case "玉林":
                    Code 
= "452501";
                    
break;
                
case "桂林":
                    Code 
= "450301";
                    
break;
                
case "百色":
                    Code 
= "452601";
                    
break;
                
case "河池":
                    Code 
= "452701";
                    
break;
                
case "钦州":
                    Code 
= "452802";
                    
break;
                
case "柳州":
                    Code 
= "450201";
                    
break;
                
case "北海":
                    Code 
= "450501";
                    
break;
                
case "防城港":
                    Code 
= "450502";
                    
break;
                
case "贵港":
                    Code 
= "450503";
                    
break;
                
case "贺州":
                    Code 
= "450504";
                    
break;
                
case "海口":
                    Code 
= "460100";
                    
break;
                
case "三亚":
                    Code 
= "460200";
                    
break;
                
case "西沙群岛":
                    Code 
= "460300";
                    
break;
                
case "成都":
                    Code 
= "510101";
                    
break;
                
case "眉山":
                    Code 
= "513321";
                    
break;
                
case "雅安":
                    Code 
= "513101";
                    
break;
                
case "峨嵋山":
                    Code 
= "513229";
                    
break;
                
case "自贡":
                    Code 
= "510301";
                    
break;
                
case "重庆":
                    Code 
= "500100";
                    
break;
                
case "万州":
                    Code 
= "500102";
                    
break;
                
case "涪陵":
                    Code 
= "500103";
                    
break;
                
case "南充":
                    Code 
= "512901";
                    
break;
                
case "泸州":
                    Code 
= "510501";
                    
break;
                
case "德阳":
                    Code 
= "510601";
                    
break;
                
case "绵阳":
                    Code 
= "510701";
                    
break;
                
case "遂宁":
                    Code 
= "510901";
                    
break;
                
case "内江":
                    Code 
= "511001";
                    
break;
                
case "乐山":
                    Code 
= "511101";
                    
break;
                
case "宜宾":
                    Code 
= "512501";
                    
break;
                
case "广元":
                    Code 
= "510801";
                    
break;
                
case "达州":
                    Code 
= "513021";
                    
break;
                
case "资阳":
                    Code 
= "513401";
                    
break;
                
case "攀枝花":
                    Code 
= "510401";
                    
break;
                
case "阿坝":
                    Code 
= "510402";
                    
break;
                
case "甘孜":
                    Code 
= "510403";
                    
break;
                
case "凉山":
                    Code 
= "510404";
                    
break;
                
case "广安":
                    Code 
= "510405";
                    
break;
                
case "巴中":
                    Code 
= "510406";
                    
break;
                
case "黔江":
                    Code 
= "500239";
                    
break;
                
case "贵阳":
                    Code 
= "520101";
                    
break;
                
case "六盘水":
                    Code 
= "520200";
                    
break;
                
case "铜仁":
                    Code 
= "522201";
                    
break;
                
case "安顺":
                    Code 
= "522501";
                    
break;
                
case "凯里":
                    Code 
= "522601";
                    
break;
                
case "都匀":
                    Code 
= "522701";
                    
break;
                
case "兴义":
                    Code 
= "522301";
                    
break;
                
case "毕节":
                    Code 
= "522421";
                    
break;
                
case "遵义":
                    Code 
= "522101";
                    
break;
                
case "昆明":
                    Code 
= "530101";
                    
break;
                
case "德宏":
                    Code 
= "530201";
                    
break;
                
case "曲靖":
                    Code 
= "532201";
                    
break;
                
case "楚雄":
                    Code 
= "532301";
                    
break;
                
case "玉溪":
                    Code 
= "532401";
                    
break;
                
case "红河":
                    Code 
= "532501";
                    
break;
                
case "文山":
                    Code 
= "532621";
                    
break;
                
case "思茅":
                    Code 
= "532721";
                    
break;
                
case "昭通":
                    Code 
= "532101";
                    
break;
                
case "西双版纳":
                    Code 
= "532821";
                    
break;
                
case "大理":
                    Code 
= "532901";
                    
break;
                
case "保山":
                    Code 
= "533001";
                    
break;
                
case "怒江":
                    Code 
= "533121";
                    
break;
                
case "丽江":
                    Code 
= "533221";
                    
break;
                
case "迪庆":
                    Code 
= "533321";
                    
break;
                
case "临沧":
                    Code 
= "533521";
                    
break;
                
case "拉萨":
                    Code 
= "540101";
                    
break;
                
case "昌都":
                    Code 
= "542121";
                    
break;
                
case "山南":
                    Code 
= "542221";
                    
break;
                
case "日喀则":
                    Code 
= "542301";
                    
break;
                
case "那曲":
                    Code 
= "542421";
                    
break;
                
case "阿里":
                    Code 
= "542523";
                    
break;
                
case "林芝":
                    Code 
= "542621";
                    
break;
                
case "西安":
                    Code 
= "610101";
                    
break;
                
case "铜川":
                    Code 
= "610201";
                    
break;
                
case "宝鸡":
                    Code 
= "610301";
                    
break;
                
case "咸阳":
                    Code 
= "610401";
                    
break;
                
case "渭南":
                    Code 
= "612101";
                    
break;
                
case "汉中":
                    Code 
= "612301";
                    
break;
                
case "安康":
                    Code 
= "612401";
                    
break;
                
case "商洛":
                    Code 
= "612501";
                    
break;
                
case "延安":
                    Code 
= "612601";
                    
break;
                
case "榆林":
                    Code 
= "612701";
                    
break;
                
case "兰州":
                    Code 
= "620101";
                    
break;
                
case "白银":
                    Code 
= "620401";
                    
break;
                
case "金昌":
                    Code 
= "620301";
                    
break;
                
case "天水":
                    Code 
= "620501";
                    
break;
                
case "张掖":
                    Code 
= "622201";
                    
break;
                
case "武威":
                    Code 
= "622301";
                    
break;
                
case "定西":
                    Code 
= "622421";
                    
break;
                
case "平凉":
                    Code 
= "622701";
                    
break;
                
case "临夏":
                    Code 
= "622901";
                    
break;
                
case "嘉峪关":
                    Code 
= "620201";
                    
break;
                
case "酒泉":
                    Code 
= "622102";
                    
break;
                
case "西宁":
                    Code 
= "630100";
                    
break;
                
case "果洛":
                    Code 
= "632121";
                    
break;
                
case "海西":
                    Code 
= "632221";
                    
break;
                
case "格尔木":
                    Code 
= "632321";
                    
break;
                
case "海东":
                    Code 
= "632521";
                    
break;
                
case "海北":
                    Code 
= "632621";
                    
break;
                
case "玉树":
                    Code 
= "632721";
                    
break;
                
case "黄南":
                    Code 
= "632802";
                    
break;
                
case "银川":
                    Code 
= "640101";
                    
break;
                
case "石嘴山":
                    Code 
= "640201";
                    
break;
                
case "吴忠":
                    Code 
= "642101";
                    
break;
                
case "固原":
                    Code 
= "642221";
                    
break;
                
case "乌鲁木齐":
                    Code 
= "650101";
                    
break;
                
case "克拉玛依":
                    Code 
= "650201";
                    
break;
                
case "吐鲁番":
                    Code 
= "652101";
                    
break;
                
case "哈密":
                    Code 
= "652201";
                    
break;
                
case "昌吉":
                    Code 
= "652301";
                    
break;
                
case "博乐":
                    Code 
= "652701";
                    
break;
                
case "库尔勒":
                    Code 
= "652801";
                    
break;
                
case "阿克苏":
                    Code 
= "652901";
                    
break;
                
case "克州":
                    Code 
= "653001";
                    
break;
                
case "喀什":
                    Code 
= "653101";
                    
break;
                
case "伊犁":
                    Code 
= "654101";
                    
break;
                
case "石河子":
                    Code 
= "655001";
                    
break;
                
case "塔城":
                    Code 
= "655002";
                    
break;
                
case "阿勒泰":
                    Code 
= "655003";
                    
break;
                
case "台北":
                    Code 
= "710001";
                    
break;
                
case "辽阳":
                    Code 
= "211001";
                    
break;
                
case "和田":
                    Code 
= "653201";
                    
break;
                
case "澳门":
                    Code 
= "820000";
                    
break;
                
case "香港":
                    Code 
= "810000";
                    
break;
                    
                
default:
                    
break;
            }
            
return Code;
        }
    }
}

3.获取三天以内的天气,以指定格式输出。

核心代码如下:


public static ArrayList GetThreeDayWeather(string City)
        {
            ArrayList al 
= new ArrayList();
            
/*
           [0] "今天 北京"              string
           [1] "2009-04-17,星期五"     string
           [2] "晴转多云"               string
           [3] "12℃"                   string
           [4] "25℃"                   string
           [5] "2-3级"                  string
           [6] "明天 北京"              string
           [7] "2009-04-18,星期六"     string
           [8] "阴转阵雨"               string
           [9] "11℃"                   string
           [10] "21℃"                  string
           [11] "2-3级"                 string
           [12] "后天 北京"             string
           [13] "2009-04-19,星期日"    string
           [14] "多云转阵雨"            string
           [15] "9℃"                   string
           [16] "20℃"                  string
           [17] "2-3级"                 string         
           
*/
            
string Html = "";       //返回来的网页的源码
            ASCIIEncoding encoding = new ASCIIEncoding();
            
string postData = string.Format("city=" + City);
            
byte[] data = encoding.GetBytes(postData);
            
try
            {
                HttpWebRequest request 
= (HttpWebRequest)WebRequest.Create("http://php.weather.sina.com.cn/search.php?city=" + System.Web.HttpContext.Current.Server.UrlEncode(City) + "&f=1&dpc=1");
                request.Method 
= "Get";
                request.ContentType 
= "application/x-www-form-urlencoded ";
                WebResponse response 
= request.GetResponse();
                Stream s 
= response.GetResponseStream();
                StreamReader sr 
= new StreamReader(s, System.Text.Encoding.GetEncoding("GB2312"));
                Html 
= sr.ReadToEnd();
                s.Close();
                sr.Close();
            }
            
catch (Exception err)
            {
                
throw new Exception("访问地址出错~~~ ");
            }
            
//去除多余代码,便于分析跟提高效率
            int count = Html.Length;
            
int starIndex = Html.IndexOf("<div id=\"Weather3DBlk\">"0, count);
            
int endIndex = Html.IndexOf("<div id=\"SideBar\">"0);
            Html 
= Html.Substring(starIndex, endIndex  starIndex);
            
try
            {
                
#region 得到今天的天气
                
//得到今天的标识跟城市
                int firstDayAndCityStartIndex = Html.IndexOf("<h3>"0);
                
int firstDayAndCityEndIndex = Html.IndexOf("</h3>"0);
                
string FirstDayAndCity = Html.Substring(firstDayAndCityStartIndex + 4, firstDayAndCityEndIndex  firstDayAndCityStartIndex  4);
                
//得到今天的日期跟星期
                int firstDateStartIndex = Html.IndexOf("<p>", firstDayAndCityEndIndex);
                
int firstDateEndIndex = Html.IndexOf("</p>", firstDayAndCityEndIndex);
                
string FirstDate = Html.Substring(firstDateStartIndex + 3, firstDateEndIndex  firstDateStartIndex  3).Replace("&nbsp;""");
                
//得到今天的天气
                int firstWeatherStartIndex = Html.IndexOf("<div class=\"Weather_TP\">", firstDateEndIndex);
                
int firstWeatherEndIndex = Html.IndexOf(" ", firstWeatherStartIndex + 24);
                
string FirstWeather = Html.Substring(firstWeatherStartIndex + 24, firstWeatherEndIndex  firstWeatherStartIndex  24);
                
//得到今天的温度

                
int firstTemperatureStartIndex = firstWeatherEndIndex + 1;
                
int firstTemperatureEndIndex = Html.IndexOf("</div>", firstTemperatureStartIndex);
                
string FirstTemperature = Html.Substring(firstTemperatureStartIndex, firstTemperatureEndIndex  firstTemperatureStartIndex);
                
int int1 = FirstTemperature.IndexOf(""0);
                
int int2 = FirstTemperature.IndexOf(""0);
                
int int3 = FirstTemperature.IndexOf("", int2);
                
string FirstMinTemperature = FirstTemperature.Substring(int2 + 1, int3  int2);
                
string FirstMaxTemperature = FirstTemperature.Substring(0, int2  int1 + 2);
                
//得到今天的风力
                int firstWindforceStartIndex = Html.IndexOf("风力:", firstTemperatureEndIndex);
                
int firstWindforceEndIndex = Html.IndexOf("</div>", firstWindforceStartIndex);
                
string FirstWindforce = Html.Substring(firstWindforceStartIndex + 3, firstWindforceEndIndex  firstWindforceStartIndex  3);
                
if (FirstWindforce.Contains(""))
                {
                }
                
else if (FirstWindforce.Contains("<"))                  //判断风力是否含有"<"或"≤"字样将,如果有的话,将其替换为2-
                {
                    
string strWindforce = FirstWindforce.Substring(1, FirstWindforce.Length  2);
                    
int minWindforce = Int32.Parse(strWindforce)  1;
                    FirstWindforce 
= FirstWindforce.Replace("<", minWindforce.ToString() + "");
                }
                
else if (FirstWindforce.Contains(""))
                {
                    
string strWindforce = FirstWindforce.Substring(1, FirstWindforce.Length  2);
                    
int minWindforce = Int32.Parse(strWindforce)  1;
                    FirstWindforce 
= FirstWindforce.Replace("", minWindforce.ToString() + "");
                }
                
#endregion
                
#region 得到明天的天气
                
//得到明天的标识跟城市
                int secondDayAndCityStartIndex = Html.IndexOf("<h3>", firstWindforceEndIndex);
                
int secondDayAndCityEndIndex = Html.IndexOf("</h3>", secondDayAndCityStartIndex);
                
string secondDayAndCity = Html.Substring(secondDayAndCityStartIndex + 4, secondDayAndCityEndIndex  secondDayAndCityStartIndex  4);
                
//得到明天的日期跟星期
                int secondDateStartIndex = Html.IndexOf("<p>", secondDayAndCityEndIndex);
                
int secondDateEndIndex = Html.IndexOf("</p>", secondDateStartIndex);
                
string SecondDate = Html.Substring(secondDateStartIndex + 3, secondDateEndIndex  secondDateStartIndex  3).Replace("&nbsp;""");
                
//得到明天的天气
                int secondWeatherStartIndex = Html.IndexOf("<div class=\"Weather_TP\">", secondDateEndIndex);
                
int secondWeatherEndIndex = Html.IndexOf(" ", secondWeatherStartIndex + 24);
                
string SecondWeather = Html.Substring(secondWeatherStartIndex + 24, secondWeatherEndIndex  secondWeatherStartIndex  24);
                
//得到明天的温度

                
int secondTemperatureStartIndex = secondWeatherEndIndex + 1;
                
int secondTemperatureEndIndex = Html.IndexOf("</div>", secondTemperatureStartIndex);
                
string SecondTemperature = Html.Substring(secondTemperatureStartIndex, secondTemperatureEndIndex  secondTemperatureStartIndex);
                
int int4 = SecondTemperature.IndexOf(""0);
                
int int5 = SecondTemperature.IndexOf(""0);
                
int int6 = SecondTemperature.IndexOf("", int2);
                
string SecondMinTemperature = SecondTemperature.Substring(int5 + 1, int6  int5);
                
string SecondMaxTemperature = SecondTemperature.Substring(0, int5  int4 + 2);
                
//得到明天的风力
                int secondWindforceStartIndex = Html.IndexOf("风力:", secondTemperatureEndIndex);
                
int secondWindforceEndIndex = Html.IndexOf("</div>", secondWindforceStartIndex);
                
string SecondWindforce = Html.Substring(secondWindforceStartIndex + 3, secondWindforceEndIndex  secondWindforceStartIndex  3);
                
if (SecondWindforce.Contains(""))
                {
                }
                
else if (SecondWindforce.Contains("<"))                  //判断风力是否含有"<"或"≤"字样将,如果有的话,将其替换为2-
                {
                    
string strWindforce = SecondWindforce.Substring(1, FirstWindforce.Length  2);
                    
int minWindforce = Int32.Parse(strWindforce)  1;
                    SecondWindforce 
= SecondWindforce.Replace("<", minWindforce.ToString() + "");
                }
                
else if (SecondWindforce.Contains(""))
                {
                    
string strWindforce = SecondWindforce.Substring(1, SecondWindforce.Length  2);
                    
int minWindforce = Int32.Parse(strWindforce)  1;
                    SecondWindforce 
= SecondWindforce.Replace("", minWindforce.ToString() + "");
                }
                
#endregion
                
#region 得到后天的天气
                
//得到后天的标识跟城市
                int thirdDayAndCityStartIndex = Html.IndexOf("<h3>", secondWindforceEndIndex);
                
int thirdDayAndCityEndIndex = Html.IndexOf("</h3>", thirdDayAndCityStartIndex);
                
string thirdDayAndCity = Html.Substring(thirdDayAndCityStartIndex + 4, thirdDayAndCityEndIndex  thirdDayAndCityStartIndex  4);
                
//得到后天的日期跟星期
                int thirdDateStartIndex = Html.IndexOf("<p>", thirdDayAndCityEndIndex);
                
int thirdDateEndIndex = Html.IndexOf("</p>", thirdDateStartIndex);
                
string ThirdDate = Html.Substring(thirdDateStartIndex + 3, thirdDateEndIndex  thirdDateStartIndex  3).Replace("&nbsp;""");
                
//得到后天的天气
                int thirdWeatherStartIndex = Html.IndexOf("<div class=\"Weather_TP\">", thirdDateEndIndex);
                
int thirdWeatherEndIndex = Html.IndexOf(" ", thirdWeatherStartIndex + 24);
                
string ThirdWeather = Html.Substring(thirdWeatherStartIndex + 24, thirdWeatherEndIndex  thirdWeatherStartIndex  24);
                
//得到后天的温度

                
int thirdTemperatureStartIndex = thirdWeatherEndIndex + 1;
                
int thirdTemperatureEndIndex = Html.IndexOf("</div>", thirdTemperatureStartIndex);
                
string ThirdTemperature = Html.Substring(thirdTemperatureStartIndex, thirdTemperatureEndIndex  thirdTemperatureStartIndex);
                
int int7 = ThirdTemperature.IndexOf(""0);
                
int int8 = ThirdTemperature.IndexOf(""0);
                
int int9 = ThirdTemperature.IndexOf("", int2);
                
string ThirdMinTemperature = ThirdTemperature.Substring(int8 + 1, int9  int8);
                
string ThirdMaxTemperature = ThirdTemperature.Substring(0, int8  int7 + 2);
                
//得到后天的风力
                int thirdWindforceStartIndex = Html.IndexOf("风力:", thirdTemperatureEndIndex);
                
int thirdWindforceEndIndex = Html.IndexOf("</div>", thirdWindforceStartIndex);
                
string ThirdWindforce = Html.Substring(thirdWindforceStartIndex + 3, thirdWindforceEndIndex  thirdWindforceStartIndex  3);
                
if (ThirdWindforce.Contains(""))
                {
                }
                
else if (ThirdWindforce.Contains("<"))                  //判断风力是否含有"<"或"≤"字样将,如果有的话,将其替换为2-
                {
                    
string strWindforce = ThirdWindforce.Substring(1, ThirdWindforce.Length  2);
                    
int minWindforce = Int32.Parse(strWindforce)  1;
                    ThirdWindforce 
= ThirdWindforce.Replace("<", minWindforce.ToString() + "");
                }
                
else if (ThirdWindforce.Contains(""))
                {
                    
string strWindforce = ThirdWindforce.Substring(1, ThirdWindforce.Length  2);
                    
int minWindforce = Int32.Parse(strWindforce)  1;
                    ThirdWindforce 
= ThirdWindforce.Replace("", minWindforce.ToString() + "");
                }
                
#endregion
                al.Add(FirstDayAndCity);
                al.Add(FirstDate);
                al.Add(FirstWeather);
                al.Add(FirstMinTemperature);
                al.Add(FirstMaxTemperature);
                al.Add(FirstWindforce);
                al.Add(secondDayAndCity);
                al.Add(SecondDate);
                al.Add(SecondWeather);
                al.Add(SecondMinTemperature);
                al.Add(SecondMaxTemperature);
                al.Add(SecondWindforce);
                al.Add(thirdDayAndCity);
                al.Add(ThirdDate);
                al.Add(ThirdWeather);
                al.Add(ThirdMinTemperature);
                al.Add(ThirdMaxTemperature);
                al.Add(ThirdWindforce);
            }
            
catch (Exception err)
            {
            }
            
return al;
        }

代码下载:/Files/wlb/xinlangweather.rar

特 殊说明,使用第三种方法获取天气预报,输入城市的时候可能会受新浪提供的服务的影响,可能有些城市搜不到,前两种方法应该是不会受影响的。另外,由于代码 写的比较急,所以难免代码的质量就会有些问题,还请大家多多包涵。单纯从代码上看,可能确实没有什么难度,可是如果在您的工作中如果因为我的代码为您节省 了一些时间,笔者深感欣慰了。

另外,由于我的开发环境是vs2008+sp1,如果您的vs版本较低,不妨把项目文件删除掉,然后打开您的vs,选择打开网站,然后定位到本程序的目录,这样就可以进行代码的编译跟踪调试了。

再补充一点,有人可能对代码中出现的:System.Environment.NewLine 这句代码比较迷糊,因为这个是.net提供的简单的换行方法,我推荐大家以后能够使用这种.net提供给大家的高效、简便的方法。最关键的是它不容易出错。

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

[JavaScript]简单、通用的JQuery Tab实现

mikel阅读(827)

网页上的空间是寸土寸金,虽然显示器的分辨率越来越大,可是直到今天,网页设计中仍然是以至少1024×768 像素的支持为主,也就是说,每一屏页面只有区区 955×600像素 的安全尺寸可以用而已。于是,为了在有限的空间里容纳更多的内容,滑动门式的标签切换(Tabs)方式越来越受欢迎。通过滑动门技术,可以在同一块页面区 域内放置数倍的内容。根据用户的选择来决定显示哪一部分。最近我在实际应用中,逐步完善出一种基于 jQuery,但是比 jQuery UI Tabs 插件更小巧也更通用的简单 Tabs 实现。

最早的滑动门的技术,一般都是结合 onclick 或者 onmouSEOver 事件传递一个参数给 JS 函数,根据传递的参数来决定显示哪一个标签。比如:

function showTabs(n) {
var tabsNumber = 3;
for (i = 0; i < tabsNumber; i++) {
if (i == n) {
document.getElementById("tabPanel-" + i).style.display = "block";
} else {
document.getElementById("tabPanel-" + i).style.display = "none";
}
}
}

加入有这样的一个函数,就可以在tab的标题按钮中设置 onclick = “showTabs(1)”来设置第二块内容显示,而其它块隐藏。

这种方式最大的缺点是:

  1. HTML代码和JS代码混合;
  2. 可扩展性差;

尽管可以通过window.onload绑定事件等方式来是实现JS代码从HTML中分离,也可以把函数改得更复杂以实现通用性。但是总的来说,还是很难做到一处定义到处引用。

后来随着各种JS类库的出现,更强大的Tabs出现了,最出名的就是JQuery UI中的tabs插件。一旦加载了JQuery框架和JQuery UI插件,那么要在页面中实现Tabs,就变得简单了许多。首先我们的页面中的Tabs代码这样写:

<div class="tabs">
<ul>
<li><a href="#panel-1">标签一</a></li>
<li><a href="#panel-2">标签二</a></li>
</ul>
<div id="panel-1">区域一</div>
<div id="panel-2">区域二</div>
</div>

注意:这里的代码非常干净,不含任何的JS代码或者与文档结构无关的定义。然后,在head区域,或者在页面任何地方增加一段js代码:

$(function() {
$(".tabs").tabs();
});

就实现了Tabs功能,这行JS代码执行后,上面的HTML代码就会变成:

<div class="tabs">
<ul class="ui-tabs-nav" jquery1239647486215="2">
<li class="ui-tabs-selected"><a href="#panel-1" jquery1239647486215="8">标签一</a></li>
<li><a href="#panel-2" jquery1239647486215="9">标签二</a></li>
></ul>
<div id="panel-1" class="ui-tabs-panel" jquery1239647486215="4">
区域一
</div>
<div id="panel-2" class="ui-tabs-panel ui-tabs-hide" jquery1239647486215="5">
区域二
</div>
</div>

结合我们自己编写的css,或者jQuery UI 自带的CSS,就可以实现滑动门效果。并且,由于jQuery的强大,我们可以在页面中放置多组滑动门,然后一次性设定。

要说明的是,这个地方由于只启用了jQuery UI 中的 Tabs 插件,因此生成的代码还是比较干净的,只增加了 ui-tabs-xxxx 这几个相关的css类。如果你同时包含了jQuery UI的其它插件,那么即使不启用,也会添加一堆的css定义。而且,jQuery UI Tabs 还提供了非常强大的控制功能,你可以动态地添加 tab,可以随意更改激活事件,可以定义切换效果,还可以设置默认激活状态和禁用等。

但是我在实际应用中遇到了一些问题,除了 jQuery UI 自带的js脚本很大,css不符合实际应用需求外,还有一个最大的问题,你可能已经注意到了,在作为导航的标签定义中,每个标签对应哪一个区域是用链接目 标来定义的。比如 <a href="#panel-1">标签一</a>和<div id="panel-1">区域一</div>对应,如果你的标签和区域没有对应起来,绑定tabs()就不起作用了。

而且,这种方式来带来另一个麻烦,就是当我们需要给标签加上链接的时候,没办法加。即使你将标签的激活事件设置为onmouSEOver而不是onclick,链接也不能实现,因为链接用于指定目标了。这种需求在我们的实际应用中并不是不存在的。比如:
imageimage

这两个图片中的tabs标签,都要添加到对应的新闻类别或者论坛板块的链接。这时候 jQuery UI的默认绑定就带来了麻烦。

其实分析一下,我们在实现滑动门的时候,用以下HTML结构就可以满足需要:

<div class="tabs">
<ul>
<li>标签一</li>
<li>标签二</li>
</ul>
<div>区域一</div>
<div>区域二</div>
</div>

借助 jQuery 库,我们可以通过 $(".tabs")找到要实现的标签,然后 .find("li") 来找到要添加事件的元素,绑定事件的时候,我们可以通过该元素在$(".tabs li")集合中的索引值来明确是哪一个标签被激活,然后对应索引值的panel显示。代码类似这样:

<script type="text/javascript">
$(function() {
$(".tabs").find("li").onmouseover(function(e) {
if (e.target == this) {
var tabs = $(this).parent().children("li");
var panels = $(this).parent().parent().children("div");
var index = $.inArray(this, tabs);
if (panels.eq(index)[0]) {
tabs.removeClass("ui-tabs-selected")
.eq(index).addClass("ui-tabs-selected");
panels.addClass("ui-tabs-hide")
.eq(index).removeClass("ui-tabs-hide");
}
}
});
});
</script>

这段代码只使用了两个css类来处理,并且,自动判断tabs和panels的对应状态,假如你有4个tab,但是只有前三个启用了,那么你只需要写三个panel就可以,第四个panel不存在,则第四个tab自动不生效。

在实际使用中,会遇到一个问题,一般我们会给tab中的文字加链接,那么当鼠标滑过这个tab的时候,如果指到了文字,那么激发事件的对象有可能是a元素而不是li元素,则事件就不能正确激发。所以我们改进代码如下:

<script type="text/javascript">
$(function() {
$(".ui-tabs-nav > li > a")onmouseover(function(e) {
if (e.target == this) {
var tabs = $(this).parent().parent().children("li");
var panels = $(this).parent().parent().parent().children(".ui-tabs-panel");
var index = $.inArray(this, tabs);
if (panels.eq(index)[0]) {
tabs.removeClass("ui-tabs-selected")
.eq(index).addClass("ui-tabs-selected");
panels.addClass("ui-tabs-hide")
.eq(index).removeClass("ui-tabs-hide");
}
}
});
});
</script>

与此对应的HTML结构是:

<div>
<ul class="ui-tabs-nav">
<li class="ui-tabs-selected"><a href="/bbs">论坛新帖</a></li>
<li><a href="/blog">最新博文</a></li>
</ul>
<div class="ui-tabs-panel">
<!--这里调用最新论坛文章-->
</div>
<div class="ui-tabs-panel ui-tabs-hide">
<!--这里调用最新博客文章-->
</div>
</div>

同时,我们有以下的css类定义:

.ui-tabs-nav
{
/*导航容器定义*/
}
.ui-tabs-nav li
{
/*默认标签样式*/
}
.ui-tabs-nav li.ui-tabs-selected
{
/*激活的标签样式*/
}
.ui-tabs-panel
{
/*默认的显示区域样式*/
}
.ui-tabs-hide
{
display: none;
}

这样,就可以根据你的需要,结合自己的 css,定制不同样式的滑动门了。把相应的js代码放到页面中,那么在页面里任何地方只要你按照HTML结构编写了一段html,这段html就会自动变 成滑动门。而不用在每个页面里单独指定特定的selector来应用滑动门的tabs()方法。并且,根据需要给你的滑动门标签添加需要的链接,或者不要 链接(href="#" 或者 href="JavaScript:void(0)")。

这段滑动门代码只要具有jQuery core 就可以正常运行,不需要加载jQuery UI。非常简单,而且很通用,样式上喜欢怎么扩展都可以。

具体效果可以参见 http:/www.taihainet.com , 在 台海网 首页中,我一共应用了四个样式共九组滑动门,代码就只是上面给出的那一段。四个样式列举如下:

image 
滑动门一:多个搜索表单,暂时只实现两个,后面三个由于没有对应的ui-tabs-panel,自动禁用,但是链接可以点击。

image 
滑动门二:多块商务信息区域,其中第三个由于没有对应的ui-tabs-panel,自动禁用。

 

image
滑动门三:新闻栏目切换,标签中的文字链接到对应的新闻栏目。

image
滑动门四:论坛分板块帖子调用。标签中的文字链接到对应的论坛板块。

[杂志]JavaEye论坛热点推荐-2009年3月

mikel阅读(1064)

如果无法通过大米盘下载,点击通过JavaEye本地下载:http://www.javaeye.com/topics/download/ce32dbc6-7116-3724-b3a3-e1160e6dc03e

Java编程和Java企业应用
 

  1. “山寨”框架3宗罪
  2. 刚看了个自制框架的帖子,其实之前这类帖子见得比较多,吵得也都挺厉害。好像总有人一边对提议“不要发明轮子”的同学挺反感,一边以发明家自居,把话扯到中国的软件业上去。之前还看到有人见到“只用SSH的公司“坚决不去。我想说说我的看法。
    我觉得框架这东西,首先要明白为什么要用它——这是废话,但又是最重要的。真的有些人是不知道为什么用框架,乱用一通,产品代码乱七八糟。
    然后是学习如何用好它,掌握框架后接着可能 …
       

  3. 要不要就一起升级JDK6.0?
  4.   背景音乐:《要不要就一起加入G C D》,唱:陈珊妮,词:夏宇。
      JDK5是一个巨大的转折点(泛型,Annotation,并发…),而JDK6就是一个可安全升级的加强版,对新项目是不升白不升,白升谁不升,对旧项目就是升了也白升,不升也没啥损失吧。
      稍微整理了一下官方JDK6官方Features and Enhancement说明

    比较有用的升级
    性能提升
        : …
       

  5. ConcurrentHashMap之实现细节
  6. ConcurrentHashMap是Java 5中支持高并发、高吞吐量的线程安全HashMap实现。在这之前我对ConcurrentHashMap只有一些肤浅的理解,仅知道它采用了多个锁,大 概也足够了。但是在经过一次惨痛的面试经历之后,我觉得必须深入研究它的实现。面试中被问到读是否要加锁,因为读写会发生冲突,我说必须要加锁,我和面试 官也因此发生了冲突,结果可想而知。还是闲话少说,通过仔细阅读源代码, …
       

  7. 为公司写的1个简单的WEB框架,希望大家给点意见
  8. 因公司需要,为公司写了1个很简单的WEB框架,目前已经在公司很多内部系统中使用,希望大家能多给点意见:功能简介:   1. 把前台JSP页面和后面java表单类(这个称呼是自己随便取的,对应一般MVC框架中的C)自动对应起来,比如:先假设所有的表单类都放在 jnet.forms包下,你前台有个index.jsp,那对这个index.jsp的所有操作代码必须放在 jnet.forms.index.java …
       

  9. XMemcached介绍(发布1.0-beta)
  10. xmemcached发布1.0-beta,从0.60直接到1.0-beta,主要改进如下:1、支持更多协议,在已有协议支持的基础上添加了 append、prepend、gets、批量gets、cas协议的支持,具体请查看XMemcachedClient类的实例方法。重点是cas操 作。
    2、memcached分布支持,支持连接多个memcached server,支持简单的余数分布和一致性哈希分布。

       

  11. 实际项目数据下的序列化性能对比:PHPRPC vs Hessian2 vs AMF3
  12. (最新测试结果见第二页:加入AMF3)
    实际项目中的一个企业信息表:15072条记录,测试结果令人吃惊:Hessian胜出太多,不知andot是怎么看,测试过程有误?
    测试过程很简单:读取数据表全部数据至List实例,然后序列化,并将数据保存至本地硬盘。
    测试环境:Oracle10,jdk1.5,3G内存。
    测试结果:
    引用
    开始测试,序列化后二进制数据写进本地文件…
    测试记录总数:15072条 …
       

  13. 解开 phprpc 序列化性能高于 hessian 的秘密
  14. Phprpc, 是一个声称在某些场景下, 性能比hessian还要高的协议.
    下面url是javaeye andot 写的两个协议的性能测试报告:
    http://www.javaeye.com/topic/333720
    看到这个报告是有点激动的, 那么它到底在实现上有何高明之处呢?
    在这篇文章里, 我会给大家简单阐述一下, 为什么phprpc在某些场景下, 性能 …
       

  15. 中文分词 mmseg4j
  16. 使用 paoding 的配置觉得有点复杂,而且管理词库也有点不方便,同时自己也想了解下中文分词算法,然后就想自己写个中文分词器,在lucene和solr中使用。在 javaeye 看到有关 mmseg 的简介,看了下还不错,简单方便,那就用它来写个java版吧,我搜索 mmseg的java版(好像是 solo,官方已经下载不了),弄清分词算法还是自己写写吧。
    实在想不到其它名字,就称它为 mmseg …
       

  17. Spring AOP 简单入门示例
  18. 严重提示:仅供参考
    分享一个自己写的最为简单的Spring AOP的应用,其实,本人也是学习Spring不久,只是把一些个人的理解分享下,供参考。可能很多人刚开始不太理解到底啥是AOP,其实它也是相对 OOP来说的,类似OOP其实也是一种编程思想吧。本人暂且把Spring 中的AOP理解成一种方法的拦截器(可能有所片面,主要是方便理解)。
    个人通俗理解,就好比你去自动取款机取钱,边上装了个摄像头 …
       

  19. 杰哥私房题──约瑟夫问题
  20. 问题描述
    约瑟夫问题:有n只猴子,按顺时针方向围成一圈选大王(编号从1到n),从第1号
    开始报数,一直数到m,数到m的猴子退出圈外,剩下的猴子再接着从1 开始报数。就这样,
    直到圈内只剩下一只猴子时,这个猴子就是猴王,编程求输入n,m后,输出最后猴王的编
    号。
    输入数据
    每行是用空格分开的两个整数,第一个是 n, 第二个是 m ( 0 < m, n < 300)。最后一行
    是:
    0 0

       

  21. JAVA每日一题01
  22. 还是我来继续jythoner的JAVA每日一题吧!最近比较闲哦!希望jythone不要见怪哦!
    真是不好意思啊!早上有点忙!呵呵!
    题目:一项抽奖程序要求读者从整数1-49之间选择6个不同的数字。编写一个程序来完成这项工作,并生成5组结果。
    package com.tengfei.lesson01;
    public class Lottery {
      public stati …
       

  23. jBPM工作流应用
  24. 一、 环境配置
    基础环境是eclipse3+myeclipse6+mySQL5+tomcat6 。
    首先从网站上下载jBPM开发包(jbpm-jpdl-suite-3.2.3.zip),据说现在jBPM3已经升到3.2.6了,而且据说已 经推出了jBPM4了。将jbpm-jpdl-suite-3.2.3.zip解压缩后,里面有个\designer文件夹,这个是eclipse IDE的jBPM插件,是可 …
       

  25. ibatis操作 模板基类设计 实现所有的,增,删,改,查,分页
  26. 上一篇介绍了:hibernate  模板基类设计
    这一次介绍一下 ibatis  模板基类设计 实现公共方法,减少开发工作量
    代码更整洁清淅
    EntityDAO 类代码如下:
    /**
    * (#)EntityDAO.java 1.0 2008-6-7 上午09:25:37
    *
    */
    package com.keyi.util.dao;
    import java.io.Seri …
       

  27. jBPM4.0的用户指南(一)
  28.   项目终于上线了,终于不用天天加班了,于是拿出时间来翻译了下jBPM4.0的文档。根据自己理解翻译的,如果有句子不通顺或者理解有问题的地方,大家可以踊跃提出。
    第 1 章 导言
    1.1. 许可证与最终用户许可协议
    jBPM是依据GNU Lesser General Public License(LGPL)和JBoss End User License Agreement(EULA)中的协议发布的 …
       

  29. jBPM4.0的用户指南(二)
  30. 第 2 章 安装配置
    2.1. 类库
    jBPM 不需要其他任何依赖库就可以在Java虚拟机上运行,参见开发指南的对象执行模式。但是在这个用户指南里我们将介绍使用jBPM的最常用方式。也就是让 jBPM将流程定义、流程实例、历史记录保存在关系数据库里。在这种情况下,hibernate就需要添加在classPath中了,当然还有些jPDL 的功能需要用到其他的类库。因此,将lib这个目录下所有的类库添加 …
       

  31. JAVA语言写的FTP服务器:Apache-FTP Server-1.0.0源码的初步研究
  32. 最近Apache又出了一款JAVA写的新的项目,叫Apache-FTP Server,目前的版本为1.0.0,本文是以比较基础的1.0.0版本来进行最浅白的初步研究
    在http://mina.apache.org/ftpserver/downloads.html可以下到Apache FTP Server的最新的一个版本。直接下ftpserver-1.0.0-src.zip,里面包括发布的版本和源代码 …
       

  33. Java过滤特殊字符的正则表达式
  34. 在网上找了好久也没找到个合适的正则表达式以过滤特殊字符;自己学习了下,写了两个,实现要求。
    // 过滤特殊字符
    public   static   String StringFilter(String   str)   throws   PatternSyntaxException   {  
                    // 只允许字母和数字
                    // …
       

  35. 新工具框架“仓库猫”发布,希望大家多多试用,多多提意见和BUG
  36. 大家好。这是我参考前几天在这里发帖的zhongxuchen提出的“陈氏查询”理论编写的处理动态查询语句的工具框架。因为本人极度爱猫,所以命名为“仓库猫”,英文名翻译得不好,通过金山词霸命名为“catsorage”。
    项目地址:http://code.google.com/p/catstorage/。
    不过因为我还没来得及编写使用帮助,所以目前只提供SVN源代码下载。该框架使用Apache2.0许可证 …
       

  37. 小试ThreadLocal想到“隐式参数”模式
  38. 最近同事想通过自定义函数来输出国际化文字。比如:
    ${my:i18n('login.userid')}.
    EL支持我们自定义这样的函数,问题是这个函数没法获取request对象,不知道当前页面的语言。
    由此我想到threadlocal也许可以解决这个问题。
    我的思路是做一个filter,每次都把request引用保存在一个threadlocal变量里。然后在上述的i18n自定义函数里读取这个thre …
       

  39. 环形布局管理器 + 环形弹出菜单(学习swing的一些小成果)
  40. 最近闲来无事, 学习学习swing。
    在查看JPopMenu的代码时候突发奇想, 想实现一个环形的弹出菜单,说干就干。
    我们都知道, swing 组件的位置和大小是由于layout 管理的,所以想实现环形的弹出菜单就必须实现一个环形的布局管理器。请看我的实现
    效果
    package info.mikewang.gui.layout;
    import static java.lang.Mat …
       

  41. Web Service开发指南 V2.3.1
  42. 纯自己经验的总结,不多说了,见附件吧。
          Ruby
     

  43. 请注意Rails2.3自带的memcache-client有性能问题
  44. Rails2.3版本发布了,这个版本内部的改动非常大,相关介绍可以看JavaEye这篇新闻:http://www.javaeye.com /news/5390,估计最近也有不少人开始动手升级到Rails2.3了,JavaEye也不例外,这一升级才发现性能低得令人发指。
    由于过于信任Rails框架,没有进行本地性能测试,在通过了兼容性测试就兴冲冲上线了。这一上线,动态请求立刻堵了一大堆,仔细看了看fas …
       

  45. Ruby 1.9: 中文编程
  46. 突然想起,Ruby 1.9支持中文方法名和变量名!
    def 召唤 家丁
      case 家丁
      when '阿福', '旺财'
        puts "……少爷,我系#{家丁}……"
      else
        puts '……(一段短短的沉默,然后一段长长的沉默)'
      end
    end
    家丁甲, 家丁乙 = %w[阿福 旺财]
    召唤 家丁甲
    哼哼,我们可以改造黄瓜或者阿死别克了 …
       

  47. JavaEye闲聊客户端(Ruby+Shoes版)
  48. 下载地址:http://javaeye-client-ruby.googlecode.com/files/EyeShoes-0.1-alpha.zip
    代码已经开源:http://javaeye-client-ruby.googlecode.com/svn/trunk/
       

  49. YARV和JIT,还有JRuby……
  50. 昨天承night_stalker老兄的提醒,去google了一下YARV,看看我是不是把事情记错了。记得Ruby还没1.9的时候我就稍微关注过YARV的信息,但印象中Ruby 1.9/YARV是没有JIT的……
    Hmm,我貌似是没记错。目前的Ruby 1.9.1里并没有JIT。
    首先需要定义我这里所指的JIT是什么。JIT,Just-In-Time Compiler,也就是所谓的即时编译器,其过程是 …
       

  51. 如果你升级完Rails 2.3.2 遇到了问题
  52. 最主要的提示是说:
    uninitialized constant ApplicationController
    c:/ruby/lib/ruby/gems/1.8/gems/activesupport-2.3.2/lib/active_support/dependencies.rb:443:in `load_missing_constant'
    c:/ruby/lib/ruby/gems/1.8/g …
       

  53. 建立一个典型的RubyOnRails网站(一)
  54. 写在前面:
        第一次,写连载啊,希望能对自己和各位看官有点帮助,多了解一点RubyOnRails开发上的情况吧。能够有点帮助,这就是坚持的理由。^_^
    生命百科全书:www.eol.org
    网站是基于RoR的,内容是做互动的生命百科博物馆,本文主要介绍,如果通过该网站提供的代码,建立起RoR的网站,并且学习,该网站中使用到的一些,RoR最普通的技术。
    资源地址如下,匿名svn下载。
    svn:/ …
       

  55. Ruby 和 Rails 的 chm 参考手册
  56. ruby手册的原版地址 有各种版本 http://www.ruby-doc.org/
    chm版,方便初学的不习惯rdoc的新手,进行搜索和参考
    虽然版本有点旧 rails 2.0.2 ruby 1.7
    rdoc的版本是新的 rails 2.2.2 ruby 1.8.6
    还是喜欢用chm版的,有时间了应该把另外的也生产chm
       

  57. Ubuntu Linux下ruby 1.9.1和Rails 2.3升级记录
  58. 安装ruby
    首先不再使用ubuntu的apt-get安装ruby,自己编译安装,这样虽然不能使用ubuntu系统apt-get的升级功能对ruby进行升级,但胜在自由度最高,想怎么折腾就怎么折腾.
    到 http://www.ruby-lang.org/zh_CN/downloads/
    下载ruby 1.9.1的源代码.
    #解压文件
    tar  …
          Web前端技术:AJAX和RIA
     

  59. 你应当了解的几个CSS3新技术
  60. 作为一个Web开发者,保持对未来Web标准的关注、学习和了解是很重要的。这里是一些CSS3相关的文章和资源,不管你是第一次准备去认识CSS3,还是已经略知皮毛,下面这些资源都将有助你更好的学习和理解CSS3。
    英文CSS3资源
    CSS3.info
    看域名就知道,这是一个专门介绍CSS3的网站。所有你想了解的有关CSS3的信息,都能在这里找到。CSS3.com
    又一个专门介绍CSS3的网站,包 …
       

  61. Mootools和Jquery之间的选择问题
  62.     Portotype虽然是强大的,和script.aculo.us结合可以做出很多完美的效果,可是它里面有太多我不想用的代码,或许这是我后面选择 mootools的主要原因,我只是想选择性的打包。其实当时我也很想用一下JQuery,仔细对比了一下,我还是觉得mootools更加的适合于我。 有比较,才有机会去选择,我想从下面5个方面讨论一下。
    可读性VS简洁
        在网上,很多人把mootool …
       

  63. Mootools、jQuery之说三道四
  64. 看到很多人在讨论JQuery和Mootools,但是流于分析,理论,我来说说一些实际的体会吧。
    首先摆明立场,我是倾向Mootools的,jQuery的很多东西让我觉得他只是名声在外,下面我会一一说明。
    Prototype公布1.6的时候,我就开始在同时关注jQuery和MT。当时的MT没有jQuery出名,代码也没有jQ的精彩,但是当时他具备了一些很重要的特性,比如样式转换类的CSS,缓动类的Tw …
       

  65. KindEditor 下一步开发计划
  66. KindEditor是简单的在线HTML编辑器,支持目前流行的大部分浏览器,主要优势在于简单、快速、定制方便,非常适合博客、SNS日记、WEB邮箱等互联网网站。
    3.1.1版本初始化时被GZIP压缩以后总大小为22KB,分别为1个JavaScript文件,2个CSS文件,2个gif图片,目前国内的ADSL宽带速度下基本上可以瞬间打开。
    不足的地方是:
    1. 每个浏览器生成的HTML不一致。
    2. U …
       

  67. show一下Extjs+j2ee项目成果(收集开源意见)
  68. 以下介绍的各个系统均采用extjs+easyjweb+spring+jpa+任意数据库构架开发
    蓝源SaaS开源计划:
    http://www.erpwin.com/news.ejf?cmd=showSystemAnnounce&id=7
    在开源人网站平台发布的开源问卷调查:
    http://www.vifir.com/bbs/html/20081213/3276827.html
    蓝源SAAS( …
       

  69. jquery ui 日历插件(换色+类型)
  70. 抽空把jquery ui
    里的日历做了个整理,整合了换色和几种调用方法。
    文件里的example.html是我写的调用页面,所有的颜色主题和调用方法都可以通过这个页面呈现出来。
    example页面,点击查看大图
    一共有17个颜色主题,如下图,点击查看大图
       

  71. 三、DWR配置文件详解与bean转换
  72. DWR配置文件详解与bean转换
    1、了解DWR内置配置文件
    DWR内置配置文件提供了框架配置所需要的一些内容,框架加载的时候把这些信息加载到框架中。dwr内置配置文件也为dwr.xml,和我们在外 面配置的dwr.xml是同一个dtd,但是为什么要内置?因为dwr在加载的时候会去加载这个默认的配置文件,目的是什么?我们先看一下,
    <init>
        <creator id=&q …
       

  73. Javascript工作流引擎代码及实例
  74. 最近在学习jBPM和JavaScript,所以按照一些相关概念自己写了下面的200行代码的“工作流引擎”,工作流管理系统包含了流程定义, 引擎,及应用系统三个主要部分,下面的代码实现了流程的分支合并,目前只支持一种环节上的迁移。下载html附件双击就可以跑起来。
    /*/////////流程定义///////////
      start
       / \
      1   \
    / \   \ …
          移动编程和手机应用开发
     

  75. [原创] 如何在Android Market赚钱 part 1 – paid app 付费app
  76. 随着我的第一个付费app的成功交易, 我想有必要写下Android app如何认证, 发布, 到赚钱的几个流程, 与大家共享, 也希望大家有条件的可以试试开发一些免费或者付费的Android小软件, 以后移动开发的机会会很多, 现在先练练手关于如何开发Android app, 这里就不详细叙述了, 外面相关的文章大把.
    发布app前, 你需要一个google id, 然后要注册成android de …
       

  77. [原创] 如何在Android Market赚钱 part 2 – 免费app附带广告
  78. 如果你的app用户量潜力上来说是巨大的, 而且用户会时常使用你的app的, 比如twitter客户端, SNS插件等等, 你可以考虑用广告的收入方式而发布免费的app.
    说起广告banners, 大家一定不会陌生了.  最流行的就是google adsense.  但本人对adsense的印象很不好.  自己有个adsense的帐号, 但时不时广告都会变成公益广告, 而且收入奇低, -__-#至今未 …
       

  79. [原创] 如何在iPhone AppStore赚钱 part 1 – 付费app
  80. iPhone无疑是目前最炙热的开发平台之一.  开发者只需要好好的做自己的app, 销售收款物流交易和发布渠道全部由Apple搞定, 收入37开, Apple 3, 开发者7.  比起北美这边运营商动不动就40+%强行占有开发者的收入, Apple算是很厚道了.  无疑, 初期几个很成功的app让一批人赚了第一桶金子.  当然大家都跃跃欲试的时候, iPhone的开发也就有点开始白菜化的趋势.  但 …
          Microsoft .Net
     

  81. [Ray Linn]用Visual Studio 2008开发IE BHO  (浏览器帮助对象)之一
  82. 这篇文章是应同学们的要求写的,以前都是用VC++ 6.0+Platform SDK完成的. 迁移到 VS2008之后,原来Visual Studio 6.0里的BHO向导不复存在,因此特此不厌其烦,详细说明,本文也适用于VS2005.
    首先谈BHO的开发工具,我偏向使用VC++(unmanaged C++) 作为开发工具,因为Java JVM或.Net CLR的虚拟机是个很笨重的东西,也是内存杀手, …
          Python
     

  83. 写了个开心网查看朋友果实的小程序
  84. 用python2.6写的,用2.5跑可能要改里面json相关部分
    其实就是菜园旁边那个按钮的功能,只是没那么多时间老去打开浏览器看那个东西,所以做了这个
    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    # coded by Daniel King
    import re, time, thread, webbrowser
    from Tkinter i …
       

  85. python vs ruby
  86. 1.python是弱对象语言, 而ruby是纯对象语言
    python可以没有对象运行,但是ruby的根基就是Object。
    而且python对于封装的约束比较弱,而ruby相对较强。
    比如一个class的实例dog。
    python:dog.name = "dogy" 相当于给dog赋予了一个属性
    ruby: dog.name = "dogy" 报错,dog没有 …
       

  87. python vs javascript
  88. python的结构体(包括class,function 和class 实例)和javascript的结构体(Object和function)实现有些相似,也有些不同。
    python的class对数据和函数的存储都是维绕着__dict__.
    对象的属性和方法都存在里面。
    而javascript的对象(Object或function)应该有两个hash存储数据,一个是prototype,一个是xxx(现 …
          综合技术
     

  89. Memcached源码分析(线程模型)
  90. 目前网上关于memcached的分析主要是内存管理部分,下面对memcached的线程模型做下简单分析
    有不对的地方还请大家指正,对memcahced和libevent不熟悉的请先google之
    先看下memcahced启动时线程处理的流程
    memcached的多线程主要是通过实例化多个libevent实现的,分别是一个主线程和n个workers线程
    无论是主线程还是workers线程全部通过lib …
       

  91. D语言编译器DMD开源了!
  92.   自从1999年12月,Walter设计开发D语言以来,D语言的编译器DMD因为不开源,成了业界的诟病。Walter曾经承诺,将开放源代码。    伟大的Walter Bright在今天2009年3月5日下午18点40分(中国时间) 释放出了D1和D2的前端和后端的源代码!!开源协议为GPL1.0或者Artistic license。后端协议看了看,有Digital Mars和SYMANTEC的版 …
       

  93. KeyValue DB之redis
  94. redis,是快客王在je chat中介绍过的一个keyvalue数据库,打着在'入门级服务器'上10w/s set ops高性能头衔来忽悠众多不明真相的程序猿。
    事实上,号称的'入门级服务器'服务器是一台 Xeon X3320 2.5Ghz 的机器,在我的
    AMD Athlon(tm) 64 X2 Dual Core Processor 4000+ 台式机上,benchmark结果是
    引用==== …
       

  95. 读“DataBase Sharding at Netlog”,看DataBase Scale Out
  96.        今天看了“Database Sharding at Netlog, with MySQL and PHP”一文,和去年我们讨论扩展的思路很类似(不过这种分布式扩展,计算,存储的思路都很类似),但是这片文章的作者是在日益爆炸式增长的用户数据下实 践的分享,因此这里将文中的一些思想记录下来分享一下。
           Netlog拥有4000万活跃用户,每个月有超过5000万的独立用户访问网站 …
          软件开发和项目管理
     

  97. 一个时间进度安排比较急的项目,组员却水平有限
  98. 公司新接到一个项目,需求设计之后,便是编码阶段
    编码之前,开发成员除了美工之外有3个人,一个是我,2个同事,从需求,和设计进行了介绍,也提供了相关的文档。安排了进度之后,大家没有问题,便开始开发。
    时间安排比较紧凑,因为客户比较急。
    采用的是非常普遍的SSH,用acegi做的权限这块,另外两个组员我也不是很了解,所以安排了2个比较简单的,对数据库的一个表进行增删改查,其中有一对多或者多对一的关系,我 …
       

  99. TDD是不是以开发效率换代码质量(原标题:单元测试/TDD的成本和收益)
  100.     最近我在公司搞代码评审,做的过程中发现一个矛盾的问题:评审发现了问题,于是需要重构,可是重构需要有完善的单元测试做保障,而项目已接近开发结束,基本没有单元测试,结果发现的问题只能搁置,因为你很难下决心去为了完善一个东西而去冒毁坏它的风险!
        这样下去,代码评审将流于形式
        我意识到TDD与code review有着很紧密的联系,其实以前就听说过敏捷的十二个实践都是有内在联系的。

       

  101. 也谈IT人员流失问题 王泽宾
  102.     一个公司或者团队都会遇到过人员流失的问题,可能小公司可能更为严重。最可怕的是团队中重要成员的流失,将对公司的业务或者项目造成不小的麻烦。究竟怎么看待这个问题呢?很多人都发表过自己的见解,争论也很大。
        俗话说:“屁股决定脑袋”,其实大家的立场不同,结论自然不同。
        很多管理者,会抱怨员工没有职业道德,经不住诱惑;有的人抱怨猎头太可恨,扰乱IT就业市场;还有的人指责同行不厚道,拿高 …
          入门讨论
     

  103. 集合比较算法(Java)
  104. 最近做了一个小测试,对两个集合的比较,目的是想删除出两个集合相同的数据。
    分别用List、Map、和Set进行测试
    利用List比较
    10000用户的数据(6000相同的用户,4000不同的用户),完成比较的时间共耗时1531毫秒
    100000用户的数据(60000相同的用户,40000不同的用户),完成比较的时间共耗时143735毫秒
    利用Map比较
    10000用户的数据(60000相同的用户,4 …
       

  105. Struts+Spring+Hibernate学后感
  106.      最近利用时间好好的看了一下SSH,Struts,Spring,Hibernate,并且做了个小项目,我把我个人学习的感觉和理解记了下来,和大家讨论讨论,想来个抛砖引玉,希望大家多多讨论,理解的不对的地方还请直言赐教。
         Struts,负责表示层的,主要玩的就是MVC,主要的就是struts-config.xml配置文件,另外还有标签和验证。主要负责的就是把请求和回复分开,形成层状结 …
       

  107. javamail发送邮件
  108. 今天学习了一下JavaMail,javamail发送邮件确实是一个比较麻烦的问题。为了以后使用方便,自己写了段代码,打成jar包,以方便 以后使用。呵呵以下三段代码是我的全部代码,朋友们如果想用,直接复制即可。jar包因为我不知道怎么传到javaeye上,所以朋友们回去自己打吧。
    我的代码有三个类:
    第一个类:MailSenderInfo.java
    package com.util.mail;
    /* …
       

  109. Dtree+Jquery动态生成树节点例子《新手可参考》
  110. Dtree+Jquery动态生成树节点.
    最近有朋友问我一个动态生成树怎么做.后来就用Dtree  js组件和Jquery实现了一个demo给他.现在贴出来给初学者做些参考.
    1.先来介绍一下. dtree 的用法.(我引用了以前我收集的一篇文章.还比较详细,出处不记得啦).文章下面会附带dtree用法的例子.
             Dtree目录树的总结
                      一:函 …
       

  111. 被Groovy震的第一下
  112.     呵呵,下面这段代码,看起来很眼熟吧?猜猜输出的是什么?
    println new URL("http://manyinjin.javaeye.com").openConnection().getInputStream().getText();
        简单的一行语句,blog里面的东西都print了,重要的是,我只需要知道java里怎么做就行了。。。
        你甚至可以这 …
       

  113. 彻底被groovy折服
  114. 被groovy彻底征服:
    先瞅瞅人家groovy是怎样解析xml的:
    plan.xml
    <plan>
    <week capacity="8">
    <task done="2" total="2" title="read XML chapter" />
    <task done= …

[SQL]解决SQL Server配置管理器提示“无法连接到WMI提供程序”问题

mikel阅读(851)

出错提示:启动SQL Server 2005 配置管理器时,出现以下提示错误

              无法连接到WMI提供程序。您没有权限或者该服务器无法访问……

              Invalid class [0x80041010]

解决方法

以管理员身份运行“命令提示符”(在附件下面),进入后执行以下命令

   1)cd /d    C:\Program Files\Microsoft SQL Server\90\Shared

   2)mofcomp "C:\Program Files\Microsoft SQL Server\90\Shared\sqlmgmproviderxpsp2up.mof"

当看到有如下信息时表明操作成功

MOF file has been successfully parsed
Storing data in the repository…
Done!

参考文章http://blogs.msdn.com/echarran/archive/2006/01/03/509061.aspx

[JavaScript]JavaScript刷新页面方法

mikel阅读(830)

1.无提示刷新网页

大家有没有发现,有些网页,刷新的时候,会弹出一个提示窗口,点“确定”才会刷新。

而有的页面不会提示,不弹出提示窗口,直接就刷新了.

如果页面没有form,则不会弹出提示窗口。如果页面有form表单,

a)< form method="post" …> 会弹出提示窗口

b)< form method="get" …> 不会弹出

2.javascript刷新页面的方法

window.location.reload();

使用window.open()弹出的弹出窗口,刷新父窗口

window.opener.location.reload()

使用window.showDialog弹出的模式窗口

window.dialogArguments.location.reload();

3.javascript弹出窗口代码

下面给两个弹出屏幕居中窗口的例子

window.open()方式

function ShowDialog(url) {
            var iWidth=300; //窗口宽度
            var iHeight=200;//窗口高度
            var iTop=(window.screen.height-iHeight)/2;
            var iLeft=(window.screen.width-iWidth)/2;
            window.open(
            url,"Detail","Scrollbars=no,Toolbar=no,Location=no,Direction=no,Resizeable=no,
            Width="+iWidth+" ,Height="+iHeight+",top="+iTop+",left="+iLeft
            );
           }

window.showModalDialog方式

function ShowDialog(url) {
            var iWidth=300; //窗口宽度
            var iHeight=200;//窗口高度
            var iTop=(window.screen.height-iHeight)/2;
            var iLeft=(window.screen.width-iWidth)/2;
            window.showModalDialog(
            url,window,"dialogHeight: "+iHeight+"px;dialogWidth: "+iWidth+"px;
            dialogTop: "+iTop+"; dialogLeft: "+iLeft+"; resizable: no; status: no;scroll:no"
);
          }

注意这里的第二个参数,window

4.模式窗口数据不刷新(缓存)问题

在jsp页面加入如下语句

<%
       response.setHeader("Pragma","No-Cache");
       response.setHeader("Cache-Control","No-Cache");
       response.setDateHeader("Expires", 0);
%>

5.模式窗口中,链接弹出新窗口问题

在< /head >和< body >间加入< base target="_self" >

6.无提示关闭页面的方法

function CloseWin(){
     var ua = navigator.userAgent; var ie = navigator.appName==
"Microsoft Internet Explorer"?true:false;
     if(ie){
var IEversion = parseFloat(ua.substring(ua.indexOf("MSIE ")+5,
ua.indexOf(";",ua.indexOf("MSIE "))));
     if( IEversion< 5.5){
     var str = '';
     document.body.insertAdjacentHTML("beforeEnd", str);
     document.all.noTipClose.Click();
    } else {
     window.opener =null; window.close();
    }
   }else{
   window.close()
   }
}

[SQL]TSQL查询内幕::(2.3)查询计划与更新计划

mikel阅读(904)

显示计划(Showplan)是表示由查询优化器生成的文本、图形或XML格式的查询计划的术语。他包含了有关SQL Server如何处理查询的信息,对查询计划中的每个表,显示计划可以告诉你是否使用了索引,或者是否有必要执行表扫描,以及不同操作的执行顺序。

在本系列随笔的2.1中对一个显示计划做了初步的分析。

 

SQL Server2005可以生成三种不同格式的显示计划:图形、文本和XML

在计划内容方面,SQL Server可以生成只包含运算符的计划,包含估计成本的计划,以及包含运行时信息的计划。下表列出了生成不同格式计划的命令:

 

内容

格式

文本

XML

图形

运算符

SET SHOWPLAN_TEXT ON

N/A

N/A

运算符和估计成本

SET SHOWPLAN_ALL ON

SET SHOWPLAN_XML ON

在企业管理器中“显示估计的执行计划”

运行时信息

SET STATISTICS PROFILE ON

SET STATISTICS XML ON

在企业管理器中“包含实际的执行计划”

 

首先看一个简单的查询:

SET NOCOUNT ON;

USE Northwind;

GO

SET SHOWPLAN_TEXT ON;

GO

Select ProductName, Products.ProductID

FROM dbo.[Order Details]

 JOIN dbo.Products

    ON [Order Details].ProductID = Products.ProductID

Where Products.UnitPrice > 100;

GO

SET SHOWPLAN_TEXT OFF;

GO

 

运行结果:

StmtText

——————————————————————————————————————————————————————————–

Select ProductName, Products.ProductID

FROM dbo.[Order Details]

 JOIN dbo.Products

    ON [Order Details].ProductID = Products.ProductID

Where Products.UnitPrice > 100;

 

StmtText

——————————————————————————————————————————————————————————————–

 |–Nested Loops(Inner Join, OUTER REFERENCES:([Northwind].[dbo].[Products].[ProductID]))

       |–Clustered Index Scan(OBJECT:([Northwind].[dbo].[Products].[PK_Products]), Where:([Northwind].[dbo].[Products].[UnitPrice]>($100.0000)))

       |–Index Seek(OBJECT:([Northwind].[dbo].[Order Details].[ProductID]), SEEK:([Northwind].[dbo].[Order Details].[ProductID]=[Northwind].[dbo].[Products].[ProductID]) orDERED FORWARD)

 

输出结果表明:该查询由三个运算符组成:Nested LoopsClustered Index ScanIndex Seek

 

首先看第一句:

|–Nested Loops(Inner Join, OUTER REFERENCES:([Northwind].[dbo].[Products].[ProductID]))

Nested Loops对两个表进行内部联接,且外部表为Products表。

然后是:

|–Clustered Index Scan(OBJECT:([Northwind].[dbo].[Products].[PK_Products]), Where:([Northwind].[dbo].[Products].[UnitPrice]>($100.0000)))

SQL Server使用Clustered Index Scan访问物理数据,这里扫描聚集索引相当于描述整个表。

最后是:

|–Index Seek(OBJECT:([Northwind].[dbo].[Order Details].[ProductID]), SEEK:([Northwind].[dbo].[Order Details].[ProductID]=[Northwind].[dbo].[Products].[ProductID]) orDERED FORWARD)

SQL Server使用Index Seek访问索引行,其中的Object显示了索引的完整名称。Seek是查找谓词,这里是要根据外部表的ProductID来进行索引查找。

 

当执行计划时,数据的传递通常是从右到左,从上到下的。缩进多的运算符生成行供缩进少的运算符使用。在这里,Clustered Index Scan运算符和Index Seek运算符是缩进最多的,且Clustered Index ScanIndex Seek的上面,所以先运算Clustered Index Scan,然后是Index Seek,最后是Nested Loops

 

运行示例还可以注意到,当使用了SET SHOWPLAN_TEXT ON后,会阻止执行查询。

 

XML格式的显示计划

有两种格式的显示计划。一个是SET SHOWPLAN_XML ON,他将包括估计的执行计划。另一个是SET STATISTICS XML ON,他包含运行时的信息。

 

生成XML格式的显示计划可以通过以下方法:

1. 上面写的这两个指令,其中SET SHOWPLAN_XML ON在编译批处理时生成,它为整个批处理生成一个XML文档;而SET STATISTICS XML ON在运行时产生输出,他为批处理中的每个语句生成单独的XML文档。

2. 使用企业管理器的“显示图形化显示计划”。

3. 使用SQL Server Profiler

 

名为Showplanxml.xsdXML Schema描述了包括编译时估计、运行时的XML显示计划。在运行时,XML显示计划提供了一些额外的信息。这个文件在安装完SQL Server 2005后,被放置在Microsoft SQL Server"90"tools"Binn"schemas"SQLServer"2004"07"showplan目录下。

 

XML格式的显示计划的内容最详细,包含了计划大小(CachePlanSize属性),和优化该计划是用到的参数值(ParameterList元素),而且只有运行时XML显示计划才包含并行计划中不同线程所处理的行数(RunTimeCountersPerThread元素的ActualRows属性),以及执行查询时的实际平行度(DegreeOfParallelism属性)

 

 

图形化的显示计划

在企业管理器中,有“显示估计的执行计划”和“包括实际的执行计划”两种图形化的显示方法。

显示估计的执行计划:点选后,在结果窗口会立刻显示图形化执行计划。

包含实际的执行计划:点选后,不会立刻显示计划,而是在点击执行后,将实际的计划结果显示出来。

 

无论使用上面哪种方式图形显示计划,都会显示下面的图形:

但可以想到他们之间存在的区别,当查看运算符的详细信息时:

未标题-2.png未标题-2.png

因为“包含实际的执行计划”,所以会给出实际运行时的一些信息。

右键->计划另存为,还可以将显示计划保存为XML显示计划。扩展名为sqlplan

 

显示计划中的运行时信息

SET STATISTICS XML ON|OFF

XML显示计划包含两种运行时信息:每个SQL语句的信息和每个线程的信息。如果语句有参数,它的计划将包含ParameterRuntimeValue属性,表示该语句被执行时每个参数的值。它可能不同于编译该语句时用到的值(ParameterCompiledValue属性),但只有优化器在优化并知道该参数值时,该属性才会出现在计划中,且只与传递到存储过程的参数有关。

 

DegreeOfParallelism属性表示此次执行的实际平行度(DOP,它是单个查询的并发线程数)。它可能与编译时计算的值不同,在编译期间,查询优化器假设为当时的工作负荷为CPU的一半。在执行时,DOP的值会根据执行时开始的工作负荷被调整。如果执行并行计划时DOP=1,当创建执行上下文时,SQL Server将从查询计划中移除Exchange运算符。MemoryGrant属性表示以KB为单位的用于执行该查询的实际内存。SQL Server使用这些内存为哈希联接(hash Join)生成哈希表或在内存中执行排序。

 

RunTimeCountersPerThread元素包含5个属性,每个线程都有相应的值:ActualRebindsActualRewindsActualRowsActualEndofScansActualExecutionsActualExecutions值告诉我们该运算符在每个线程中被初始化的次数。如果运算符是一个扫描运算符,ActualEndofScans表示扫描到达数据集结尾的次数。所以用ActualExecutions-ActualEndofScans就可以得到运算符没有扫描的次数。如:如果Select中使用TOP限定了返回的行数,则输出集合将在扫描到达表的结尾之前被收集。

 

SET STATISTICS PROFILE ON

这个指令返回的计划与SET SHOWPLAN_ALL ON相比有两个区别。他在输出中包含了另外的两列:RowsExecutes

Rows :是所有线程的RunTimeCountersPerThread元素RowCount属性的合计,他告诉我们每个运算符实际返回的行数。

Executes:是该元素中ActualExecutions属性的合计,他告诉我们SQL Server为处理一行或多行而初始化该运算符的次数。

 

当检查某个查询计划时,可以找到查询优化器的估计行数与实际行数之间的最大差异。EstimateRows列是每次执行所估计的输出行数,而Rows是运算符所有执行返回的累积行数。因此我们可以先把EstimateRows乘以EstimateExcutions,在把它与SET STATISTICS PROFILE输出的Roes列中返回的实际总行数做比较。

 

SQL跟踪捕获显示计划

使用跟踪来捕获显示计划是非常精确的,这样可以避免在企业管理器中观察到的计划和在应用程序执行起来使用的实际计划之间产生的差异。最常见的,如:用不同的参数调用同一个存储过程、统计信息被自动更新、在编译和运行之间的可用资源(CPU或内存)发生变化。

使用跟踪进行监视非常消耗资源,监视的事件越多,影响越严重。

下表显示了9类性能事件:

跟踪事件类

编译或运行

是否包含运行时信息

是否包含XML显示计划

是否为SQL Server 2000生成跟踪

Showplan ALL

运行

Showplan All for Query Compile

编译

Showplan Statistics Profile

运行

Showplan Text

运行

Showplan Text(未编码)

运行

Showplan XML

运行

Showplan XML for Query Compile

编译

Showplan XML Statistics Profile

运行

Performance Statistics

编译和运行

 

如果在开发或调试中,应该使用Showplan XML Statistics Profile事件。它生成所有的查询计划和运行时信息。

 

即使你的服务器很忙,如果设计了有良好的计划重用率的查询,因为编译率较低,也可以使用Showplan XML For Query Compile事件。他只在有存储过程或语句被编译或重新编译时才生成跟踪记录,但不包括运行时信息。

 

通过为各个列设置筛选值可以减少跟踪的大小。在设置跟踪筛选器时,只有ApplicationNameClientProcessIDHostNameLogionNameLogionSidNTDomainNameNTUserNameSPID这些列上应用筛选器会抑制时间的生成。其他筛选器只有在时间被生成并到达客户端后才会应用,所以并不能减少服务器的开销,事实上会造成更多的开销。

 

另外,相对于在企业管理器中使用显示计划,显示计划跟踪事件进一步扩大了SQL Server所能捕获计划的语句集合。如:CreateInsert INTO … EXEC语句等。

 

从过程缓存中提取显示计划

上面已经介绍,SQL Server当生成计划后,会把它保存到过程缓存之中。我们可以用几个动态管理视图和函数、DBCC PROCCACHE、以及目录视图sys.syscacheobjects来检查过程缓存。

 

Sys.dm_exec_query_plan (DMF)XML格式返回位于过程缓存的计划。DMF要求一个计划句柄作为唯一的参数。计划句柄是一个VARBINARY(64)类型的查询计划标识符,DMV为当前过程缓存中每个查询都可以返回该标识符。

看一个示例:

Select qplan.query_plan AS [Query Plan]

FROM sys.dm_exec_query_stats AS qstats

 CROSS APPLY sys.dm_exec_query_plan(qstats.plan_handle) AS qplan;

这个查询为所有缓存的查询计划返回XML显示计划。

 

但是想这样找到某个查询计划非常困难,因为查询文本被包含在XML显示计划内部。下面的查询使用Xquery value方法从显示计划中提取出序列号(No)和查询文本(Statement Text)。因为每个批处理都有sql_handle,所以用ORDER BY sql_handle可以按这些语句在批处理中的顺序进行排序显示。

WITH XMLNAMESPACES ('http://schemas.microsoft.com/SQLServer/2004/07/showplan' AS sql)

Select

 C.value('@StatementId', 'INT') AS [No],

 C.value('(./@StatementText)', 'NVARCHAR(MAX)') AS [Statement Text],

 qplan.query_plan AS [Query Plan]

FROM (Select DISTINCT plan_handle FROM sys.dm_exec_query_stats) AS qstats

 CROSS APPLY sys.dm_exec_query_plan(qstats.plan_handle) AS qplan

 CROSS APPLY query_plan.nodes('/sql:ShowPlanXML/sql:BatchSequence/sql:Batch/ sql:Statements/ descendant::*[attribute::StatementText]') AS T(C)

ORDER BY plan_handle, [No];

运行结果:

 

更新计划

当优化InsertUpdateDelete这些数据修改语句时,优化器必须要处理几个特殊的问题。IUD计划(InsertUpdateDelete)包括两个阶段。

 

第一个阶段:通过生成用于描述数据更改的数据流来确定哪些行将被IUD对于Insert,数据流包含列值,对于Delete,它包含表键,对于Update,他既包含表键也包含被修改的列的值。

 

第二个阶段,把数据流中的描述的更改应用到表,通过执行约束验证保证数据完整性,它维护非聚集索引和索引视图,如果存在触发器则引发触发器。

UpdateDelete查询计划通常还包含两个对目标表的引用:第一个引用用于标识受影响的行,第二个引用执行更改的地方。Insert计划只包含一个对目标表的引用。

在一些简单的情况是,SQL ServerIUD计划的两个阶段合并在一起。如:把值直接插入表,成为标量插入,或者更新/删除由目标表主键标识的行。

 

如果SQL Server需要执行约束验证,则在第二个阶段会自动包含Assert运算符。SQL Server通过在受影响的行和列上计算一个通常成本较低的标量表达式为InsertUpdate验证CHECK约束。

 

对包含外键约束的表执行InsertUpdate会强制验证CHKECK约束,而且对包含被引用的表所执行的InsertUpdate也会强制验证外键约束。为验证约束,即使不是IUD操作目标的相关表也被扫描。声明主键将自动地在该列创建唯一的索引,但外键不一样,对被引用的外键列执行UpdateDelete必须为每个被更新或删除的主键值访问外键表。如果这个约束是一个级联引用完整性约束,那么将会执行更改,否则将验证被删除的键是否存在。因此,要对键值执行Update或对主表执行Delete,那么应该确保外键上存在索引。

 

在处理InsertDelete语句时,除了在聚集索引或堆上执行IUD操作,还会维护所有非聚集索引,Update查 询还维护包含被修改列的索引。因为非聚集索引包含聚集索引键和分区键以允许高效地访问数据行,所以更新那些参与聚集索引键或分区键的列成本很高,因为它会 修改或重建所有索引。更新分区键还会导致行在分区之间的移动。因此,如果可能的话,应该选择不更新的列作为聚集键或分区键。

 

总的来说,IUD语句的性能与包含目标列的索引数量密切相关,因为他们将会被重建或修改。对索引执行单行InsertDelete操作要求遍历一次索引树。SQL Server更新索引键和分区键的方法是先执行Delete再执行Insert,所以在索引操作上,Update的成本比InsertDelete要多一倍。

 

查询优化器执行IUD语句时有两种不同的策略:每行维护和每索引维护。

首先使用这个文件准备参考数据:3.rar

然后来看两个查询

查询1

Delete FROM dbo.Orders Where orderDate = '2002-01-01'

更新计划:

这就是一个每行查询的例子,SQL Server为该查询所影响的每一行同时维护索引和基表(基表=堆或聚集索引),并且对所有非聚集索引的更新将与对基表中每一行的更新同时执行。这个查询计划没有对第2个索引执行任何删除操作,因为这些工作在聚集索引删除运算符的计算是同时完成。

 

查询2

Delete FROM dbo.Orders Where orderDate < '2006-01-01'

更新计划:

这个查询2与查询1的更新计划完全不同,因为它执行的是每索引维护。

首先,该计划从聚集索引中删除符合条件的行,同时构建一个临时的假脱机表(spool table),其中包含必须进行维护的三个非聚集索引的键值。SQL Server为每个索引读取一次假脱机数据。在读取假脱机数据和从非聚集索引中删除行之间,SQL Server按被维护索引的顺序排序假脱机数据,以确保对索引页的最佳访问。

Sequence运算符强制其分支的执行顺序,在这里索引的按从上倒下的顺序进行删除。

 

每行更新策略在CPU方面是高效的,因为同时更新表和所有索引只需很短的代码路径。每索引维护的代码稍微有些复杂,但这样更节省I/O。如果对键排序后再单独更新非聚集索引,即使同一页中有许多行都被更新,我们也只须访问索引页一次。这也就是为什么SQL Server查询优化器认为每行策略需要多次读取被维护索引的同一页才能完成维护,这时它通常选择每索引维护策略。

[ASP.Net]翻译-ASP.NET MVC Contact Manager开发之旅迭代4 -

mikel阅读(705)

本翻译系列为asp.net mvc官方实 例教程。在这个系列中,Stephen Walther将演示如何通过ASP.NET MVC framework结合单元测试、TDD、Ajax、软件设计原则及设计模式创建一个完整的Contact Manager应用。本系列共七个章节,也是七次迭代过程。本人将陆续对其进行翻译并发布出来,希望能对学习ASP.NET MVC 的各位有所帮助。由于本人也是个MVC菜鸟,且E文水平亦是平平,文中如有疏漏敬请见谅。
注:为保证可读性,文中Controller、View、Model、Route、Action等ASP.NET MVC核心单词均未翻译。

 

ContactManager开发之旅-索引页

ContactManager开发之旅 迭代1 – 创建应用程序

ContactManager开发之旅 迭代2 – 修改样式,美化应用

ContactManager开发之旅 迭代3 – 验证表单

迭代4 利用设计模式松散耦合

本次迭代

这是ContactManager的第四次迭代,本次迭代中我们将重构应用程序,通过合理的利用设计模式松散其耦合。松耦合的程序更有弹性,更易维 护。当应用程序面临改动时,你只需修改某一部分的代码,而不会出现大量修改与其耦合严重的相关代码这种牵一发而动全身的情况。

在当前的ContactManager应用中,所有的数据存储及验证逻辑都分布在controller类中,这并不是个好主意。要知道这种情况下一 旦你需要修改其中一部分代码,你将同时面临为其他部分增加bug的风险。比如你需要修改验证逻辑,你就必须承担数据存储或controller部分的逻辑 会随之出现问题的风险。

(SRP-单一职责原则), 就一个类而言,应该只专注于做一件事和仅有一个引起它变化的原因。将controller、验证及数据存储逻辑耦合在一起严重的违反了SRP。

需求的变更,个人想法的升华、始料未及的情况如此频繁,你不得不就当前的应用作出一些改变:为应用程序添加新功能、修复bug、修改应用中某个功能的实现等。就应用程序而言,它们很难处于一种静止不动的状态,他们无时无刻在被不停的改变、改善。

现在的情况是,ContactManager应用中使用了Microsoft Entity Framework处理数据通信。想象一下,你决定对数据存储层实现做出一些改变,你希望使用其它的一些方案:如ADP.NET Data Services或NHibernate。由于数据存储相关的代码并不独立于验证及controller中的代码,你将无法避免修改那些原本应该毫无干系 的代码。

而另一方面,对于一个松耦合的程序来说,上面的问题就不存在了。一个经典的场景是:你可以随意切换数据存储方案而不用管验证逻辑、controller中的那些劳什子代码。

在这次迭代中,我们将利用软件设计模式的优点重构我们的Contact Manager应用程序,使其符合我们上面提到的“松耦合”的要求。尽管做完这些以后,我们的应用程序并不会表现的与以往有任何不同,但是我们从此便可轻松驾驭其未来的维护及修改过程。

重构就是指在不改变程序外在行为的前提下,对代码做出修改,改进程序内部结构的过程。

 

使用Repository模式

我们的第一个改动便是使用叫做Repository的设计模式改善我们的应用。我们将使用这个模式将数据存储相关的代码与我们应用中的其他逻辑独立开来。

要实现Repository模式,我们需要做以下两件事

  1. 新建一个接口
  2. 新建一个类实现上面的接口。

首先,我们需要新建一个接口约定所有我们需要实现的数据存储方法。IContactManagerRepository接口代码如下。这个接口约定 了五个方法:CreateContact()、DeleteContact()、EditContact()、GetContact()及 ListContacts()方法:

using System;
using System.Collections.Generic;
namespace ContactManager.Models
{
public interface IContactRepository
{
Contact CreateContact(Contact contactToCreate);
void DeleteContact(Contact contactToDelete);
Contact EditContact(Contact contactToUpdate);
Contact GetContact(int id);
IEnumerable<Contact> ListContacts();
}
}

接着,我们需要新建一个具体的类来实现IContactManagerRepositoyr接口。由于我们这里使用Microsoft Entity Framework操作数据库,所以我们为这个类命名为“EntityContactManagerRepository”,这个类的代码如下:

using System.Collections.Generic;
using System.Linq;
namespace ContactManager.Models
{
public class EntityContactManagerRepository : ContactManager.Models.IContactManagerRepository
{
private ContactManagerDBEntities _entities = new ContactManagerDBEntities();
public Contact GetContact(int id)
{
return (from c in _entities.ContactSet
where c.Id == id
select c).FirstOrDefault();
}
public IEnumerable<Contact> ListContacts()
{
return _entities.ContactSet.ToList();
}
public Contact CreateContact(Contact contactToCreate)
{
_entities.AddToContactSet(contactToCreate);
_entities.SaveChanges();
return contactToCreate;
}
public Contact EditContact(Contact contactToEdit)
{
var originalContact = GetContact(contactToEdit.Id);
_entities.ApplyPropertyChanges(originalContact.EntityKey.EntitySetName, contactToEdit);
_entities.SaveChanges();
return contactToEdit;
}
public void DeleteContact(Contact contactToDelete)
{
var originalContact = GetContact(contactToDelete.Id);
_entities.DeleteObject(originalContact);
_entities.SaveChanges();
}
}
}

注意,EntityContactManagerRepository类实现了IContactManagerRepository接口约定的5个方法。

为什么我们一定要要建立个接口再建立一个类来实现它呢?

应用程序中的其他部分将与接口而不是具体的类进行交互。也就是说,它们将调用接口声明方法而不是具体的类中的方法。

所以,我们可以以一个新的类实现某个接口但不用修改应用程序中其他的部分。例如,将来我们可能需要建立一个 DataServicesContactManagerRepository类实现IContactManagerRepository接口。 DataServicesContactManagerRepository类使用ADO.NET Data Services,我们用它代替Microsoft Entity Framework.与数据库通信进行数据存储。

如果我们的应用程序代码是基于IContactManagerRepository接口而不是 EntityContactManagerRepository这个具体的类,那么我们可以只改变不同的类名而非代码中的其他部分。例如我们可以将 EntityContactManagerRepository修改成DataServicesContactManagerRepository而不用 去碰数据存储和验证逻辑相关的代码。

面向接口(虚类)编程使我们的应用程序更有弹性,更易修改。

通过在VS中选择“重构”菜单->“提取接口”,你可以根据一个具体的类方便快速的创建出一个与之对应的接口。例如你可以先建立一个 EntityContactManagerRepository类,然后使用如上文所述的方法自动生成 IContactManagerRepository接口。

使用依赖注入

现在,我们已经将数据访问相关的代码独立到了Repository类中。而后,我们需要修改Contact controller以适应这些改变。这里我们将使用依赖注入的方式。

修改后的Contact controller代码如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using System.Web;
using System.Web.Mvc;
using System.Web.Mvc.Ajax;
using ContactManager.Models;
namespace ContactManager.Controllers
{
public class ContactController : Controller
{
private IContactManagerRepository _repository;
public ContactController()
: this(new EntityContactManagerRepository()) { }
public ContactController(IContactManagerRepository repository)
{
_repository = repository;
}
protected void ValidateContact(Contact contactToValidate)
{
if (contactToValidate.FirstName.Trim().Length == 0)
ModelState.AddModelError("FirstName", "First name is required.");
if (contactToValidate.LastName.Trim().Length == 0)
ModelState.AddModelError("LastName", "Last name is required.");
if (contactToValidate.Phone.Length > 0 && !Regex.IsMatch(contactToValidate.Phone, @"((\(\d{3}\) ?)|(\d{3}-))?\d{3}-\d{4}"))
ModelState.AddModelError("Phone", "Invalid phone number.");
if (contactToValidate.Email.Length > 0 && !Regex.IsMatch(contactToValidate.Email, @"^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$"))
ModelState.AddModelError("Email", "Invalid email address.");
}
//
// GET: /Home/
public ActionResult Index()
{
return View(_repository.ListContacts());
}
//
// GET: /Home/Details/5
public ActionResult Details(int id)
{
return View();
}
//
// GET: /Home/Create
public ActionResult Create()
{
return View();
}
//
// POST: /Home/Create
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create([Bind(Exclude = "Id")] Contact contactToCreate)
{
//Validation logic
ValidateContact(contactToCreate);
if (!ModelState.IsValid)
{
return View();
}
else
{
try
{
_repository.CreateContact(contactToCreate);
return RedirectToAction("Index");
}
catch
{
return View();
}
}
}
//
// GET: /Home/Edit/5
public ActionResult Edit(int id)
{
return View(_repository.GetContact(id));
}
//
// POST: /Home/Edit/5
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(Contact contactToEdit)
{
ValidateContact(contactToEdit);
if (!ModelState.IsValid)
return View();
try
{
_repository.EditContact(contactToEdit);
return RedirectToAction("Index");
}
catch
{
return View();
}
}
//
// GET: /Home/Delete/5
public ActionResult Delete(int id)
{
return View(_repository.GetContact(id));
}
//
// POST: /Home/Delete/5
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Delete(Contact contactToDelete)
{
try
{
_repository.DeleteContact(contactToDelete);
return RedirectToAction("Index");
}
catch
{
return View();
}
}
}
}

注意上面代码中,Contact controller包含两个构造函数。第一个构造函数向第二个构造函数传递一个基于IContactManagerRepository接口的实例。这就是“构造子注入”。

EntityContactManagerRepository类仅仅在第一个构造函数中被使用。其他的地方一律使用IContactManagerRepository接口代替确切的EntityContactManagerRepository类。

在这种情况下,如果以后我们想改变IContactManagerRepository的实现也就很方便了。比如你想使用 DataServicesContactRepository类代替EntityContactManagerRepository类,则只需要修改第一 个构造函数即可。

不仅如此,构造子注入更使Contact controller的可测试性变得更强。在你的单元测试用,你可以通过传递一个mock的IContactManagerRepository的实现进 而实例化Contact controller。依赖注入所带来的特性将在我们对Contact Manager的下一次迭代—进行单元测试—时显得非常重要。

如果你希望将Contact controller类与具体的IContactManagerRepository接口的实现彻底解耦,则可以使用一些支持依赖注入的框架,如 StructureMap或Microsoft Entity Framework (MEF)。有了这些依赖注入框架的帮忙,你就不必在代码中面向具体的类了。

建立service层

你应该注意到了,我们的验证逻辑仍与上面代码中修改过的controller逻辑混合在一起。像我们独立数据存储逻辑一样,将验证逻辑独立出来同样是个好注意。

So,我们应当建立service层。在这里,它作为独立的一层以衔接controller和repository类。service层应当包括所有的业务逻辑,我们的验证逻辑当然也不例外。

ContactManagerService的代码如下,我们将验证逻辑转移到了这里:

using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Web.Mvc;
using ContactManager.Models.Validation;
namespace ContactManager.Models
{
public class ContactManagerService : IContactManagerService
{
private IValidationDictionary _validationDictionary;
private IContactManagerRepository _repository;
public ContactManagerService(IValidationDictionary validationDictionary)
: this(validationDictionary, new EntityContactManagerRepository())
{ }
public ContactManagerService(IValidationDictionary validationDictionary, IContactManagerRepository repository)
{
_validationDictionary = validationDictionary;
_repository = repository;
}
public bool ValidateContact(Contact contactToValidate)
{
if (contactToValidate.FirstName.Trim().Length == 0)
_validationDictionary.AddError("FirstName", "First name is required.");
if (contactToValidate.LastName.Trim().Length == 0)
_validationDictionary.AddError("LastName", "Last name is required.");
if (contactToValidate.Phone.Length > 0 && !Regex.IsMatch(contactToValidate.Phone, @"((\(\d{3}\) ?)|(\d{3}-))?\d{3}-\d{4}"))
_validationDictionary.AddError("Phone", "Invalid phone number.");
if (contactToValidate.Email.Length > 0 && !Regex.IsMatch(contactToValidate.Email, @"^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$"))
_validationDictionary.AddError("Email", "Invalid email address.");
return _validationDictionary.IsValid;
}
#region IContactManagerService Members
public bool CreateContact(Contact contactToCreate)
{
// Validation logic
if (!ValidateContact(contactToCreate))
return false;
// Database logic
try
{
_repository.CreateContact(contactToCreate);
}
catch
{
return false;
}
return true;
}
public bool EditContact(Contact contactToEdit)
{
// Validation logic
if (!ValidateContact(contactToEdit))
return false;
// Database logic
try
{
_repository.EditContact(contactToEdit);
}
catch
{
return false;
}
return true;
}
public bool DeleteContact(Contact contactToDelete)
{
try
{
_repository.DeleteContact(contactToDelete);
}
catch
{
return false;
}
return true;
}
public Contact GetContact(int id)
{
return _repository.GetContact(id);
}
public IEnumerable<Contact> ListContacts()
{
return _repository.ListContacts();
}
#endregion
}
}

需要注意的是,ContactManagerService的构造函数中需要一个ValidationDictionary参数。service层 通过这个ValidationDictionary与controller层进行交互。我们将在接下来讨论装饰者模式时来说明它。

更值得注意的是,ContactManagerService实现了IContactManagerService接口。你需要时刻努力进行面向接 口变成。Contact Manager应用中的其他类都不与具体的ContactManagerService类直接交互。它们皆需面向 IContactManagerService接口。

IContactManagerService接口的代码如下:

 

using System.Collections.Generic; namespace ContactManager.Models { public interface IContactManagerService { bool CreateContact(Contact contactToCreate); bool DeleteContact(Contact contactToDelete); bool EditContact(Contact contactToEdit); Contact GetContact(int id); IEnumerable<Contact> ListContacts(); } }

修改后的Contact controller类代码如下,这里Contact controller类已不再与ContactManager service交互,每一层都尽可能的与其他层独立开来。

using System.Web.Mvc;
using ContactManager.Models;
using ContactManager.Models.Validation;
namespace ContactManager.Controllers
{
public class ContactController : Controller
{
private IContactManagerService _service;
public ContactController()
{
_service = new ContactManagerService(new ModelStateWrapper(this.ModelState));
}
public ContactController(IContactManagerService service)
{
_service = service;
}
public ActionResult Index()
{
return View(_service.ListContacts());
}
public ActionResult Create()
{
return View();
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create([Bind(Exclude = "Id")] Contact contactToCreate)
{
if (_service.CreateContact(contactToCreate))
return RedirectToAction("Index");
return View();
}
public ActionResult Edit(int id)
{
return View(_service.GetContact(id));
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(Contact contactToEdit)
{
if (_service.EditContact(contactToEdit))
return RedirectToAction("Index");
return View();
}
public ActionResult Delete(int id)
{
return View(_service.GetContact(id));
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Delete(Contact contactToDelete)
{
if (_service.DeleteContact(contactToDelete))
return RedirectToAction("Index");
return View();
}
}
}

我们的应用程序已经不再违反SRP原则了。上面所示代码的Contact controller中,所有的验证逻辑都被转移到service层中,所有的数据库存储逻辑都被转移到repository层中。

使用装饰者模式

我们欲将service层与controller层完全解耦,原则上讲也就是我们应当可以在独立的程序集中编译service层而无需添加对MVC应用程序的引用。

然而我们的service层需要将验证错误信息回传给controller层,那么我们如何才能在service层和controller不耦合的前提下完成这项任务呢?答案是:装饰者模式。

Contrlooer使用名为ModelState的ModelStateDictionary表现验证错误信息。因此我们可能会想将 ModelState从controller层传递到sercice层。然而在service层中使用ModelState会使你的服务层依赖于 ASP.NET MVC framework提供的某些特性。这可能会很糟,假设某天你想在一个WPF应用程序中使用这个service层,你就不得不添加对ASP.NET MVC framework的引用才能使用ModelStateDictionary类。

装饰者模式通过将已有的类包装在新的类中从而实现某接口。我们的Contact Manager项目中包含的ModelStateWrapper类的代码如下:

using System.Web.Mvc;
namespace ContactManager.Models.Validation
{
public class ModelStateWrapper : IValidationDictionary
{
private ModelStateDictionary _modelState;
public ModelStateWrapper(ModelStateDictionary modelState)
{
_modelState = modelState;
}
public void AddError(string key, string errorMessage)
{
_modelState.AddModelError(key, errorMessage);
}
public bool IsValid
{
get { return _modelState.IsValid; }
}
}
}

其接口代码如下:

namespace ContactManager.Models.Validation
{
public interface IValidationDictionary
{
void AddError(string key, string errorMessage);
bool IsValid { get; }
}
}

仔细观察IContactManagerService接口中的代码,你会发现ContactManager service层中仅使用了IValidationDictionary接口。ContactManager service不依赖ModelStateDictionary类。当Contact controller创建ContactManager service时,controller将其ModelState包装成如下的样子:

_service = new ContactManagerService(new ModelStateWrapper(this.ModelState));

总结

本次迭代中,我们并没有对Contact Manager应用添加任何的功能。本次迭代的目的是通过重构应用程序,使Contact Manager更易维护、更易修改。

首先,我们实现了Repository软件设计模式。我们将所有的数据存取相关的代码提取到独立的ContactManager repository类中。同时,我们也将验证逻辑从controller逻辑中独立出来,将其放入我们另外创建的独立的service层中。 controller层与service层交互,service层则与repository层交互。

然后我们通过装饰者模式将ModelState从service层中独立出来。在我们的service层中,我们只需针对IValidationDictionary接口进行编码,而非针对ModelState类。

最后,我们使用了依赖注入这种软件设计模式。该模式使得我们在开发中可以避开具体类,而针对接口(虚类)编码。得益于依赖注入模式,我们的代码变得更具测试性。在下一次迭代中,我们将向项目中添加单元测试。

作者:紫色永恒
出处:http://024hi.cnblogs.com/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利
Tag标签: asp.net mvc

posted on 2009-04-12 19:27 紫色永恒 阅读(1032) 评论(18)  编辑 收藏 网摘 所属分类: Asp.Net MVC

Feedback

#1楼  2009-04-12 19:55 ·风信子·      

又见博主的大作,支持   回复  引用  查看    

#2楼  2009-04-12 20:50 EntLib      

不错,很专业!

我们也在翻译NerdDinner 范例程序的创建过程。

一步一步学习ASP.NET MVC 1.0创建NerdDinner 范例程序 (Part 1-10) – 强烈推荐!
一步一步学习ASP.NET MVC 1.0创建NerdDinner 范例程序 (Part 11-20) – 强烈推荐! 
欢迎交流

  回复  引用  查看    

#3楼  2009-04-12 21:07 Jeffrey Zhao      

这是Repository吗?这是Decorator吗?不过是一路打通的简单逻辑,分为了业务逻辑数据访问层,再根据接口进行访问——哪来Repository?哪来Decorator?   回复  引用  查看    

#4楼 [楼主] 2009-04-12 21:22 紫色永恒      

@Jeffrey Zhao
其实我也有此问,但翻译就是翻译   回复  引用  查看    

#5楼  2009-04-12 23:18 Q.Lee.lulu      

@Jeffrey Zhao
这不是Repository么?那么对于真正的Repository还差些什么呢?   回复  引用  查看    

#6楼 [楼主] 2009-04-13 07:57 紫色永恒      

@Q.Lee.lulu
我主要觉得这里面的DI实在有点勉强   回复  引用  查看    

#7楼  2009-04-13 09:32 假正经哥哥      

Repository?扫下盲。。。   回复  引用  查看    

#8楼  2009-04-13 19:56 Jeffrey Zhao      

Repository应该是一个collection like的interface,提供查询/修改操作,而不是包含一系列CRUD方法的接口。
例如Repository的查询:
var r = new Repository()
var q = new SomeQueryObject();
return r.List(q);
可以看出,linq是个不错的内置的query object。
修改呢?get/modify/save
添加呢?create/add/save
而不是一个功能一个接口,否则就是个普通的数据访问层而已,什么Repository……   回复  引用  查看    

#9楼  2009-04-13 20:51 EntLib      

@Jeffrey Zhao
在Scott Gu的ASP.NET MVC 1.0 – NerdDinner 范例程序,是这样定义IDinnerRepository接口的:
public interface IDinnerRepository {
IQueryable<Dinner> FindAllDinners();
IQueryable<Dinner> FindByLocation(float latitude, float longitude);
IQueryable<Dinner> FindUpcomingDinners();
Dinner GetDinner(int id);
void Add(Dinner dinner);
void Delete(Dinner dinner);
void Save();
}
和你的想法似乎不一致。
  回复  引用  查看    

#10楼  2009-04-13 21:53 Jeffrey Zhao      

@EntLib
我说的是PoEAA那本书上的样子(也就是Query Object),ScottGu也只是多加了一些额外的查询接口,和我的说法大体一致阿,都是针对集合的操作,比如Add/Delete/Save。
不像现在的文章,CreateXxx,DeleteXxx,EditXxx,都不是针对集合的操作,只是一个数据访问层对象而已。试想如果要更多接口的会怎么样呢?
UpdateContactsForCompany(int companyId, …)
这又算什么Repository呢?   回复  引用  查看    

#11楼  2009-04-13 22:36 EntLib      

@Jeffrey Zhao
根据我的理解,我认为Scott Gu定义的IDinnerRepository 接口,和本文中定义了IContactRepository 接口基本思路是一样的。
不知道其他朋友有什么想法?
  回复  引用  查看    

#12楼  2009-04-13 22:44 Jeffrey Zhao      

@EntLib
嗯,我总结一下我的观点:
Repository有其特定的概念和操作方式,这也是为什么Scott和我认为Repository都需要一个Save方法的缘故。 Repository是一个集合,NHibernate,Linq to SQL都适合实现Repository,它是把Domain和关系进行映射(Mapping),而Repository是一个抽象的“容器”。
这片文章里就是个普通的数据访问层,和我们随便写一个程序,其中的数据访问层就会是这样的,说白了,这更像是最普通的Trasaction Scripts,缺少Mapping的概念。
所以说,看起来有接口类似(部分查询功能),但是操作理念是大相径庭的,这也就是我说这篇文章在胡搞的原因。
// Decorator更不用说了,没有组合,没有扩展,一点Decorator的特点都没有。   回复  引用  查看    

#13楼  2009-04-13 23:15 EntLib      

@Jeffrey Zhao,
首先,很高兴和你讨论这个问题。谢谢!
关于引入Repository Class,Scott Gu 在文章中是这样解释的:

创建DinnerRepository
对于一个小应用程序而言,有时让Conrollers类直接使用LINQ to SQL DataContext类,并将LINQ 查询语句写在Controllers中。但随着应用程序越来越大,这一方法的维护和测试将变得麻烦,并且导致重复的LINQ查询在多个地方出现。
 

让维护和测试更方便的方法是使用Repository 模式。Repository类帮助封装数据查询和存储逻辑,从应用程序中抽象隔离具体的数据存储实现。除了是应用程序代码更加简洁外,使用 Repository模式使将来更改数据库存储实现更加方便,并且有助于在没有真实数据库的情况下,进行应用程序进行单元测试。
原文链接:一步一步学习ASP.NET MVC 1.0创建NerdDinner 范例程序,Part 5

[SQL]TSQL查询内幕::(2.2)编译

mikel阅读(1001)

批处理是作为一个单元编译的一个或多个T-SQL语句的组合,存储过程就是一个批处理的例子。另一个例子就是在查询管理器中,输入一组语句,GO命令把多条语句分隔为单独的批处理。GO不是一个T-SQL语句,只是SQL Server的企业管理器的一个关键字,企业管理器通过这个关键字来分隔批处理(SQLCMDOSQL也是使用GO来表示批处理的)

 

SQL Server把批处理中的语句编译到一个被称为“执行计划”(execution plan)的可执行单元。在编译期间,编译器展开这些语句,其中包含该语句执行期间需要执行的相关约束、触发器以及级联操作。如果经过编译的批处理包含对其他存储过程或函数的调用,且缓存中没有他们的执行计划,则这些存储过程和函数也将被递归的编译。

 

批处理编译的主要步骤:

编译和执行查询处理是两个截然不同的阶段,有可能一个编译需要几个小时,可是执行该查询只需要几毫秒。处理即席查询时缓存中通常不包含其执行计划,因此它被编译后会立即执行。而经常执行的存储过程的已编译计划可能会在过程缓存中保留很长时间。在需要释放存储空间时,SQL Server会从过程缓存中首先移除不常用的计划

 

SQL Server准备处理一个批处理时,如果缓存中不存在该批处理的执行计划,则编译该批处理并生成执行计划。

 

首先,SQL Server执行分析。分析是检查语法并把SQL批处理转换为分析树(parse tree)的过程,如检查表或列明是否以数字开头,但他不会检查Where中的列是否存在于FROM子句中所写的表中。

 

然后,SQL Server进行绑定。绑定过程确定SQL语句所引用对象的特征,他检查请求语义是否有意义,参见下面的Algebrizer

 

优化是编译的最后一步。优化器必须把基于几何的非过程SQL语句转换为可以高效执行并返回期望结果的程序。与绑定类似,优化器一次只优化批处理中的一条语句。在编译器为该批处理生成执行计划并存储到过程缓存之后,将执行该计划的执行上下文的一个特殊副本。SQL Server像缓存查询计划一样缓存执行上下文。SQL Server并不优化批处理中的每条语句。它只优化那些访问表而且可能生成多个执行计划的语句。SQL Server优化所有的DML语句(SelectInsertDeleteUpdate)。除了DML ,一些T-SQL语句也会被优化,Create INDEX便是其中之一。只有被优化过的语句才会生成查询计划。

 

Algebrizer

Algebrizer执行的整个过程称为绑定。

分析阶段输出的分析树,正是Algebrizer的输入。遍历几次分析树后,Algebrizer生成查询处理器树(query processor tree),用于查询优化。

 

运算符平展:

Algebrizer展开二元运算符UNIONANDOR

在分析阶段,分析器把这些逻辑运算符都看作是二元的,就像下图左侧的样子。

 

而分析器之后的所有步骤则会把多个二元运算符组合成一个n元运算符,像右侧所示。这对于非常长的IN列表是非常重要的,分析器会把它转换为一连串的OR运算符。此外,展开运算符还会避免在后续传递中由非常深的树所引起的大部分堆溢出问题。SQL Server内部用于执行运算符展开的代码都尽可能的使用迭代而非递归,这样Algebrizer本身就不容易出现同样的问题。

 

名称解析:

分析树中的每个表名称和列名称都关联到相应表或列对象的引用。表示相同对象的名称具有相同的引用。Algebrizer检查查询中的每个对象名称是否真的引用了系统目录中已经存在的有效表或列,以及是否在特定的查询范围内可见。然后,Algebrizer把目录中的信息关联到该对象的名称。

其中,对视图的名称解析使用视图树替换视图引用的过程。如果试图还引用了其他视图,他们将被递归地进行解析。

 

类型派生:

T-SQL中的数据类型是静态确定的,Algebrizer负责确定分析树中每个节点的类型。

 

聚合绑定:

首先看一个查询,T1T2是两个表,T1c1,c2两列,T2只有x一列。其中,T1.c2列与T2.x列具有同样的数据类型。

 

Select c1 FROM dbo.T1

GROUP BY c1

HAVING EXISTS

 (Select * FROM dbo.T2

   Where T2.x > MAX(T1.c2));

 

对于上面这个查询,SQL Server会在外层查询Select c1 FROM dbo.T1 GROUP BY c1时计算MAX聚合。MAX不是在内部查询中进行的吗?为什么不是在子查询中计算呢?因为每个聚合都需要绑定到他的宿主查询,这样才可以正确的计算聚合。

 

分组绑定:

看一个查询,T1是一个表,包含了c1c2c3列。

Select c1 + c2, MAX(c3) FROM dbo.T1 GROUP BY c1 + c2;

这个表达式是合法的,但如果只在Select列表中使用c1,则不合法。因为分组查询与非分组查询有不同的语义。

Select列表中,所有非聚合的列或表达式,必须在GROUP BY列表中有直接的对应项。检验是否满足这一规则的过程就叫做分组绑定。

不光如此,根据SQL的规定,只要出现绑定到特定列表的聚合函数,及时没有GROUP BYHAVING,也会使Select列表被分组。

看这个例子:

Select c1, MAX(c2) FROM dbo.T1;

这是一个分组Select,因为查询中包含了一个MAX聚合,所以因为Select中有c1,所以这个查询是不合法的。

再看一个例子:

Select c1,

       (Select T2.y

          FROM dbo.T2

        Where T2.x = MAX(T1.c2))

 FROM dbo.T1;

这个查询也不合法,在聚合绑定中,我们知道MAX(T1.c2)是在外部查询中计算的,所以外部查询是一个分组,所以c1Select列表中是非法的。

 

优化

处理查询时所涉及的最重要最复杂的组件是查询优化器。优化器的任务是为批处理或存储过程中的每个查询生成高校的执行计划。

这里先请查询优化器做一个自我介绍:“我是个基于成本的优化器,我会尝试为每个SQl语句生成成本最低的执行计划,我不会分析所有可能的组合,但会尝试找出成本非常接近理论最小值的一个执行计划”。

这里,查询分析器先生所说的成本,表现为估计的完成查询所需的时间。然而,最低时间成本不一定是最低资源成本。比如,使用多个CPU处理一个查询,有时会减少时间成本,但是却增加了资源成本。

查询分析器这时作出了补充:“我主张使用并行计划,而且如果对该服务器上的负载不会产生负面影响,SQL Server会使用并行计划来执行”。

 

优化本身包括几个步骤。细微计划优化是第一步。这一步产生的原因,是因为查询优化器做成本优化的本身成本较高。如果SQL Server通过分析该查询发现只有一种可行的计划,他就可以避免查询优化器做成本优化时初始化和执行所需的大量工作。

比如有这样一个情况:带有VALUES子句的Insert语句,这个插入语句的目标表不参与任何索引视图,那么就只有一个可能的计划。

再比如,执行一个对没有索引的表,进行不带GROUP BYSelect查询。

查询优化器生成的这个细微计划的成本很低。

 

如果优化器没有找到细微计划,SQL Server将进行一些简化,以寻找可以被重新整理的可交换属性和操作,说白了就是对查询语句进行语法转换的简化。

比如,在联接之前计算表的Where筛选器就是一个简化的例子。因为在逻辑查询顺序中[TSQL::(1)逻辑查询处理],筛选器在联接之后计算,但在联接之前计算筛选器也可以得到正确的结果,而且是永远是更高效的,所以在简化这步会做出类似的语法优化。

再比如,看下面这个查询

USE Northwind;

Select

 [Order Details].OrderID,

 [Products].ProductName,

 [Order Details].Quantity,

 [Order Details].UnitPrice

FROM dbo.[Order Details]

 LEFT OUTER JOIN dbo.Products

    ON [Order Details].ProductID = Products.ProductID

Where Products.UnitPrice > 10;

 

这个加红的LEFT OUTER,会被简化为INNER,原因大家应该都知道。

下图是简化后的执行计划:

 

SQL Server做完了简化之后,查询优化器开始启动基于成本的优化过程。

通常,正确的结果都是相同的,但是有些不相同的结果也被认为是正确的。如:

Select TOP (10) <select_list> FROM Orders;

 

如果优化器比较所有可用计划的成本并选择成本最低的一个,优化过程会花费非常长的时间,因为可用的计划数量可能非常大。因此,优化被分成三个搜索阶段。

每个阶段关联一组转换规则。每个阶段之后,SQL Server会得到这个阶段成本最低的查询计划。当在某一个阶段的成本足够低,SQL Server将不再进行下一步的优化,而采用这个满足要求的计划。

 

阶段0:应用于至少包括4个表的查询。只使用有限的联接顺序数量,只考虑Hash联接和嵌套循环。如果找到成本低于0.2的计划,优化即结束。阶段0产生查询计划的查询通常出现在事务处理应用程序中,所以这个阶段也叫做事务处理阶段。

 

阶段1(非并行):快速计划优化。他使用更多的转换规则并尝试不同的联接顺序。该阶段完成后,如果最佳计划的成本低于1.0,则优化结束。

 

阶段1(并行):将再次执行阶段1的并行版本。当阶段1的两个版本都执行之后,将会比较两个版本的最佳计划,然后进入完全优化阶段。

 

阶段2:完全优化阶段。阶段2包括额外的规则,如:使用Outer Join重新排序并用索引视图自动替换多表视图。

 

总结以上的步骤,可以画出SQL Server查询优化的流程图:

 

SQL Server 2005中可以使用DMV(动态管理视图)sys.dm_exec_query_optimizer_info。这个视图提供了从SQL Server启动以来执行的所有优化的累积信息。

使用如下代码进行查询:

SET NOCOUNT ON;

USE Northwind;  这里使用你自己的数据库

DBCC FREEPROCCACHE;  清空过程缓存

GO

 我们将用tempdb..OptStats 表来捕获执行几次

— sys.dm_exec_query_optimizer_info所得到的信息

IF (OBJECT_ID('tempdb..OptStats') IS NOT NULL)

 Drop TABLE tempdb..OptStats;

GO

 这条语句用于创建临时表tempdb..OptStats

Select 0 AS Run, *

INTO tempdb..OptStats

FROM sys.dm_exec_query_optimizer_info;

GO

 该语句的计划将被保存到过程缓存

 这样当下一次执行时不产生任何优化器事件

 后面的GO用于确保下次执行这段脚本时

 可以重用Insert的计划

GO

Insert INTO tempdb..OptStats

 Select 1 AS Run, *

 FROM sys.dm_exec_query_optimizer_info;

GO

 原因同上,只是用"2"替换"1"

 这样我们将得到不同的计划

GO

Insert INTO tempdb..OptStats

 Select 2 AS RUN, *

 FROM sys.dm_exec_query_optimizer_info;

 清空临时表

TRUNCATE TABLE tempdb..OptStats

GO

 存储运行前的信息

 sys.dm_exec_query_optimizer_info的输出

 保存到临时表,RUn列的值为"1"

GO

Insert INTO tempdb..OptStats

 Select 1 AS Run, *

 FROM sys.dm_exec_query_optimizer_info;

GO

 在这里执行你自己的语句或批处理

 下面是一个示例

Select C.CustomerID, COUNT(O.OrderID) AS NumOrders

FROM dbo.Customers AS C

 LEFT OUTER JOIN dbo.Orders AS O

    ON C.CustomerID = O.CustomerID

Where C.City = 'London'

GROUP BY C.CustomerID

HAVING COUNT(O.OrderID) > 5

ORDER BY NumOrders;

 示例结束

GO

 存储运行后的信息,

 sys.dm_exec_query_optimizer_info的输出

 保存到临时表,Run列的值为"2"

GO

Insert INTO tempdb..OptStats

 Select 2 AS Run, *

 FROM sys.dm_exec_query_optimizer_info;

GO

 从临时表中提取出Runs1Runs2之间

— OCCURRENCE Value值发生变化的所有事件

 然后显示执行批处理或查询之前(Run1OccourenceRun1Value)

 和之后所有这些事件(Run2OccurrenceRun2Value)

 OccurrenceValue

WITH X (Run, Counter, Occurrence, Value)

AS

(

 Select *

 FROM tempdb..OptStats Where Run=1

),

Y (Run, Counter, Occurrence, Value)

AS

(

 Select *

 FROM tempdb..OptStats

 Where Run=2

)

Select X.Counter, Y.OccurrenceX.Occurrence AS Occurrenc,

 CASE (Y.OccurrenceX.Occurrence)

    WHEN 0 THEN (Y.Value*Y.Occurrencex.Value*x.Occurrence)

      ELSE (Y.Value*Y.OccurrenceX.Value*X.Occurrence)/(Y.Occurrencex.Occurrence)

 END AS Value

FROM X JOIN Y

 ON (X.Counter=Y.Counter

      AND (X.Occurrence<>Y.Occurrence OR x.Value<>Y.Value));

GO

 删除临时表

Drop TABLE tempdb..OptStats;

GO

执行结果: