in

SDT Community Server

SDT Forums, Blogs, Photos server.

slash

  • 什么是SOA

    对于面向同步和异步应用的,基于请求/响应模式的分布式计算来说,SOA是一场革命。一个应用程序的业务逻辑(business logic)或某些单独的功能被模块化并作为服务呈现给消费者或客户端。这些服务的关键是他们的松耦合特性。例如,服务的接口和实现相独立。应用开发人员或者系统集成者可以通过组合一个或多个服务来构建应用,而无须理解服务的底层实现。举例来说,一个服务可以用。NET或J2EE来实现,而使用该服务的应用程序可以在不同的平台之上,使用的语言也可以不同。

      SOA有以下特性

      SOA服务具有平台独立的自我描述XML文档。Web服务描述语言(WSDL, Web Services Description Language)是用于描述服务的标准语言。

      SOA 服务用消息进行通信,该消息通常使用XML Schema来定义(也叫做XSD, XML Schema Definition)。消费者和提供者或消费者和服务之间的通信多见于不知道提供者的环境中。服务间的通讯也可以看作企业内部处理的关键商业文档。

      在一个企业内部,SOA服务通过一个扮演目录列表(directory listing)角色的登记处(Registry)来进行维护。应用程序在登记处(Registry)寻找并调用某项服务。统一描述,定义和集成(UDDI, Universal Description, Definition, and Integration)是服务登记的标准。

      每项SOA服务都有一个与之相关的服务品质(QoS, quality of service)。QoS的一些关键元素有安全需求(例如认证和授权),可靠通信(译注:可靠消息是指,确保消息“仅且仅仅”发送一次,从而过滤重复信息。),以及谁能调用服务的策略。

      为什么选择SOA?

      不同种类的操作系统,应用软件,系统软件和应用基础结构(application infrastructure)相互交织,这便是IT企业的现状。一些现存的应用程序被用来处理当前的业务流程(business processes),因此从头建立一个新的基础环境是不可能的。企业应该能对业务的变化做出快速的反应,利用对现有的应用程序和应用基础结构(application infrastructure)的投资来解决新的业务需求,为客户,商业伙伴以及供应商提供新的互动渠道,并呈现一个可以支持有机业务(organic business)的构架。SOA凭借其松耦合的特性,使得企业可以按照模块化的方式来添加新服务或更新现有服务,以解决新的业务需要,提供选择从而可以通过不同的渠道提供服务,并可以把企业现有的或已有的应用作为服务, 从而保护了现有的IT基础建设投资。

      如图1的例子所示,一个使用SOA的企业,可以使用一组现有的应用来创建一个供应链复合应用(supply chain composite application),这些现有的应用通过标准接口来提供功能。

      Figure 1. Supply chain application. Click on thumbnail to view full-sized image.

      服务架构

      为了实现SOA,企业需要一个服务架构,图2显示了一个例子:

      Figure 2. A sample service architecture. Click on thumbnail to view full-sized image.

      在图2中, 服务消费者(service consumer)可以通过发送消息来调用服务。这些消息由一个服务总线(service bus)转换后发送给适当的服务实现。这种服务架构可以提供一个业务规则引擎(business rules engine),该引擎容许业务规则被合并在一个服务里或多个服务里。这种架构也提供了一个服务管理基础(service management infrastructure),用来管理服务,类似审核,列表(billing),日志等功能。此外,该架构给企业提供了灵活的业务流程,更好地处理控制请求(regulatory requirement),例如Sarbanes Oxley(SOX),并且可以在不影响其他服务的情况下更改某项服务。

      SOA基础结构

      要运行,管理SOA应用程序,企业需要SOA基础,这是SOA平台的一个部分。SOA基础必须支持所有的相关标准,和需要的运行时容器。图3所示的是一个典型的SOA基础结构。接下来的章节将逐一讨论该结构的每个部分。

      Figure 3. A typical SOA infrastructure. Click on thumbnail to view full-sized image.

      SOAP, WSDL, UDDI

      WSDL,UDDI和SOAP是SOA基础的基础部件。WSDL用来描述服务;UDDI用来注册和查找服务;而SOAP,作为传输层,用来在消费者和服务提供者之间传送消息。SOAP是Web服务的默认机制,其他的技术为可以服务实现其他类型的绑定。一个消费者可以在UDDI注册表(registry)查找服务,取得服务的WSDL描述,然后通过SOAP来调用服务。

      WS-I Basic Profile

      WS-I Basic Profile,由Web服务互用性组织(Web Services Interoperability Organization)提供,是SOA服务测试与互用性所需要的核心构件。服务提供者可以使用Basic Profile测试程序来测试服务在不同平台和技术上的互用性。

      J2EE 和 .Net

      尽管J2EE和。NET平台是开发SOA应用程序常用的平台,但SOA不仅限于此。像J2EE这类平台,不仅为开发者自然而然地参与到SOA中来提供了一个平台,还通过他们内在的特性,将可扩展性,可靠性,可用性以及性能引入了SOA世界。新的规范,例如 JAXB(Java API for XML Binding),用于将XML文档定位到Java类;JAXR(Java API for XML Registry)用来规范对UDDI注册表(registry)的操作;XML-RPC(Java API for XML-based Remote Procedure Call)在J2EE1.4中用来调用远程服务,这使得开发和部署可移植于标准J2EE容器的Web服务变得容易,与此同时,实现了跨平台(如。NET)的服务互用。

      服务品质

      在企业中,关键任务系统(mission-critical system,译注:关键任务系统是指如果一个系统的可靠性对于一个组织是至关重要的,那么该系统就是该企业的关键任务系统。比如,电话系统对于一个电话促销企业来说就是关键任务系统,而文字处理系统就不那么关键了。)用来解决高级需求,例如安全性,可靠性,事物。当一个企业开始采用服务架构作为工具来进行开发和部署应用的时候,基本的Web服务规范,像WSDL,SOAP,以及UDDI就不能满足这些高级需求。正如前面所提到的,这些需求也称作服务品质(QoS,quality of services)。与QoS相关的众多规范已经由一些标准化组织(standards bodies)提出,像W3C(World Wide Web Consortium)和OASIS(the Organization for the Advancement of Structured Information Standards)。下面的部分将会讨论一些QoS服务和相关标准。

      安全

      Web服务安全规范用来保证消息的安全性。该规范主要包括认证交换, 消息完整性和消息保密。该规范吸引人的地方在于它借助现有的安全标准,例如,SAML(as Security Assertion Markup Language)来实现web服务消息的安全。OASIS正致力于Web服务安全规范的制定。

      可靠

      在典型的SOA 环境中,服务消费者和服务提供者之间会有几种不同的文档在进行交换。具有诸如“仅且仅仅传送一次”( once-and-only-once delivery),“最多传送一次”( at-most-once delivery),“重复消息过滤”(duplicate message elimination),“保证消息传送”(guaranteed message delivery)等特性消息的发送和确认,在关键任务系统(mission-critical systems)中变得十分重要。WS-Reliability 和 WS-ReliableMessaging是两个用来解决此类问题的标准。这些标准现在都由OASIS负责。

      策略

      服务提供者有时候会要求服务消费者与某种策略通信。比如,服务提供商可能会要求消费者提供Kerberos安全标示,才能取得某项服务。这些要求被定义为策略断言(policy assertions)。一项策略可能会包含多个断言。WS-Policy用来标准化服务消费者和服务提供者之间的策略通信。

      控制

      当企业着手于服务架构时,服务可以用来整合数据仓库(silos of data),应用程序,以及组件。整合应用意味着例如异步通信,并行处理,数据转换,以及校正等进程请求必须被标准化。在SOA中,进程是使用一组离散的服务创建的。BPEL4WS 或者 WSBPEL(Web Service Business Process Execution Language)是用来控制这些服务的语言。WSBPEL目前也由OASIS负责。

      管理

      随着企业服务的增长,所使用的服务和业务进程的数量也随之增加,一个用来让系统管理员管理所有运行在多相环境下的服务的管理系统就显得尤为重要。WSDM(Web Services for Distributed Management)规定了任何根据WSDM实现的服务都可以由一个WSDM适应(WSDM-compliant)的管理方案来管理。

      其它的qos特性,比如合作方之間的溝通和通訊,多個服務之間的事務處理,都在WS-Coordination 和 WS-Transaction 標準中描述, 這些都是OASIS 的工作。

      SOA 不是Web服务

      在理解SOA和Web服务的关系上,经常发生混淆。根据2003年4月的Gartner报道,Yefim V. Natis就这个问题是这样解释的:“Web服务是技术规范,而SOA是设计原则。特别是Web服务中的WSDL,是一个SOA配套的接口定义标准:这是Web服务和SOA的根本联系。”从本质上来说,SOA是一种架构模式,而Web服务是利用一组标准实现的服务。Web服务是实现SOA的方式之一。用Web服务来实现SOA的好处是你可以实现一个中立平台,来获得服务,而且随着越来越多的软件商支持越来越多的Web服务规范,你会取得更好的通用性。

      SOA的优势

      SOA的概念并非什么新东西,SOA不同于现有的分布式技术之处在于大多数软件商接受它并有可以实现SOA的平台或应用程序。SOA伴随着无处不在的标准,为企业的现有资产或投资带来了更好的重用性。SOA能够在最新的和现有的应用之上创建应用;SOA能够使客户或服务消费者免予服务实现的改变所带来的影响;SOA能够升级单个服务或服务消费者而无需重写整个应用,也无需保留已经不再适用于新需求的现有系统。总而言之,SOA以借助现有的应用来组合产生新服务的敏捷方式,提供给企业更好的灵活性来构建应用程序和业务流程。

    Posted Sep 03 2010, 04:00 PM by slash with no comments
    Filed under:
  • oracle 的分析函数over函数用法

    oracle 的分析函数over函数用法:
    1.sum(salary) over( order by employee)     按照员工的薪水逐条累计显示,例如:
    select ename,sal,
    2   sum(sal) over (order by ename) 连续求和,
    3   sum(sal) over () 总和,                -- 此处sum(sal) over () 等同于sum(sal)
    4   100*round(sal/sum(sal) over (),4) "份额(%)"
    5   from emp
    6   /

    ENAME          SAL 连续求和    总和 份额(%)
    ---------- ---------- ---------- ---------- ----------
    ADAMS          1100    1100    29025    3.79
    ALLEN          1600    2700    29025    5.51
    BLAKE          2850    5550    29025    9.82
    CLARK          2450    8000    29025    8.44

    um(sal) over (partition by deptno order by ename) 分部门“连续”求总和
    sum(sal) over (partition by deptno) 分部门求总和
    sum(sal) over (order by deptno,ename) 按部门、员工“连续”求总和
    sum(sal) over () 不按部门,求所有员工总和,效果等同于sum(sal)。

    体会:在"... from emp;"后面不要加order   by 子句,使用的分析函数的(partition by deptno order by sal)
    里已经有排序的语句了,如果再在句尾添加排序子句,一致倒罢了,不一致,结果就令人费劲了

    2.Rank()
    rank() over( partition by col1 order by col2) 
    dense_rank over( partition by col1 order by col2) 
    功能:按照col1进行划分,并根据col2排序的结果给col评级,如果col2值相同,则评级值相同。
    dense_rank与rank()用法相当,但是有一个区别:dence_rank在处理相同的等级时,等级的数值不会跳过。rank则跳过。即rank的级值可能是不连续的。
    例如:表     A B C 
    a          liu          wang 
    a          jin          shu 
    a          cai          kai 
    b          yang      du 
    b          lin          ying 
    b          yao        cai 
    b          yang      99 

    例如:当rank时为: 
    select m.a,m.b,m.c,rank() over(partition by a order by b) liu from test3 m 
    A          B             C          LIU 
    a          cai          kai          1 
    a          jin           shu        2 
    a          liu           wang     3 
    b          lin           ying        1 
    b          yang      du           2 
    b          yang      99           2 
    b          yao        cai           4 

    而如果用dense_rank时为: 
    select m.a,m.b,m.c,dense_rank() over(partition by a order by b) liu from test3 m 
    A          B             C          LIU 
    a          cai          kai          1 
    a          jin           shu        2 
    a          liu           wang     3 
    b          lin           ying        1 
    b          yang      du           2 
    b          yang      99           2 
    b          yao        cai           3 
    Posted Sep 02 2010, 04:52 PM by slash with no comments
    Filed under: ,
  • 体验Chrome重力效果 Google Gravity上线

    去年Google曾启动了一个Chrome体验项目,让用户体验Chrome先进的JavaScript等性能,其中就包括Google Gravity(Google重力),由Hi-Res开发,该页面能为你带来与传统不同的搜索视觉体验,如今Google公开提供了这一功能,让每个Chrome用户都更直观地看到Chrome的优异之处。如果你已经厌烦了当前这种乏味的搜索页面,那么建议你用Chrome试一试Google提供的这个新服务。

      在Google搜索主页搜索框中输入“Google Gravity”,然后点击搜索框下的“手气不错”或者“I’m Feeling Lucky”(注意,不要点击“Google搜索”或是敲击回车)。然后,你就会看到Google主页的这些文字、搜索框纷纷落下,就像受到了地球引力一样。你还可以随意拖动窗口,窗口中的文字也会做出相应的动作。

      Google.com.hk虽然也可以进行相同操作,不过会自动跳转到英文页面。当然了,这并不是一个花架子,你仍然可以从下面的一堆杂物中找到搜索框,正常进行搜索工作。

    体验Chrome重力效果GoogleGravity上线

    体验Chrome重力效果GoogleGravity上线

    体验Chrome重力效果GoogleGravity上线

    体验Chrome重力效果GoogleGravity上线

    体验Chrome重力效果GoogleGravity上线

    体验Chrome重力效果GoogleGravity上线

    Posted Aug 24 2010, 09:36 AM by slash with no comments
    Filed under:
  • JSON是什么?

    JSON定义

    JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式,易于阅读和编写,同时也易于机器解析和生成。它基于ECMA262语言规范(1999-12第三版)中JavaScript编程语言的一个子集。 JSON采用与编程语言无关的文本格式,但是也使用了类C语言(包括C, C++, C#, Java, JavaScript, Perl, Python等)的习惯,这些特性使JSON成为理想的数据交换格式。

    JSON的结构基于下面两点

    1. "名称/值"对的集合不同语言中,它被理解为对象(object),记录(record),结构(struct),字典(dictionary),哈希表(hash table),键列表(keyed list)等
    2. 值的有序列表 多数语言中被理解为数组(array)
    JSON使用:

    JSON以一种特定的字符串形式来表示 JavaScript 对象。如果将具有这样一种形式的字符串赋给任意一个 JavaScript 变量,那么该变量会变成一个对象引用,而这个对象就是字符串所构建出来的,好像有点拗口,我们还是用实例来说明。
    这里假设我们需要创建一个User对象,并具有以下属性

    用户ID
    用户名
    用户Email
    您可以使用以下JSON形式来表示User对象:

    {"UserID":11, "Name":"Truly", "Email":"zhuleipro◎hotmail.com"};
    然后如果把这一字符串赋予一个JavaScript变量,那么就可以直接使用对象的任一属性了。

    完整代码:

    <script>
    var User = {"UserID":11, "Name":"Truly", "Email":"zhuleipro◎hotmail.com"};
    alert(User.Name);
    </script>
    实际使用时可能更复杂一点,比如我们为Name定义更详细的结构,使它具有FirstName和LastName:

    {"UserID":11, "Name":{"FirstName":"Truly","LastName":"Zhu"}, "Email":"zhuleipro◎hotmail.com"}
    完整代码:

    <script>
    var User = {"UserID":11, "Name":{"FirstName":"Truly","LastName":"Zhu"}, "Email":"zhuleipro◎hotmail.com"};
    alert(User.Name.FirstName);
    </script>
    现在我们增加一个新的需求,我们某个页面需要一个用户列表,而不仅仅是一个单一的用户信息,那么这里就需要创建一个用户列表数组。
    下面代码演示了使用JSON形式定义这个用户列表:

    [
    {"UserID":11, "Name":{"FirstName":"Truly","LastName":"Zhu"}, "Email":"zhuleipro◎hotmail.com"},
    {"UserID":12, "Name":{"FirstName":"Jeffrey","LastName":"Richter"}, "Email":"xxx◎xxx.com"},
    {"UserID":13, "Name":{"FirstName":"Scott","LastName":"Gu"}, "Email":"xxx2◎xxx2.com"}
    ]

    完整代码:

    <script>
    var UserList = [
    {"UserID":11, "Name":{"FirstName":"Truly","LastName":"Zhu"}, "Email":"zhuleipro◎hotmail.com"},
    {"UserID":12, "Name":{"FirstName":"Jeffrey","LastName":"Richter"}, "Email":"xxx◎xxx.com"},
    {"UserID":13, "Name":{"FirstName":"Scott","LastName":"Gu"}, "Email":"xxx2◎xxx2.com"}
    ];
    alert(UserList[0].Name.FirstName);
    </script>
    事实上除了使用"."引用属性外,我们还可以使用下面语句:

    alert(UserList[0]["Name"]["FirstName"]); 或者 alert(UserList[0].Name["FirstName"]);

    现在读者应该对JSON的使用有点认识了,归纳为以下几点:

    对象是属性、值对的集合。一个对象的开始于“{”,结束于“}”。每一个属性名和值间用“:”提示,属性间用“,”分隔。
    数组是有顺序的值的集合。一个数组开始于"[",结束于"]",值之间用","分隔。
    值可以是引号里的字符串、数字、true、false、null,也可以是对象或数组。这些结构都能嵌套。
    字符串和数字的定义和C或Java基本一致。
    小节:

    本文通过一个实例演示,初步了解了JSON 的强大用途。可以归结如下:

    JSON 提供了一种优秀的面向对象的方法,以便将元数据缓存到客户机上。
    JSON 帮助分离了验证数据和逻辑。
    JSON 帮助为 Web 应用程序提供了 Ajax 的本质。

    Posted Aug 16 2010, 02:35 PM by slash with no comments
    Filed under:
  • ASP.NET MVC案例教程

    摘要
          本文将简要介绍这个文章系列的目的、形式及大体内容。并且完成开始学习这个系列前所必要的准备工作。

    前言
          ASP.NET MVC作为微软官方的MVC解决方案,推出有一段时间了。可以说自动推出以来,一直广受关注。在经历了漫长的Preview之后,前几天终于推出了其beta版。并且在官方文档中,微软声明最终的正式版与beta版相比不会有大的变化。所以,对于.NET平台的开发人员来说,是时候学习ASP.NET MVC了。
          本系列文章作为一个ASP.NET MVC的入门教程,将不会长篇大论介绍其中的概念及理论。而是通过案例实践来学习ASP.NET MVC。在这系列文章中我将逐步完成一个“公告发布系统”。我的写作策略是:先动手做,遇到需要解释概念和理论的时候再解释,而不是先把概念和理论解释完再做东西。
          另外,我还有几点要说明的:
          1.为了将大家的关注点充分集中在ASP.NET MVC上,这个Demo的业务处理将使用Mock的方式。即不会真正去访问数据库,而是虚拟一些数据。
          2.本Demo将不考虑任何美工问题。
          下面,让我们一起开始ASP.NET MVC之旅吧。在这一篇中,我们将做完所有的准备工作。

    配置环境
          如果您还没有安装ASP.NET MVC的话,请到这里下载安装。
          下载后,按提示安装就可以了。

    新建项目
          安装完ASP.NET MVC后,在VS里新建一个项目,可以看到有一个“ASP.NET MVC Web Application”的选项,选择它,新建一个项目,并命名为“MVCDemo”。
          建完项目后,可以看到默认情况下已经建立了很多文件夹,这里简略说一下各个文件夹的作用。
          Content——存放应用需要的一些资源文件,如图片、CSS等。
          Controllers——存放控制器类。
          Models——存放业务模型组件。
          Scripts——存放JavaScript脚本文件。
          Views——存放视图。
          现在不了解一些概念没关系,后续文章将慢慢解释。

    准备工作
          新建完项目后,我们要做的准备工作就是建立Mock业务模型,这样以后我们就直接使用这些“欺骗”式的业务模型进行业务处理,而将全部关注点放在ASP.NET MVC的学习上。
          首先在Models下新建三个文件夹,分别叫做Entities、Interfaces、MockModels,分别用来存放实体类、接口及Mock业务模型。
          Entities下有两个类:CategoryInfo和AnnounceInfo,分别是公告类别和公告的实体类。具体代码如下:
    CategoryInfo.cs:

     1using System;
     2using
     System.Collections.Generic;
     3using
     System.Linq;
     4using
     System.Web;
     5

     6namespace
     MVCDemo.Models.Entities
     7
    {
     8    
    /// <summary>
     9    /// 分类实体类
    10    /// </summary>

    11    public class CategoryInfo
    12    
    {
    13        public int ID getset; }

    14        public string Name getset; }
    15    }

    16}


    AnnounceInfo.cs:

     1using System;
     2using
     System.Collections.Generic;
     3using
     System.Linq;
     4using
     System.Web;
     5

     6namespace
     MVCDemo.Models.Entities
     7
    {
     8    
    /// <summary>
     9    /// 公告实体类
    10    /// </summary>

    11    public class AnnounceInfo
    12    
    {
    13        public int ID getset; }

    14        public string Title getset; }
    15        public string Content getset; }
    16    }

    17}


          接下来,我们将定义两个接口,分别是公告类别服务和公告服务必须实现的接口。这两个接口放在Interfaces下。
    ICategoryService.cs:


    using System;
    using
     System.Collections.Generic;
    using
     System.Linq;
    using
     System.Text;
    using
     MVCDemo.Models.Entities;

    namespace
     MVCDemo.Models.Interfaces
    {
        
    /// <summary>

        
    /// 分类服务组件接口
        
    /// </summary>

        public interface ICategoryService
        {
            
    /// <summary>

            
    /// 添加分类
            
    /// </summary>

            
    /// <param name="category"></param>
            void Add(CategoryInfo category);

            
    /// <summary>

            
    /// 修改分类名称
            
    /// </summary>

            
    /// <param name="id"></param>
            
    /// <param name="name"></param>
            void ChangeName(int id,string name);

            
    /// <summary>

            
    /// 删除分类
            
    /// </summary>

            
    /// <param name="id"></param>
            void Remove(int id);

            
    /// <summary>

            
    /// 取得某个分类详细信息
            
    /// </summary>

            
    /// <param name="id"></param>
            
    /// <returns></returns>
            CategoryInfo GetDetail(int id);

            
    /// <summary>

            
    /// 取得所有分类
            
    /// </summary>

            
    /// <returns></returns>
            List<CategoryInfo> GetAll();
        }
    }


    IAnnounceService.cs


    using System;
    using
     System.Collections.Generic;
    using
     System.Linq;
    using
     System.Text;
    using
     MVCDemo.Models.Entities;

    namespace
     MVCDemo.Models.Interfaces
    {
        
    /// <summary>

        
    /// 公告服务组件接口
        
    /// </summary>

        public interface IAnnounceService
        {
            
    /// <summary>

            
    /// 发布公告
            
    /// </summary>

            
    /// <param name="announce"></param>
            void Release(AnnounceInfo announce);

            
    /// <summary>

            
    /// 修改公告信息
            
    /// </summary>

            
    /// <param name="announce"></param>
            void Notify(AnnounceInfo announce);

            
    /// <summary>

            
    /// 删除公告
            
    /// </summary>

            
    /// <param name="id"></param>
            void Remove(int id);

            
    /// <summary>

            
    /// 取得公告详细内容
            
    /// </summary>

            
    /// <param name="id"></param>
            
    /// <returns></returns>
            AnnounceInfo GetDetail(int id);

            
    /// <summary>

            
    /// 取得某个分类下的所有公告
            
    /// </summary>

            
    /// <param name="categoryId"></param>
            
    /// <returns></returns>
            List<AnnounceInfo> GetByCategory(CategoryInfo category);
        }
    }


    然后,我们在MockModels下建立两个Mock业务逻辑服务模型。注意它们要各自实现自己的接口。
    MockCategoryService.cs:


    using System;
    using
     System.Collections.Generic;
    using
     System.Linq;
    using
     System.Web;
    using
     MVCDemo.Models.Interfaces;
    using
     MVCDemo.Models.Entities;

    namespace
     MVCDemo.Models.MockModels
    {
        
    /// <summary>

        
    /// “欺骗”服务组件,用于模拟分类的业务服务
        
    /// </summary>

        public class MockCategoryService : ICategoryService
        {
            
    /// <summary>

            
    /// 添加分类
            
    /// </summary>

            
    /// <param name="category"></param>
            public void Add(CategoryInfo category)
            {
                
    return
    ;
            }

            
    /// <summary>

            
    /// 修改分类名称
            
    /// </summary>

            
    /// <param name="id"></param>
            
    /// <param name="name"></param>
            public void ChangeName(int id, string name)
            {
                
    return
    ;
            }

            
    /// <summary>

            
    /// 删除分类
            
    /// </summary>

            
    /// <param name="id"></param>
            public void Remove(int id)
            {
                
    return
    ;
            }

            
    /// <summary>

            
    /// 取得某个分类详细信息
            
    /// </summary>

            
    /// <param name="id"></param>
            
    /// <returns></returns>
            public CategoryInfo GetDetail(int id)
            {
                
    return new
     CategoryInfo
                {
                    ID 
    =
     id,
                    Name 
    = "最新通告"
    ,
                };
            }

            
    /// <summary>

            
    /// 取得所有分类
            
    /// </summary>

            
    /// <returns></returns>
            public List<CategoryInfo> GetAll()
            {
                List
    <CategoryInfo> categories = new List<CategoryInfo>
    ();
                
    for (int i = 1; i <= 5; i++
    )
                {
                    CategoryInfo category 
    = new
     CategoryInfo
                    {
                        ID 
    =
     i,
                        Name
    ="通告类别"+
    i,
                    };

                    categories.Add(category);
                }

                
    return
     categories;
            }
        }
    }


    MockAnnounceService.cs


    using System;
    using
     System.Collections.Generic;
    using
     System.Linq;
    using
     System.Web;
    using
     MVCDemo.Models.Interfaces;
    using
     MVCDemo.Models.Entities;

    namespace
     MVCDemo.Models.MockModels
    {
        
    /// <summary>

        
    /// “欺骗”服务组件,用于模拟公告的业务服务
        
    /// </summary>

        public class MockAnnounceService : IAnnounceService
        {
            
    /// <summary>

            
    /// 发布公告
            
    /// </summary>

            
    /// <param name="announce"></param>
            public void Release(AnnounceInfo announce)
            {
                
    return
    ;
            }

            
    /// <summary>

            
    /// 修改公告信息
            
    /// </summary>

            
    /// <param name="announce"></param>
            public void Notify(AnnounceInfo announce)
            {
                
    return
    ;
            }

            
    /// <summary>

            
    /// 删除公告
            
    /// </summary>

            
    /// <param name="id"></param>
            public void Remove(int id)
            {
                
    return
    ;
            }

            
    /// <summary>

            
    /// 取得公告详细内容
            
    /// </summary>

            
    /// <param name="id"></param>
            
    /// <returns></returns>
            public AnnounceInfo GetDetail(int id)
            {
                
    return new
     AnnounceInfo
                {
                    ID 
    =
     id,
                    Title 
    = "" + id + "则公告"
    ,
                    Content 
    = "全体同学明早九点集体做俯卧撑!"
    ,
                };
            }

            
    /// <summary>

            
    /// 取得某个分类下的所有公告
            
    /// </summary>

            
    /// <param name="categoryId"></param>
            
    /// <returns></returns>
            public List<AnnounceInfo> GetByCategory(CategoryInfo category)
            {
                List
    <AnnounceInfo> announces = new List<AnnounceInfo>
    ();
                
    for (int i = 1; i <= 10; i++
    )
                {
                    AnnounceInfo announce 
    = new
     AnnounceInfo
                    {
                        ID 
    =
     i,
                        Title 
    = category.Name+ "的第" + i + "则公告"
    ,
                        Content 
    = "全体同学明早九点集体做俯卧撑!"
    ,
                    };

                    announces.Add(announce);
                }

                
    return
     announces;
            }
        }
    }


          可以看到,这两个类并没有访问数据库,也没有实现真正的业务逻辑,而不过是“捏造”了一些数据骗骗我们的表示层而已。
          最后,我们要建立一个生成业务逻辑模型的生成器,用来实现表示层和业务逻辑层的解耦。当然,为了简化复杂度,这里没有使用依赖注入机制。下面是我们生成器的代码,这个类是直接放在Models下的。
    ServiceBuilder.cs:


    using System;
    using
     System.Collections.Generic;
    using
     System.Linq;
    using
     System.Web;
    using
     MVCDemo.Models.Interfaces;
    using
     MVCDemo.Models.MockModels;

    namespace
     MVCDemo.Models
    {
        
    /// <summary>

        
    /// 服务组件生成类,用于生成业务服务组件
        
    /// </summary>

        public sealed class ServiceBuilder
        {
            
    /// <summary>

            
    /// 创建分类服务组件
            
    /// </summary>

            
    /// <returns>分类服务组件</returns>
            public static ICategoryService BuildCategoryService()
            {
                
    return new
     MockCategoryService();
            }

            
    /// <summary>

            
    /// 创建公告服务组件
            
    /// </summary>

            
    /// <returns>公告服务组件</returns>
            public static IAnnounceService BuildAnnounceService()
            {
                
    return new
     MockAnnounceService();
            }
        }
    }


          OK,到这里,我们的准备工作就做完了。完成这些后,系统的目录结构如下图所示:

     
    小结
          在这篇文章中,我们只是讲了一下这个系列文章要做什么,以及为案例做了一些准备工作。从下篇开始,我们将正式开始使用ASP.NET MVC完成这个案例。

    摘要
          本文首先一步一步完成Demo的第一个页面——首页。然后根据实现过程,说明一下其中用到的与ASP.NET MVC相关的概念与原理。

    让第一个页面跑起来
          现在,我们来实现公告系统中的第一个页面——首页。它非常简单,只包括所有公告分类的列表,并且每个列表项是一个超链接。其中分类数据是用我们的Mock组件得到的。实现后界面如下:

     

          在开始之前,我们要删几个东西。因为默认情况下建立一个MVC项目时里面包含了几个示例页面,我们要做的就是:
          1.将Controllers文件夹下所有文件删除。
          2.将Views文件夹下除了Shared文件夹和Web.config外的所有文件删除,然后将Shared文件夹里面的文件删除。
          完成以上几步后,就可以开始实现第一个页面了。

    实现控制器
          在Controllers文件夹下新建一个文件,类型选择“MVC Controller Class”,名字命名为HomeController.cs。这就是一个控制器类。然后我们为它编码,具体代码如下:
    HomeController.cs:

     1using System;
     2using
     System.Collections.Generic;
     3using
     System.Linq;
     4using
     System.Web;
     5using
     System.Web.Mvc;
     6using
     System.Web.Mvc.Ajax;
     7using
     MVCDemo.Models;
     8using
     MVCDemo.Models.Interfaces;
     9using
     MVCDemo.Models.Entities;
    10

    11namespace
     MVCDemo.Controllers
    12
    {
    13    public class
     HomeController : Controller
    14    
    {
    15        public
     ActionResult Index()
    16        
    {
    17            ICategoryService cServ =
     ServiceBuilder.BuildCategoryService();
    18            ViewData["Categories"=
     cServ.GetAll();
    19            return View("Index"
    );
    20        }

    21    }

    22}


          直观看来,这个类不是很复杂。它首先继承了Controller类。Controller类是ASP.NET MVC框架中提供的一个控制器积累,所有我们自定义的控制器类都要继承此基类。然后这个类中有一个Index方法,返回值类型是ActionResult。
          这里对其中涉及到的概念简单解释一下。首先,控制器类可以说是ASP.NET MVC的核心类,因为它将处理一切请求,并处理所有页面转发等表示逻辑,这也是使用了ASP.NET MVC后与传统ASP.NET应用最大的差别。在传统模式下,一个用户请求的url将对应一个aspx文件,而在ASP.NET MVC下,一个用户请求对应某个控制器类中的一个方法,而这个方法,就叫做一个Action。至于如何对应的,则是通过对url的解析。
          例如,在传统情况下,http://localhost/Default.aspx表示请求网站根目录下的Default.aspx文件。而现在,url可能变成了这种样子:http://localhost/Home/Index。这个意思就是,请求名叫HomeController控制器类下的Index方法。一般地,默认情况下,请求url的格式为http://localhost/{ControllerName}/{ActionName}。其中{ControllerName}是控制器类名“Controller”前的部分,{ActionName}就是方法名。
          当然,这种映射规则是可以更改的,而且请求Action时也可以传递参数,但这些都是后话,以后再慢慢讨论。

          下面再深入Index方法,看看这个Action都做了什么。它首先调用了业务逻辑组件(当然,是Mock的),然后将GetAll返回的公告分类数据赋予ViewData["Category"],最后调用View()方法返回一个ActionResult。ViewData是什么呢?你可以把他理解成一个关联数组,它保存需要传给视图的数据。而View是Controller类的一个方法,它返回一个ActionResult实例。这样说可能有点抽象,其实直观就是将某个视图(一般就是一个aspx文件)呈现到浏览器中。那么如何知道呈现哪一个视图呢?默认情况下,View方法会到网站的Views文件夹下的与控制器类同名的文件夹下寻找与Action方法同名的视图。例如,HomeController的Index方法就会寻找Views/Home/Index.aspx,如果找不到,就会到Shared下寻找,再找不到就报错了。当然,你也可以给View方法传递一个字符串参数,表示视图名称。

    实现视图
          上文说到,当请求http://localhost/Home/Index时,HomeController的Index方法会被调用,而Index方法最后要呈现Views/Home/Index.aspx视图,所以,我们要在Views文件夹下建立一个Home文件夹,然后再新建一个Index.aspx视图。如果您使用的是VS2008 SP1,那么建立视图非常方便,只要在Home文件夹下右键单击,选Add--->View,然后指定视图名就可以了。如果不是SP1的,就新建一个Item,类型选择“MVC View Page”。建立好的视图其实就是一个aspx页面,但是其继承了View。这也是一个基类,所有视图需要继承它。
          下面给出Index.aspx的代码:
    Index.aspx:

     1<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Index.aspx.cs" Inherits="MVCDemo.Views.Home.Index" %>
     2<%@ Import Namespace="MVCDemo.Models.Entities" %>
     3
     4<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

     5
     6<html xmlns="http://www.w3.org/1999/xhtml" >

     7<head runat="server">
     8    <title></title>
     9</head>
    10<body>
    11    <% List<CategoryInfo> categories=ViewData["Categories"as List<CategoryInfo>%>
    12    <div>
    13        <h1>MVC公告发布系统</h1>
    14        <ul>
    15            <% foreach (CategoryInfo c in categories)
    16               {
    17                    
    %>
    18            <li><%= Html.ActionLink(c.Name, "List/" + c.ID, "Announce"%></li>
    19            <% } %>
    20        </ul>
    21    </div>
    22</body>
    23</html>


          大约分析一下。刚才说过,Index这个Action最后呈现这个aspx作为视图,而且ViewData中包含了要给视图传递的数据。在那里,我们将所有公告类别数据放在ViewData["Categories"]中。这里可以看到,我们将这些数据取出,并用来呈现页面。至于那个Html.Action,这里先不细说。你只要知道,这个方法可以生成一个链接,其中第一个参数是链接文字,第二个是要链接到的url的Action名,第三个是要链接到的url的控制器名。关于这些,我们以后细细讨论。
          运行这个例子,并将请求url定位到Home/Index,就可以看到运行效果。
          你可能会发现,不需要指定Home/Index,在输入根目录后就直接呈现了这个页面。其实这是因为在默认的路由配置里,Home和Index是默认的控制器名和Action名。以后我们将会讨论路由问题。

    小结
          通过上面的过程,我们第一个ASP.NET MVC页面已经能呈现出来了。而且不单纯只是一个页面,其中还呈现了业务逻辑组件返回的数据。
          也许,您对其中许多地方还有困惑。不要着急,在下一篇中,我们做这个系统的步伐先缓一缓,我将用一整篇文章,详细介绍一下ASP.NET MVC中许多重要的概念与原理。

    摘要
          本文对ASP.NET MVC的全局运行机理进行一个简要的介绍,以使得朋友们更好的理解后续文章。

    前言
          在上一篇文章中,我们实现了第一个ASP.NET MVC页面。对于没有接触过这个框架的朋友来说,可能对有些地方会迷惑,所以这篇文章我将通过图示配合文字的方法,站在全局的角度介绍一些ASP.NET MVC的运行机制,这样可以帮助朋友们更好的理解后续文章。^_^

    全局
          首先我们来看一副图片,由于这幅图是我自己画的,不是摘自微软官方,所以如果有什么不到位的地方还望海涵!


          首先,用户通过Web浏览器向服务器发送一条url请求,这里请求的url不再是xxx.aspx格式,而是http://HostName/ControllerName/ActionName/Parameters的样子。这个请求被ASP.NET MVC的路由映射系统截获。(路由映射可以在Global.asax中配置,我们一会再说)路由映射系统按照映射规则,解析出控制器名ControllerName,Action名ActionName和各个参数Parameters,然后,找寻Controllers目录下的ControllerNameController.cs这个控制器类,默认情况下,系统总是找寻Controllers目录下的“控制器名+Controller”这么一个类,然后,找寻这个类下与ActionName同名的方法,找到后,将Parameters作为参数传给这个方法,而后Action方法开始执行,完成后返回相应视图,默认情况下,会返回Views目录下与ControllerName同名的目录下的与ActionName同名的aspx文件,并且将ViewData传递到视图。ViewData中一般包含了控制视图显示的控制量以及视图显示需要的数据。
          我们按以上思路回顾一下上一篇中主页的请求过程。我们传递的url是http://localhost/Home/Index。默认路由规则下,将ControllerName设为“Home”,ActionName设为“Index”,没有参数。于是系统找寻Controllers目录下的HomeController类的Index方法,成功找到,于是执行之。这个方法调用Mock的Model取出一些数据,放入ViewData相应键值项里。然后返回视图,返回的是Views下Home下的Index.aspx。这个视图取出ViewData中的数据按照一定格式呈现,于是完成了一次典型的ASP.NET MVC调用。

    路由
          从上面可以看出,ASP.NET MVC中路由是很重要的。它直接决定了如何解析url,因此决定了系统如何工作。那么,下面我们来揭开路由神秘的面纱。
          打开我们Demo下的Global.asax.cs文件,可以看到如下代码:
    Global.asax.cs:

     1using System;
     2using
     System.Collections.Generic;
     3using
     System.Linq;
     4using
     System.Web;
     5using
     System.Web.Mvc;
     6using
     System.Web.Routing;
     7

     8namespace
     MVCDemo
     9
    {
    10    //
     Note: For instructions on enabling IIS6 or IIS7 classic mode, 
    11    // visit http://go.microsoft.com/?LinkId=9394801

    12
    13    public class MvcApplication : System.Web.HttpApplication
    14    
    {
    15        public static void
     RegisterRoutes(RouteCollection routes)
    16        
    {
    17            routes.IgnoreRoute("{resource}.axd/{*pathInfo}"
    );
    18

    19
                routes.MapRoute(
    20                "Default",                                              // Route name

    21                "{controller}/{action}/{id}",                           // URL with parameters
    22                new { controller = "Home", action = "Index", id = "" }  // Parameter defaults
    23            );
    24

    25        }

    26
    27        protected void
     Application_Start()
    28        
    {
    29
                RegisterRoutes(RouteTable.Routes);
    30        }

    31    }

    32}


          我们拣重点说。注意上面有个routes.MapRoute方法。这个方法的作用是向系统增加一条路由规则。这里唯一的一条规则是系统默认增加的,第一个参数是规则名,是一个普通字符串。关键是第二个参数,它也是一个字符串,但是它描述了如何解析url。可以这样理解,它描述了url串HostName后面部分如何匹配,其中带{}的表示参数匹配,如果不带则表示字符串匹配。
          例如,上面的{controller}/{action}/{id}表示如果HostName后面有三段由“/”分割的字符串,则这个url被匹配,并且分别被解析成控制器名,Action名和一个叫“id”的参数。如果你输入的是http://localhost/Home/Index/1则后面的“1”将被当做参数id的值,但是如果你请求http://localhost/Home/Index/1/2,抱歉,你的请求无法成功,因为这条路由规则没法匹配你的url,因为你的HostName后面有四段,而这个路由规则只能匹配三段的。
          也许你还注意到一个问题,http://localhost/Home/Index明明HostName后面只有两段,怎么也被匹配了呢?这就是MapRoute方法的第三个参数起作用了。这个参数的作用是为上面规则中各个{}匹配段设置默认值,如上,id的默认值为"",即空。所以在http://localhost/Home/Index中,虽然没有显示指定id,但是它依然可以匹配成功,默认作为空值。如果你把其中id=""去掉,你会发现http://localhost/Home/Index已经无法匹配了。依次类推,http://localhost/Home/也可以匹配成功,因为{action}默认是Index,http://localhost/也可以匹配成功,因为默认{controller}为Home,所以,在这条默认值下http://localhost/Home/Indexhttp://localhost/是等效的。
          综上分析,我们得出一条重要结论:在默认值被设置的情况下,映射规则“配少不配多”,少的部分由默认值代替。

          上面的匹配规则中,三个匹配段都带大括号的,都是参数匹配,下面我们来说说强字符串匹配。例如,我们有一个url需要这样http://localhost/Category/Detail/Name。如果按照上面的匹配规则,Name段的值会被匹配到id中去,可是我们想在CategoryController的Detail方法中使用名叫“name”的参数而不是使用名叫“id”的参数,怎么办呢?很简单,我们增加一下一条匹配规则:

     1using System;
     2using
     System.Collections.Generic;
     3using
     System.Linq;
     4using
     System.Web;
     5using
     System.Web.Mvc;
     6using
     System.Web.Routing;
     7

     8namespace
     MVCDemo
     9
    {
    10    //
     Note: For instructions on enabling IIS6 or IIS7 classic mode, 
    11    // visit http://go.microsoft.com/?LinkId=9394801

    12
    13    public class MvcApplication : System.Web.HttpApplication
    14    
    {
    15        public static void
     RegisterRoutes(RouteCollection routes)
    16        
    {
    17            routes.IgnoreRoute("{resource}.axd/{*pathInfo}"
    );
    18

    19
                routes.MapRoute(
    20                "Category",                                              // Route name

    21                "Category/Detail/{name}",                           // URL with parameters
    22                new { controller = "Category", action = "Detail", name = "" }  // Parameter defaults
    23            );
    24

    25

    26
                routes.MapRoute(
    27                "Default",                                              // Route name

    28                "{controller}/{action}/{id}",                           // URL with parameters
    29                new { controller = "Home", action = "Index", id = "" }  // Parameter defaults
    30            );
    31

    32        }

    33
    34        protected void
     Application_Start()
    35        
    {
    36
                RegisterRoutes(RouteTable.Routes);
    37        }

    38    }

    39}


          可以看到,我们在默认规则前增加了一条规则,其中其中控制器名和Action名不再是参数,而变成了强字符串(没有{})。这时,当我们请求的url是http://localhost/Cateogry/Detail/para的形式时,就会直接匹配新加的规则,而para的值不会被赋给成id而是赋给名叫name的变量。
          需要注意的是,我们新的路由规则一定要放在前面,因为ASP.NET MVC会自上向下匹配第一条找到的可匹配路由规则。

    视图
          说完了路由规则,我们再来说说视图。
          上面说道,Action方法返回类型是ActionResult,其实这个返回类型不局限于View方法返回ViewResult,它还有很多实现,这里列举几个。
          ViewResult:一般呈现某个aspx文件,由View方法返回。
          RedirectToResult:使浏览器重定向,由Redirect方法返回。
          RedirectToRouteResult:直接交给下一个Action,由RedirectToAction方法返回。
          还有几个,先不说了,因为后续文章基本用不到其他的,关于那几个以后朋友们可以自己看相关资料。

    小结
          看完这篇文章,就基本把90%的障碍扫清了。下面的文章中,将继续我们的实例。在下一篇中,我们来完成发布公告的功能,看看ASP.NET MVC下如何处理表单信息的传递。

    摘要
          本文将完成我们“MVC公告发布系统”的公告发布功能,以此展示在ASP.NET MVC中如何传递处理表单的数据。

    前言
          通过前几篇文章,我们已经能比较自如的使用ASP.NET MVC来呈现页面和数据了。但是,有一个大问题没有解决:如何处理表单数据。例如,我们将要实现的公告发布功能,用户肯定是在某个表单页面输入标题、正文等内容,而后提交,然后表单数据要被传递到相应的地方交由业务逻辑组件处理。
          在传统的ASP.NET下,使用的是Model1模式,每个aspx页面有一个同名的aspx.cs文件,当提交表单时,默认数据被提交到这个同名aspx.cs文件中某个方法下处理。但是,在ASP.NET MVC中,这种方法不能用了,因为我们换用了Model2模式,不能再用同名代码文件来处理aspx的提交请求(但是这不表明同名代码文件就没有用了,实际上,它依然会被执行,但是我们不提倡在里面处理任何逻辑,但是,有时会利用它进行一些初始化操作。),那么应该怎么做呢?不多讲,我们以例子说明问题。
          下面我们一步一步完成“MVC公告发布系统”的公告发布功能,等做完这个功能,上面的问题就明了了。

    先修改一个错误...
          这里,首先要像大家道歉,因为在第一篇里,我犯了一个错误。就是在公告的实体类AnnounceInfo中少了一个属性。现在,我们在AnnounceInfo中添加一个叫Cateogry的属性,类型为int,它用来指明这个公告属于哪个分类。
          对于这个错误,我十分抱歉。

    建立输入信息页面
          下面,正式开始我们的工作。首先,我要建立一个页面,用来让用户输入公告信息。而我们知道,在ASP.NET MVC中不能直接请求aspx文件,任何请求都要通过Controller,所以,我们首先在Controllers目录下建立一个新的Controller类,名叫AnnounceController。删除其中自动生成的Index方法,新建一个名叫Release的Action方法,具体代码如下。
    AnnounceController.cs:

     1using System;
     2using
     System.Collections.Generic;
     3using
     System.Linq;
     4using
     System.Web;
     5using
     System.Web.Mvc;
     6using
     System.Web.Mvc.Ajax;
     7using
     MVCDemo.Models;
     8using
     MVCDemo.Models.Interfaces;
     9using
     MVCDemo.Models.Entities;
    10

    11namespace
     MVCDemo.Controllers
    12
    {
    13    public class
     AnnounceController : Controller
    14    
    {
    15        public
     ActionResult Release()
    16        
    {
    17            ICategoryService cServ =
     ServiceBuilder.BuildCategoryService();
    18            List<CategoryInfo> categories =
     cServ.GetAll();
    19            ViewData["Categories"= new SelectList(categories, "ID""Name"
    );
    20            return View("Release"
    );
    21        }

    22    }

    23}


          这个就是要呈现表单页的Action方法,看看它做了什么:它首先取出所有的分类,然后将它们转成SelectList类型存入ViewData,最后呈现Release视图。
          为什么要取出所有分类呢?因为我们在发布公告时希望有个下拉列表框列出所有公告名称,让用户可以选择要发布的公告属于哪个分类。而SelectList是ASP.NET MVC中用于绑定到下拉列表的类型。它有很多重载的构造方法,其中我使用的是三个参数的,它们分别表示:生成数据的枚举,绑定到value的字段名,绑定到列表名称的字段名。这里,将把所有分类实体集合绑定到下拉列表,而ID属性作为值,Name属性作为显示在列表框中的名字。
          如果我们不需要下拉列表框来显示所有分类,那么Release方法只需一行return View("Release");就可以了。

          Action方法做完了,我们还需要视图。在Views目录下建立Announce目录,再在这个Announce目录下建立Release.aspx视图。代码如下。
    Release.aspx:

     1<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Release.aspx.cs" Inherits="MVCDemo.Views.Announce.Release" %>
     2<%@ Import Namespace="MVCDemo.Models.Entities" %>
     3
     4<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

     5
     6<html xmlns="http://www.w3.org/1999/xhtml" >

     7<head runat="server">
     8    <title></title>
     9</head>
    10<body>
    11    <% SelectList categories = ViewData["Categories"as SelectList; %>
    12    <div>
    13        <h1>MVC公告发布系统——发布公告</h1>
    14        <% Html.BeginForm("DoRelease","Announce",FormMethod.Post); %>
    15        <dl>
    16            <dt>标题:</dt>
    17            <dd><%= Html.TextBox("Title"%></dd>
    18            <dt>分类:</dt>
    19            <dd><%= Html.DropDownList("Category",categories) %></dd>
    20            <dt>内容:</dt>
    21            <dd><%= Html.TextArea("Content"%></dd>
    22        </dl>
    23        <input type="submit" value="发布" />
    24        <% Html.EndForm(); %>
    25    </div>
    26</body>
    27</html>


          代码不复杂,但是要注意几个地方。categories不多说了,这是刚才我们传递过来的所有分类组成的列表项。我觉得大家迷惑的可能是那些Html.***的东西,其实,Html是ViewPage的中的一个对象(ViewPage是所有视图的基类),它主要的左右就是产生各种表单项(先这么认为吧,其实它还有其他功能),例如Html.BeginForm就是说这里开始一个form标签,而Html.EndForm当然是form标签结束。其他几个,看名字相信大家也猜出来了。
          至于为什么这么做,也不直接使用原始的HTML标签,我先不多说,以后大家做多了自然就理解了,目前大家只要知道,这样做可以避免一个url问题以及让url更灵活就行了。^_^
          回到这个页面,BeginForm有三个参数,分别是提交请求的Action名,提交请求的Controller名和请求方式。所以,这个页面的意思就是使用post方法请求http://localhost/Announce/DoRelease这个Action来处理我们的请求。
          页面中有三个输入表单和一个提交按钮。三个输入表单分别是:名叫Title的文本框,名叫Content的文本域和名叫Category的下拉列表框。注意下拉列表是怎么绑定的,只要将含有数据的SelectList作为第二个参数就行了。完成后,页面是这样子的:

     
     
    处理请求
          现在我们可以输入信息了,但是如果你输入后点提交,你会发现产生了经典的404错误。刚才我们说了,表单提交到的Action是Announce下的DoRelease,但是现在没有这个Action,当然会404了。下面,我们来建立这个处理程序。
          回到AnnounceController,新建Action方法DoRelease,具体代码如下。
    AnnounceController.cs:
     1using System;
     2using
     System.Collections.Generic;
     3using
     System.Linq;
     4using
     System.Web;
     5using
     System.Web.Mvc;
     6using
     System.Web.Mvc.Ajax;
     7using
     MVCDemo.Models;
     8using
     MVCDemo.Models.Interfaces;
     9using
     MVCDemo.Models.Entities;
    10

    11namespace
     MVCDemo.Controllers
    12
    {
    13    public class
     AnnounceController : Controller
    14    
    {
    15        public
     ActionResult Release()
    16        
    {
    17            ICategoryService cServ =
     ServiceBuilder.BuildCategoryService();
    18            List<CategoryInfo> categories =
     cServ.GetAll();
    19            ViewData["Categories"= new SelectList(categories, "ID""Name"
    );
    20            return View("Release"
    );
    21        }

    22
    23        public
     ActionResult DoRelease()
    24        
    {
    25            AnnounceInfo announce = new
     AnnounceInfo()
    26            
    {
    27                ID = 1
    ,
    28                Title = Request.Form["Title"
    ],
    29                Category = Int32.Parse(Request.Form["Category"
    ]),
    30                Content = Request.Form["Content"
    ],
    31            }
    ;
    32

    33            IAnnounceService aServ =
     ServiceBuilder.BuildAnnounceService();
    34
                aServ.Release(announce);
    35

    36            ViewData["Announce"=
     announce;
    37            return View("ReleaseSucceed"
    );
    38        }

    39    }

    40}

          我们看,它首先新建一个AnnounceInfo类型的实体类,用来存贮这个新的公告的信息。注意它是怎么得到表单信息的,对了,用了Request.Form["表单名"],这就是获得表单信息的一种方法,当然还有其他方法,但是我推荐这一种。注意,这里的表单名就是我们使用Html.***方法生成表单时的名字。
          OK,下面就是调用业务逻辑组件,完成发布公告功能。
          但是这里有个问题,我们的业务逻辑组件是Mock的,也就是说其实什么都没做啊。如果是真的业务逻辑组件,我们可以去数据库看看有没有添加公告信息成功,可是这里没有,我们要怎么证明表单数据传递过来了呢?于是我想了一个办法,有新加了一个ReleaseSucceed视图,用来显示新发布公告的信息,以此证明我们确实把表单信息传过来了。ReleaseSucceed视图如下:
    ReleaseSucceed.aspx:
     1<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="ReleaseSucceed.aspx.cs" Inherits="MVCDemo.Views.Announce.ReleaseSucceed" %>
     2<%@ Import Namespace="MVCDemo.Models.Entities" %>
     3
     4<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

     5
     6<html xmlns="http://www.w3.org/1999/xhtml" >

     7<head runat="server">
     8    <title></title>
     9</head>
    10<body>
    11    <% AnnounceInfo announce = ViewData["Announce"as AnnounceInfo; %>
    12    <div>
    13        <h1>MVC公告发布系统——发布公告成功</h1>
    14        <dl>
    15            <dt>ID:</dt>
    16            <dd><%= announce.ID %></dd>
    17            <dt>标题:</dt>
    18            <dd><%= announce.Title %></dd>
    19            <dt>类别ID:</dt>
    20            <dd><%= announce.Category %></dd>
    21            <dt>内容:</dt>
    22            <dd><%= announce.Content %></dd>
    23        </dl>
    24    </div>
    25</body>
    26</html>

          这些代码就不用我过多解释了。下面,我们输入一些信息,提交看看:
     
          看到没有,我没有骗你们,表单数据真的传过来了!^_^|||

    小结
          通过这四篇文章,我们已经了解了ASP.NET MVC的基本原理,并且已经会呈现数据页面及传递表单数据处理了。会了这些,其实已经可以应付绝大多数主要开发了。从下篇开始,我们接触一些高级点的内容。下篇将说一下ASP.NET MVC如何与ASP.NET AJAX及JQuery结合,再后面,会讲到拦截器及与Silverlight结合的内容。

    摘要
          本文将从完成“输入数据验证”这个功能出发,逐渐展开ASP.NET MVC与Ajax结合的方法。首先,本文将使用ASP.NET MVC提供的同步方式完成数据验证。而后,将分别结合ASP.NET AJAX和JQuery将这个功能重构成异步形式。

    数据验证
          在上一篇文章中,我们完成了发布公告的功能。但是从健壮性角度看,这个功能并不完善,因为一般情况下,我们输入的数据要符合一定的约束条件,例如,在我们的例子中,我们至少不能将空字符串作为标题或内容吧。下面,我们来为程序加入数据验证功能,
          ASP.NET MVC中提供了良好的数据验证实现支持,下面我们来看实现过程。首先,我们要修改一下Release.aspx视图,修改后的视图如下。
    Release.aspx:

     1<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Release.aspx.cs" Inherits="MVCDemo.Views.Announce.Release" %>
     2<%@ Import Namespace="MVCDemo.Models.Entities" %>
     3
     4<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

     5
     6<html xmlns="http://www.w3.org/1999/xhtml" >

     7<head runat="server">
     8    <title></title>
     9</head>
    10<body>
    11    <% SelectList categories = ViewData["Categories"as SelectList; %>
    12    <div>
    13        <h1>MVC公告发布系统——发布公告</h1>
    14        <% Html.BeginForm("DoRelease","Announce",FormMethod.Post); %>
    15        <dl>
    16            <dt>标题:</dt>
    17            <dd><%= Html.TextBox("Title"%></dd>
    18            <dd><%= Html.ValidationMessage("TitleValidator"%></dd>
    19            <dt>分类:</dt>
    20            <dd><%= Html.DropDownList("Category",categories) %></dd>
    21            <dd></dd>
    22            <dt>内容:</dt>
    23            <dd><%= Html.TextArea("Content"%></dd>
    24            <dd><%= Html.ValidationMessage("ContentValidator"%></dd>
    25        </dl>
    26        <input type="submit" value="发布" />
    27        <% Html.EndForm(); %>
    28    </div>
    29</body>
    30</html>


          可以看到,并没有什么大的变动,只是多了两个Html.ValidationMessage方法。可以这样理解,这个方法相当于产生一个span标签,而这个span就是要显示错误信息的地方。这个方法接收一个参数,用来指明其在Controller中的名字。如果你对这个迷惑,不要紧,接下来看完Controller的代码,你就什么都清楚了。
    AnnounceController.cs:

     1using System;
     2using
     System.Collections.Generic;
     3using
     System.Linq;
     4using
     System.Web;
     5using
     System.Web.Mvc;
     6using
     System.Web.Mvc.Ajax;
     7using
     MVCDemo.Models;
     8using
     MVCDemo.Models.Interfaces;
     9using
     MVCDemo.Models.Entities;
    10

    11namespace
     MVCDemo.Controllers
    12
    {
    13    public class
     AnnounceController : Controller
    14    
    {
    15        public
     ActionResult Release()
    16        
    {
    17            ICategoryService cServ =
     ServiceBuilder.BuildCategoryService();
    18            List<CategoryInfo> categories =
     cServ.GetAll();
    19            ViewData["Categories"= new SelectList(categories, "ID""Name"
    );
    20            return View("Release"
    );
    21        }

    22
    23        public
     ActionResult DoRelease()
    24        
    {
    25            if (String.IsNullOrEmpty(Request.Form["Title"]) || String.IsNullOrEmpty(Request.Form["Content"
    ]))
    26            
    {
    27                if (String.IsNullOrEmpty(Request.Form["Title"
    ]))
    28                
    {
    29                    ViewData.ModelState.AddModelError("TitleValidator","公告标题不能为空!"
    );
    30                }

    31                if (String.IsNullOrEmpty(Request.Form["Content"]))
    32                
    {
    33                    ViewData.ModelState.AddModelError("ContentValidator""公告内容不能为空!"
    );
    34                }

    35
    36                return
     Release();
    37            }

    38
    39            AnnounceInfo announce = new
     AnnounceInfo()
    40            
    {
    41                ID = 1
    ,
    42                Title = Request.Form["Title"
    ],
    43                Category = Int32.Parse(Request.Form["Category"
    ]),
    44                Content = Request.Form["Content"
    ],
    45            }
    ;
    46

    47            IAnnounceService aServ =
     ServiceBuilder.BuildAnnounceService();
    48
                aServ.Release(announce);
    49

    50            ViewData["Announce"=
     announce;
    51            return View("ReleaseSucceed"
    );
    52        }

    53    }

    54}


          可以看到,我们的DoRelease这个Action方法多了不少东西。我们看多了什么:当从表单传递过来的标题或内容为空时,我们做了一定处理。注意,这个ViewData.ModelState.AddModelError方法,它就是往我们刚才说的由Html.ValidationMessage生成的span里加入错误信息的方法,它可以有两个参数,第一个指明哪个span,这个参数Html.ValidationMessage中的参数是对应的。第二个参数就是要显示的信息。
          相信结合视图和控制器,已经很好理解了。最后,如果标题或内容有空值的话,我们不再调用业务逻辑组件处理了,而是调用了Release这个Action。为什么我们不用Redirect呢?因为我们要保持ViewData中的数据,刚才我们的错误信息可都放在里面的,而使用了Redirect,ViewData的信息就传不过去了。
          现在,我们再来发布公告。我们故意什么都不填,提交,看结果:

     
          没有问题,我们的程序成功对标题和内容进行了完整性检测(这里就是均不能为空),在验证不通过时,返回了发布公告视图并正确显示了错误提示信息。
          也许你有一个疑问,为什么第一次请求Release视图时没有显示任何错误信息呢?因为那时ViewData中的ModelError是空的。而Html.ValidationMessage生成的标签会自动寻找ModelError中同名的错误信息,找不到,当然是空的了。而在提交空信息时,DoRelease这个Action为ViewData的ModelError添加了内容,于是当再次返回Release视图时,相应信息就显示在我们指定的位置了。

    使用ASP.NET AJAX实现客户端数据验证
          上面的代码运行起来没问题,也达到了我们的要求。但是验证标题内容是否为空这种行为在客户端应该就可以完成。当然,为了放置恶意攻击或浏览器将JavaScript屏蔽的情况,我们应该在后台进行验证,但是我们不能每次都将这种请求发到后台去验证,这太费资源了,毕竟恶意攻击者和JavaScript被屏蔽的浏览器只是少数。所以,在数据被送到后台前,我们应该先进行一遍验证,这样可以节约很多资源。
          下面,我们使用ASP.NET AJAX框架完成客户端的数据验证。
          说实话,在ASP.NET MVC中使用ASP.NET AJAX或JQuery实在太方便了,不信你展开Scripts文件夹,看到没,微软已经把这些库放到里面了,所以,我们要做的只是直接引用。看我们修改后的Release.aspx。
    Release.aspx:
     1<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Release.aspx.cs" Inherits="MVCDemo.Views.Announce.Release" %>
     2<%@ Import Namespace="MVCDemo.Models.Entities" %>
     3
     4<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

     5
     6<html xmlns="http://www.w3.org/1999/xhtml" >

     7<head runat="server">
     8    <title></title>
     9    <script type="text/javascript" src="<%= Url.Content("~/Scripts/MicrosoftAjax.debug.js") %>"></script>
    10    <script type="text/javascript" src="<%= Url.Content("~/Scripts/MicrosoftAjaxValidate.js") %>"></script>
    11</head>
    12<body>
    13    <% SelectList categories = ViewData["Categories"] as SelectList; %>
    14    <div>
    15        <h1>MVC公告发布系统——发布公告</h1>
    16        <% Html.BeginForm("DoRelease","Announce",FormMethod.Post); %>
    17        <dl>
    18            <dt>标题:</dt>
    19            <dd><%= Html.TextBox("Title"%></dd>
    20            <dd><span id="TitleValidator"></span></dd>
    21            <dt>分类:</dt>
    22            <dd><%= Html.DropDownList("Category",categories) %></dd>
    23            <dd></dd>
    24            <dt>内容:</dt>
    25            <dd><%= Html.TextArea("Content"%></dd>
    26            <dd><span id="ContentValidator"></span></dd>
    27        </dl>
    28        <input id="Submit" type="submit" value="发布" />
    29        <% Html.EndForm(); %>
    30    </div>
    31</body>
    32</html>

          改动有两处,首先我们在页头引用了两个js文件,第一个是ASP.NET AJAX的库文件,第二个就是我们一会要实现的包含验证代码的js文件了。你可能注意到那个Url.Content了,Url是ViewPage的一个对象,它最常用的一个方法就是Content,它的功能是返回某个文件的路径。一般情况下,在使用了ASP.NET MVC后,目录结构变得有点诡异,像js、css、图片等与路径(即使是相对路径)引用相关的地方可能会出现问题,但是,只要你在这些地方用Url.Content生成路径,而不是直接将路径写在页面里,一般就没什么问题了。所以,凡是引用js、css、图片等除,请一定使用Url.Content生成路径,其参数只有一个,就是文件原始的相对路径。
          下一个改动就是显示错误信息的span不再是Html.ValidationMessage生成的了,而是普通的span。
          下面我们在Scripts目录下新建MicrosoftAjaxValidate.js文件。
    MicrosoftAjaxValidate.js:
     1Sys.Application.add_init(onPageInit);
     2

     3function onPageInit() 
    {
     4    $addHandler($get("Submit"), "click"
    , validate);
     5}

     6
     7function validate() 
    {
     8    if ($get("Title").value == "" || $get("Content").value == ""
    {
     9        if ($get("Title").value == ""
    {
    10            $get("TitleValidator").innerHTML = "标题不能为空!"
    ;
    11        }

    12        if ($get("Content").value == ""{
    13            $get("ContentValidator").innerHTML = "内容不能为空!"
    ;
    14        }

    15
    16        return false
    ;
    17    }

    18
    19    return true
    ;
    20}

          关于这段代码我不多说了,对ASP.NET AJAX有兴趣的可以参看《ASP.NET AJAX客户端编程之旅》系列文章。
          现在运行,将标题和内容留空,提交。OK!效果和刚才很像,只不过这次是在客户端验证了,并没有提交到服务器端。

    整合JQuery
          下面我们再使用JQuery实现这个功能。
          其实看懂上面的实现后,我想你已经想到怎么整合JQuery了,无非也是引入相应库和js文件,然后使用JQuery编写验证代码。修改后的Release.aspx就没必要看了,无非是引入Scripts目录下的JQuery库,然后再引入一个自定义验证js文件,我们姑且叫JQueryValidate.js吧。
          下面在Scripts目录下新建JQueryValidate.js,代码如下。
    JQueryValidate.js:
     1$(document).ready(function(){
     2    $("#Submit").click(function() 
    {
     3        if ($("#Title").attr("value"== "" || $("#Content").attr("value"== ""
    {
     4            if ($("#Title").attr("value"== ""
    {
     5                $("#TitleValidator").attr("innerHTML""标题不能为空!"
    );
     6            }

     7            if ($("#Content").attr("value"== ""{
     8                $("#ContentValidator").attr("innerHTML""内容不能为空!"
    );
     9            }

    10
    11            return false
    ;
    12        }

    13
    14        return true
    ;
    15    }

    16    );
    17}

    18);


    小结
          从本文可以看出,在MVC框架中整合Ajax和普通应用差别不大,唯一就是注意在引用外部js时使用Url.Content方法处理一下相对路径。其实在本文中我们并没有使用到Ajax,而仅仅是整合了JavaScirpt,但是这已经足够了,因为Ajax无非就是在这些JavaScript里包含了异步后台调用。
          其实,ASP.NET MVC有专门针对ASP.NET AJAX的扩展,放在MicrosoftMvcAjax.js里。而在ViewPage里有个叫Ajax的AjaxHelper对象,可以实现一些简单的异步调用。但是这个扩展的功能很有限,有兴趣的可以自己研究一下。我个人还是建议大家自己写JS代码,当然可以使用ASP.NET AJAX或JQeury这样优秀的框架。
          这篇文章先到这里,下一篇中我们讨论一下拦截器的使用。^_^

    摘要
          本文将对“MVC公告发布系统”的发布公告功能添加日志功能和异常处理功能,借此来讨论ASP.NET MVC中拦截器的使用方法。

    一个小难题
          我们继续完善“MVC公告发布系统”,这次,我们的需求是对公告发布功能添加日志记录能力,即在发布公告前,记录一次,在公告发布成功后,再记录一次。然后还要使得其具备异常处理,即当业务组件出现问题时,跳转到相应的错误页面并显示相应提示。
          有人可能笑了,这有什么难的,在DoRelease这个Action的开始和结束处各加入相应日志功能不久结了。异常处理更不在话下,直接try...catch搞定。
          没错,以上方法确实行得通,但是存在以下两点问题:
          1.代码重复问题。很多日志处理代码和异常处理代码是很相似的,这样就导致了各个Action中存在大量重复代码。
          2.职责被破坏。不要忘了,我们的Controller仅仅是控制器,它应该只负责表示逻辑,而不应该被一大堆日志处理代码和try...catch块包围。我们要的Action,应该是干净的、工整的、仅包含表示逻辑的Action。
          以上两点,造成了我们系统中的坏味代码。那么,怎么解决这个问题呢?

    从厨师到AOP
          先来想象一个场景:饭店里的高级厨师怎么工作?我们知道,他不用洗菜切菜、不用端着盘子送菜、如果发现手里牛肉变质了他更不用拿着牛肉去找肉店老板理论,他的工作很单一:炒菜。
          当原料送来后,有专门的顺菜切菜工进行洗菜、切菜,然后把处理好的菜送给厨师,厨师只管下锅炒,炒完了送菜自然也不必关心,因为有专门的服务员负责这事。如果发现牛肉变质了,它只管说一声,自然有相应的人处理这事。
          这个场景就是典型的AOP(面向切面编程)。厨师可以看成是业务组件,它有个方法就是“炒菜”,但是炒菜前要切菜,炒完了要有人送菜,可这不是厨师该关心的事啊!于是我们的切菜工和服务员就相当于拦截器,其中切菜工在炒菜前拦截,进行切菜,服务员在炒菜后拦截,负责送菜。当然,我们还有个异常拦截器:处理问题的人,就是那个当厨师发现肉变质了喊一声,就来处理的人。
          基于这个场景,我们看看这样有什么好处。首先是厨师职责单一了,他可以专注于自己的工作:炒菜,而不必理会不该自己关心的问题。而且“拦截器们”可以复用的,例如一个抠门的老板完全可以找3个厨师但是只招一名服务员,反正一名服务员就可以给三名厨师端菜,这样,拦截器的复用使得代码重复不见了!

    回来
          好的,现在回到我们的“MVC公告发布系统”。相信看了上面的场景,你的灵感一定来了:对啊,Action不就是厨师吗,如果我们可以将日志功能做成拦截器,在DoRelease执行前先拦截一次完成记录日志功能,DoRelease执行后再拦截一次记录一次日志。最好还有个拦截器,在Action发生异常的时候可以拦截处理(就像上文处理变质牛肉的人),不就搞定了吗。
          可是要怎么实现拦截Action呢?真是幸运之极,ASP.NET MVC框架中内置了这种机制!哈哈,我们赶快来做吧!

    实现拦截器
          在ASP.NET MVC中,有三种拦截器:Action拦截器、Result拦截器和Exception拦截器。我要用到第一种和第三种。其实所谓的ASP.NET MVC拦截器,也没什么神秘的,就是一个普通的类而已。只不过需要继承FilterAttribute基类,Action拦截器还要实现IActionFilter接口,而Exception拦截器需要实现IExceptionFilter接口。
          我们先来看实现:让我们在Controllers目录下新建一个Filters目录,然后在Filters下新建两个类,一个叫LoggerFilter一个叫ExceptionFilter。首先是LoggerFilter的代码。
    LoggerFilter.cs:

     1using System;
     2using
     System.Collections.Generic;
     3using
     System.Linq;
     4using
     System.Web;
     5using
     System.Web.Mvc;
     6using
     System.Web.Mvc.Ajax;
     7

     8namespace
     MVCDemo.Controllers.Filters
     9
    {
    10    public class
     LoggerFilter : FilterAttribute, IActionFilter
    11    
    {
    12        void
     IActionFilter.OnActionExecuting(ActionExecutingContext filterContext)
    13        
    {
    14            filterContext.Controller.ViewData["ExecutingLogger"= "正要添加公告,已以写入日志!时间:" +
     DateTime.Now; 
    15        }

    16
    17        void
     IActionFilter.OnActionExecuted(ActionExecutedContext filterContext)
    18        
    {
    19            filterContext.Controller.ViewData["ExecutedLogger"= "公告添加完成,已以写入日志!时间:" +
     DateTime.Now;
    20        }

    21    }

    22}

          可以看到,这个类继承了FilterAttribute并实现了IActionFilter。其中关键是IActionFilter,它有两个方法,OnActionExecuting在被拦截Action前执行,OnActionExecuted在被拦截Action后执行。两个方法都有一个参数,虽然类型不同,但其实都是一个作用:被拦截Action的上下文。
          这个地方我得解释一下,你拦截器拦截了Action,在做处理时难免要用到被拦截Action相关的东西,例如在我们的例子中,就需要想被拦截Action所在Controller的ViewData中添加内容,所以,拦截器方法有一个参数表示被拦截Action的上下文是顺理成章的事。
          下面再看ExceptionFilter这个拦截器,它是在Action出现异常时发挥作用的。
    ExceptionFilter.cs:
     1using System;
     2using
     System.Collections.Generic;
     3using
     System.Linq;
     4using
     System.Web;
     5using
     System.Web.Mvc;
     6using
     System.Web.Mvc.Ajax;
     7

     8namespace
     MVCDemo.Controllers.Filters
     9
    {
    10    public class
     ExceptionFilter : FilterAttribute,IExceptionFilter
    11    
    {
    12        void
     IExceptionFilter.OnException(ExceptionContext filterContext)
    13        
    {
    14            filterContext.Controller.ViewData["ErrorMessage"=
     filterContext.Exception.Message;
    15            filterContext.Result = new
     ViewResult()
    16            
    {
    17                ViewName = "Error"
    ,
    18                ViewData =
     filterContext.Controller.ViewData,
    19            }
    ;
    20            filterContext.ExceptionHandled = true
    ;
    21        }

    22    }

    23}

          异常拦截器一样需要继承FilterAttribute,但是不要实现IActionFilter,而是要实现IExceptionFilter接口,这个接口只有一个方法:OnException,顾名思义,当然是发生异常时被调用了。我们看看我让它做了什么:首先将异常信息(ExceptionContext一样也是上下文,而其成员的Exception就是一个Exception类型的实例,就是被抛出的异常)记录到ViewData相应的键值里,然后我们要呈现Error这个视图。
          注意!这里已经不是Controller里了,而是另一个类,所以当然不能调用View方法返回ViewResult实例了。我们只好新建一个ViewResult实例,并将其视图名设为Error,将上下文中的DataView传过去。
          最后那行filterContext.ExcepitonHandled = true;很重要,这行的意思是告诉系统,异常已经处理,不要再次处理了。

    应用拦截器
          好了,拦截器建立完了,要怎么应用到相应的Action上呢?如果你使用过Spring,你一定对其AOP是实现之麻烦深有感触,如果你和我一样讨厌写各种XML的话,你真是太幸福了。因为在ASP.NET MVC中,应用拦截器简直是轻松加愉快。只要将拦截器当做Attribute写在要应用此拦截器的Action上就行了。看代码。
    AnnounceController.cs:
     1using System;
     2using
     System.Collections.Generic;
     3using
     System.Linq;
     4using
     System.Web;
     5using
     System.Web.Mvc;
     6using
     System.Web.Mvc.Ajax;
     7using
     MVCDemo.Models;
     8using
     MVCDemo.Models.Interfaces;
     9using
     MVCDemo.Models.Entities;
    10using
     MVCDemo.Controllers.Filters;
    11

    12namespace
     MVCDemo.Controllers
    13
    {
    14    public class
     AnnounceController : Controller
    15    
    {
    16        public
     ActionResult Release()
    17        
    {
    18            ICategoryService cServ =
     ServiceBuilder.BuildCategoryService();
    19            List<CategoryInfo> categories =
     cServ.GetAll();
    20            ViewData["Categories"= new SelectList(categories, "ID""Name"
    );
    21            return View("Release"
    );
    22        }

    23
    24
            [LoggerFilter()]
    25
            [ExceptionFilter()]
    26        public
     ActionResult DoRelease()
    27        
    {
    28            AnnounceInfo announce = new
     AnnounceInfo()
    29            
    {
    30                ID = 1
    ,
    31                Title = Request.Form["Title"
    ],
    32                Category = Int32.Parse(Request.Form["Category"
    ]),
    33                Content = Request.Form["Content"
    ],
    34            }
    ;
    35

    36            IAnnounceService aServ =
     ServiceBuilder.BuildAnnounceService();
    37
                aServ.Release(announce);
    38

    39            ViewData["Announce"=
     announce;
    40

    41            System.Threading.Thread.Sleep(2000
    );
    42            ViewData["Time"=
     DateTime.Now;
    43            System.Threading.Thread.Sleep(2000
    );
    44

    45            return View("ReleaseSucceed"
    );
    46        }

    47    }

    48}

          看到没有,只要在DoRelease上写这么两个Attribute,一切就完成了,至于什么时候该调用什么拦截器,都是框架帮你完成了。注意一点,为了让我们看出拦截器的时序,我们在DoRelease中加了一点东西,就是加了一个ViewData["Time"],里面记录了执行此Action的时间,因为日志拦截器在前后都会记录时间,我们通过比较时间就可以看出执行顺序了。至于那两个Sleep则是让效果更明显的,这行代码的意思是让程序在这里延迟2秒。

          要执行这个程序,我们还要改一下ReleaseSucceed.aspx视图,其实就是加几个地方显示ViewData里相应的数据。
    ReleaseSucceed.aspx:
     1<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="ReleaseSucceed.aspx.cs" Inherits="MVCDemo.Views.Announce.ReleaseSucceed" %>
     2<%@ Import Namespace="MVCDemo.Models.Entities" %>
     3
     4<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

     5
     6<html xmlns="http://www.w3.org/1999/xhtml" >

     7<head runat="server">
     8    <title></title>
     9</head>
    10<body>
    11    <% AnnounceInfo announce = ViewData["Announce"as AnnounceInfo; %>
    12    <div>
    13        <h1>MVC公告发布系统——发布公告成功</h1>
    14        <dl>
    15            <dt>ID:</dt>
    16            <dd><%= announce.ID %></dd>
    17            <dt>标题:</dt>
    18            <dd><%= announce.Title %></dd>
    19            <dt>类别ID:</dt>
    20            <dd><%= announce.Category %></dd>
    21            <dt>内容:</dt>
    22            <dd><%= announce.Content %></dd>
    23            <dt>发布时间:</dt>
    24            <dd><%= ViewData["Time"%></dd>
    25        </dl>
    26        <p><%= ViewData["ExecutingLogger"%></p>
    27        <p><%= ViewData["ExecutedLogger"%></p>
    28    </div>
    29</body>
    30</html>

          现在可以提交一则公告看结果了:
     
          没有问题,拦截器方法顺利执行,而且从时间可以看出,OnActionExecuting先执行,Action执行,然后OnActionExecuted执行。

          下面我们来看看异常拦截器的效果。要触发异常拦截器,首先要抛出一个异常,所以,我们在业务逻辑组件做点手脚。将MockAnnounceServices的Release方法改成如下:
    1/// <summary>
    2/// 发布公告
    3/// </summary>

    4/// <param name="announce"></param>

    5public void Release(AnnounceInfo announce)
    6
    {
    7    throw new Exception("发布公告失败了!原因?没有原因!我是业务组件,我说失败就失败!"
    );
    8    return
    ;
    9}

          另外,我们还要实现一个Error.aspx视图,这是在异常拦截器中定义的错误视图。我们将它新建在Views/Shared下就可以了。顺便说一下,共用的视图一般放在Shared下,因为ASP.NET MVC的视图寻找机理是当与Controller同名目录下不存在时,就到Shared下看看有没有此视图。
    Error.aspx:

     

     1<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Error.aspx.cs" Inherits="MVCDemo.Views.Shared.Error" %>
     2
     3<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

     4
     5<html xmlns="http://www.w3.org/1999/xhtml" >

     6<head runat="server">
     7    <title></title>
     8</head>
     9<body>
    10    <div>
    11        <h1>系统发生异常</h1>
    12        <%= ViewData["ErrorMessage"%>
    13    </div>
    14</body>
    15</html>


          好了,现在我们再提交新公告,会返回如下结果:

     
          很明显,业务组件抛出了异常,但是我们的Action方法中并没有用try...catch处理,但是异常拦截器成功拦截了异常,并做了相应处理。
          我们再回过头看看,使用了拦截器后,代码是不是很清晰呢。

    小结
          通过本文,朋友们应该可以掌握拦截器的基本使用以及使用它在表示层实现AOP了。下一篇作为本系列的终结篇,将对ASP.NET MVC做一个全面的讨论,并与Web Form模型进行一个比较,使朋友们看清其优势、劣势,从而更好的学习使用这个框架。

    摘要
          本文作为《ASP.NET MVC案例教程》的完结篇,仅从个人角度,发表一些对ASP.NET MVC框架的看法。并且在最后会附上本系列文章的Demo下载。

    前言
          写这篇文章的目的,是想总结一些东西,以帮助朋友们更好的使用这个框架。但是,我又不像把官方列举的哪些优势、功能翻译过来列举在这里。所以,我想干脆我就纯从个人观点上对这个框架评论一下吧。说的不好的,不对的还请批评指正。^_^

    ASP.NET MVC——螺旋进步的产物
          对于微软为什么要推出ASP.NET MVC,我们是无从得知的,也许是因为JavaEE平台上有Struts,也许是因为MVC太流行,也许微软是想使得自己的Web App平台更完善,总之我们只能猜测。但是如果回顾一下微软的Web App平台进化过程,还是很有意思的。

          ASP——微软最早为Web开发做出的贡献可能就是ASP了,这个动态语言把动态网页开发的难度空前降低了。但是,在很多人兴奋的用ASP写着一个又一个动态网页时,它的缺点渐渐暴露:语言过于简单,没有面向对象支持、没有好的IDE支持、动态脚本和静态HTML杂糅在一起,使得修改及维护极为困难。
          Web Form——说实话,即使是用现在的眼光看,微软推出的Web Form编程模型确实是很有创意,也很实用。微软开创性地将桌面应用的开发模式引入Web应用开发:拖控件、写事件处理、运行...一切都那么美好,而且前段静态代码和后端程序完全隔离在两个文件里,并且用户可以使用.NET平台上任意一种语言进行后端编程。对程序员来说,使用C#进行编程比使用ASP实在是舒服太多了。所以,Web Form模型可以说成为.NET Web App开发的代名词,所有基于.NET平台的Web开发人员都熟悉并接受了这种模型。
          ASP.NET MVC——就在Web Form大行其道时,微软推出了ASP.NET MVC。严格说,ASP.NET MVC和Web Form是不具有可比性的,Web Form是一个完整的新型模型,从顶层到底层是一整套的东西,而ASP.NET MVC只是给Web Form穿了件MVC样子的外套,它应该是基于Web Form的一种编程方式模型扩展。但是,从开发人员看,ASP.NET MVC的推出确实大大改变了我们的开发方式,很多Web Form下的方式不被提倡了(你仍可以用,因为ASP.NET MVC也是基于Web Form的),例如,曾饱受赞扬的服务器端控件再度被抛弃,转而再次使用客户端控件,事件驱动模型被抛弃,转而使用了类似传统的Url跳转处理模型。而且在数据验证等方式上与Web Form下提倡的方式有了很大变化。

          如此看来,真像是一个轮回,似乎ASP.NET MVC又把我们带回到了ASP时代:服务器端模型不让用、事件驱动机制不让用、类似Desktop App的开发方式不让用...我们似乎从Web Form回到了传统的ASP时代。但是,真的是这样吗?当然不是!
          只要稍微用一下,就知道虽然ASP.NET MVC提倡我们废除Web Form下的很多东西和习惯,但是绝不是让我们“回归原始”,如果非要说是一个轮回,那也应该说是一个螺旋式的轮回,是上升式的轮回。
          记得马克思主义哲学中有个很经典的命题:对于新事物来说,道路是曲折的,前途是光明的。也许,Web App模型的发展就印证了这个观点吧。也许,服务器端控件、事件驱动模型这些东西一开始就是不适合Web App的,微软走了很多弯路,现在找到了正确的方向。抛弃的痛苦的,我们要抛弃曾经认为多么习惯并且倾注了大量心血的东西,但是,事物被否定后,剩下的的一个蜕变出的新事物,是一个更优秀的东西。
          例如,我们抛弃了用了多年的务器端控件、事件驱动模型……但是我们得到了低耦合的、关注被分离的、符合MVC模型的新的Web模型。要敢于否定,才能获得新生。微软是,我们也是。

    ASP.NET MVC带来的变化
          下面,我们看看ASP.NET MVC到底让我们否定什么?又能得到什么。
          1.服务器端表单控件。
          由于ASP.NET MVC的特质,服务器端的表单控件不再被提倡使用,例如我们的文本框,不再使用asp:TextBox,而是使用传统的input,或直接让Html.TextBox生成。总之,很多服务器端控件被我们废止了。甚至GridView这样曾给我们带来无限快感的老朋友,也不再被提倡使用。但是,并不是说不能用任何服务器端控件,例如,为了实现母版,我们的ContentPlaceHolder还是必须要使用的。
          2.事件驱动模型。
          既然服务器端表单控件已经不提倡使用了,事件驱动模型自然也不被提倡,两者本来就是相辅相成的。在ASP.NET MVC中,当某个按钮被点击,你不要再习惯性想到应该在相应的aspx.cs中有个时间处理方法,你应该想到的是该有某个Controller中有个Action来处理这个事件。实际上,在ASP.NET MVC中,提倡不要在aspx.cs中写任何逻辑代码。甚至应该当他们不存在。
          3.数据绑定
          对于列表式表格数据,你一定习惯了GridView的数据绑定,可是,从你使用ASP.NET MVC开始,这不在被提倡了。你应该自己处理数据的显示。当然,我们也可以期待未来的ASP.NET MVC正式版中会有一个强大的Helper来帮我们做数据显示。

    ASP.NET MVC的收益
          你一定想知道,我们为使用ASP.NET付出了如此惨烈的代价,那么我们能得到什么?从我个人认为,你至少得到了以下东西:
          1.清晰的、关注被分离的代码。
          2.更容易的测试及维护。
          3.更符合MVC的表示层。
          4.你可以向Java程序员自豪的说:我现在也用MVC模式了,而且不用写任何XML!

    总结
          好了,到这里,这个系列就结束了。
          在这个系列开篇里,我曾经说,我做这个系列的唯一目的只是让还徘徊在ASP.NET MVC门外的朋友快速入门,快速上手,快速学会使用这个框架做应用,所以,这个系列一直是在“实用”的指导思想下写的。它不够全面,没有涉及到ASP.NET MVC的方方面面。但是,我相信现在你已经有能力去自己学习那些“方方面面”了。当然,它也不够深入,没有讲解底层的原理。但是,现在的你一定也可以随着实践经验的基类,慢慢去研究它的原理了。
          总之,只要你能通过这个系列的文章,学会使用ASP.NET MVC的基本方法,并已经开始试着做Demo了。那么,我的目的也就达到了。
          最后,附上本系列文章的Demo:MVCDemo.rar

    学习ASP.NET MVC的资料推荐
          1.微软官方ASP.NET MVC QuickStart文档。http://quickstarts.asp.net/previews/mvc/
          2.ScottGu的博客。http://weblogs.asp.net/scottgu/
          3.lulu的ASP.NET MVC入门系列。http://www.cnblogs.com/QLeelulu/archive/2008/10/05/1303997.html

  • jQuery中文入门指南,翻译加实例,jQuery的起点教程

    中文版译者:Keel

    此文以实例为基础一步步说明了jQuery的工作方式。现以中文翻译(添加我的补充说明)如下。如有相关意见或建议请 EMAIL 告知。或者在BLOG中留言。

    英文原版:http://docs.jquery.com/Tutorials:Getting_Started_with_jQuery ,感谢原文作者 Jörn Zaefferer 本文发布已征求原作者同意。

    说明:在本教程发布之后,得到了几个网友的指正,对部分内容作了修正,但在jQuery版本不断更新的情况下,教程中的某些内容已经过时(尤其是1.3以上版本),在忠于原文的基础上,我将这部分内容加以标红的补充说明,希望更多的前端开发者能对此文提出宝贵意见,谢谢! --2009-3-10

    另外我认为在学习过程中,有两个API文档你要打开随时查看:

    如果想了解更多jQuery及插件信息,可访问本站首页.

    以下部分为原文翻译:


    jQuery入门指南教程

    这个指南是一个对jQuery库的说明,要求读者了解HTML(DOM)和CSS的一些常识。它包括了一个简单的Hello World的例子,选择器和事件基础,AJAX、FX的用法,以及如何制作jQuery的插件。 这个指南包括了很多代码,你可以copy它们,并试着修改它们,看看产生的效果。

    内容提要

    1. 安装
    2. Hello jQuery
    3. Find me:使用选择器和事件
    4. Rate me:使用AJAX
    5. Animate me(让我生动起来):使用FX
    6. Sort me(将我有序化):使用tablesorter插件(表格排序)
    7. Plug me:制作您自己的插件
    8. Next steps(下一步)

    安装

    一开始,我们需要一个jQuery的库,最新的下载可以到这里找到。这个指南提供一个基本包含实例的包供下载.

    下载:jQuery Starterkit

    (译者Keel注:一定要下载这个包,光看文章不实践肯定是不行的。)

    下载后解压缩,然后用你最喜欢的文本编辑器打开starterkit.html和custom.js这两个文件。(译者Keel注:这两个就是例子文件,所有的例子都用这两个例子作出,custom.js写jQuery代码,starterkit.html观察效果.建议用editPlus打开)

    现在,我们就已经做好了一切准备来进行这个著名的"Hello world"例子.

    本章的相关链接:

    Hello jQuery

    在做所有事情之前,我们要让jQuery读取和处理文档的DOM,必须尽可能快地在DOM载入后开始执行事件,所以,我们用一个ready事件作为处理HTML文档的开始.看看我们打开的custom.js这个文件,里面已经准备好了:

    $(document).ready(function() {
    	// do stuff when DOM is ready
    });

    放一个简单的alert事件在需要等DOM完成载入,所以我们把任务稍稍变复杂一点:在点击任何一个链接时显示一个alert.

    $(document).ready(function() {
    	$("a").click(function() {
    		alert("Hello world!");
    	});
    });

    这样在你点击页面的一个链接时都会触发这个"Hello world"的提示。

    (译者Keel注:请照此代码修改custom.js并保存,然后用浏览器打开starterkit.html观察效果。)

    让我们看一下这些修改是什么含义。$("a") 是一个jQuery选择器(selector),在这里,它选择所有的a标签(译者Keel注:即<a></a>),$号是 jQuery “类”(jQuery "class")的一个别称,因此$()构造了一个新的jQuery 对象(jQuery object)。函数 click() 是这个jQuery对象的一个方法,它绑定了一个单击事件到所有选中的标签(这里是所有的a标签),并在事件触发时执行了它所提供的alert方法.

    这里有一个拟行相似功能的代码:

    <a href="#" onclick="alert('Hello world')">Link</a>

    不同之处很明显,用jQuery不需要在每个a标签上写onclick事件,所以我们拥有了一个整洁的结构文档(HTML)和一个行为文档(JS),达到了将结构与行为分开的目的,就像我们使用CSS追求的一样.

    下面我们会更多地了解到选择器与事件.

    本章的相关链接:

    Find me:使用选择器和事件

    jQuery提供两种方式来选择html的elements,第一种是用CSS和Xpath选择器联合起来形成一个字符串来传送到jQuery的构造器(如:$("div > ul a"));第二种是用jQuery对象的几个methods(方法)。这两种方式还可以联合起来混合使用。

    为了测试一下这些选择器,我们来试着在我们starterkit.html中选择并修改第一个ordered list.

    一开始,我们需要选择这个list本身,这个list有一个ID叫“orderedlist”,通常的javascript写法是document.getElementById("orderedlist").在jQuery中,我们这样做:

    $(document).ready(function() {
    	$("#orderedlist").addClass("red");
    });

    这里将starterkit中的一个CSS样式red附加到了orderedlist上(译者Keel注:参考测试包中的css目录下的core.css,其中定义了red样式)。因此,在你刷新了starterkit.html后,你将会看到第一个有序列表(ordered list )背景色变成了红色,而第二个有序列表没有变化.

    现在,让我们添加一些新的样式到list的子节点.

    $(document).ready(function() {
    	$("#orderedlist > li").addClass("blue");
    });

    这样,所有orderedlist中的li都附加了样式"blue"。

    现在我们再做个复杂一点的,当把鼠标放在li对象上面和移开时进行样式切换,但只在list的最后一个element上生效。

    $(document).ready(function() {
    	$("#orderedlist li:last").hover(function() {
    		$(this).addClass("green");
    	}, function() {
    		$(this).removeClass("green");
    	});
    });

    还有大量的类似的CSSXPath例子,更多的例子和列表可以在这里找到。(译者Keel注:入门看此文,修行在个人,要想在入门之后懂更多,所以这段话的几个链接迟早是要必看的!不会又要翻译吧...^_^!)

    每一个onXXX事件都有效,如onclick,onchange,onsubmit等,都有jQuery等价表示方法(译者Keel注:jQuery不喜欢onXXX,所以都改成了XXX,去掉了on)其他的一些事件,如ready和hover,也提供了相应的方法。

    你可以在Visual jQuery找到全部的事件列表,在Events栏目下.

    用这些选择器和事件你已经可以做很多的事情了,但这里有一个更强的好东东!

    $(document).ready(function() {
    	$("#orderedlist").find("li").each(function(i) {
    		$(this).html( $(this).html() + " BAM! " + i );
    	});
    });

    find() 让你在已经选择的element中作条件查找,因此 $("#orderedlist).find("li") 就像 $("#orderedlist li")一样。each()方法迭代了所有的li,并可以在此基础上作更多的处理。 大部分的方法,如addClass(), 都可以用它们自己的 each() 。在这个例子中, html()用来获取每个li的html文本, 追加一些文字,并将之设置为li的html文本。(译者Keel注:从这个例子可以看到.html()方法是获取对象的html代码,而.html('xxx')是设置'xxx'为对象的html代码)

    另一个经常碰到的任务是在没有被jQuery覆盖的DOM元素上call一些方法,想像一个在你用AJAX方式成功提交后的reset:

    $(document).ready(function() {
    	// use this to reset a single form
    	$("#reset").click(function() {
    		$("form")[0].reset();
    	});
    });

    (译者Keel注:这里作者将form的id也写成了form,源文件有<form id="form">,这是非常不好的写法,你可以将这个ID改成form1或者testForm,然后用$("#form1")或者$("#testForm")来表示它,再进行测试。)

    上面这个代码选择了所有的"form"元素,并在其中的第一个上call了一个reset()。如果你有一个以上的form,你可以这样做:

    $(document).ready(function() {
    	// use this to reset several forms at once
    	$("#reset").click(function() {
    		$("form").each(function() {
    			this.reset();
    		});
    	});
    });

    (译者Keel注:请注意一定要亲自将这些代码写在custom.js中并在starterkit.html上测试效果才能有所体会!必要时要观察starterkit.html的html代码)

    这样你在点击Reset链接后,就选择了文档中所有的form元素,并对它们都执行了一次reset()。

    还有一个你可能要面对的问题是不希望某些特定的元素被选择。jQuery 提供了filter() 和not() 方法来解决这个问题。 filter()以过滤表达式来减少不符合的被选择项, not()则用来取消所有符合过滤表达式的被选择项. 考虑一个无序的list,你想要选择所有的没有ul子元素的li元素。

    $(document).ready(function() {
    	$("li").not(":has(ul)").css("border", "1px solid black");//原文为$("li").not("[ul]").css("border", "1px solid black");
    });

    这个代码选择了所有的li元素,然后去除了有ul子元素的li元素。刷新浏览器后,所有的li元素都有了一个边框,只有ul子元素的那个li元素例外。

    (译者Keel注:请注意体会方便之极的css()方法,并再次提醒请务必实际测试观察效果,比方说换个CSS样式呢?再加一个CSS样式呢?像这样:$("li").not("[ul]").css("border", "1px solid black").css("color","red");)

    上面代码中的[expression] 语法是从XPath而来,可以在子元素和属性(elements and attributes)上用作过滤器,比如你可能想选择所有的带有name属性的链接:

    $(document).ready(function() {
    	$("a[name]").css("background-color","#eee"); //原文为“$("a[@name]").background("#eee");”在jQuery1.2及以上版本中,@符号应该去除,background方法被css方法取代
    });

    这个代码给所有带有name属性的链接加了一个背景色。(译者Keel注:这个颜色太不明显了,建议写成$("a[name]").css("background-color","#eee");) [注:在jQuery1.2及以上版本中,@符号应该去除,下文中不再说明]

    更常见的情况是以name来选择链接,你可能需要选择一个有特点href属性的链接,这在不同的浏览器下对href的理解可能会不一致,所以我们的部分匹配("*=")的方式来代替完全匹配("="):

    $(document).ready(function() {
    	$("a[href*=/content/gallery]").click(function() {
    		// do something with all links that point somewhere to /content/gallery
    	});
    });

    到现在为止,选择器都用来选择子元素或者是过滤元素。另外还有一种情况是选择上一个或者下一个元素,比如一个FAQ的页面,答案首先会隐藏,当问题点击时,答案显示出来,jQuery代码如下:

    $(document).ready(function() {
    	$('#faq').find('dd').hide().end().find('dt').click(function() {
             var answer = $(this).next();
             if (answer.is(':visible')) {
                 answer.slideUp();
             } else {
                 answer.slideDown();
             }
         });
    });

    这里我们用了一些链式表达法来减少代码量,而且看上去更直观更容易理解。像'#faq' 只选择了一次,利用end()方法,第一次find()方法会结束(undone),所以我们可以接着在后面继续find('dt'),而不需要再写$('#faq').find('dt')。

    在点击事件中的,我们用 $(this).next() 来找到dt下面紧接的一个dd元素,这让我们可以快速地选择在被点击问题下面的答案。

    (译者Keel注:这个例子真是太酷了,FAQ中的答案可以收缩!从利用next()的思路到实现这些效果都有很多地方需要我们消化,注意 if (answer.is(':visible'))用法,注意answer.slideUp();不懂的地方赶紧查我在最开始提到的两个必看API文档)

    除了选择同级别的元素外,你也可以选择父级的元素。可能你想在用户鼠标移到文章某段的某个链接时,它的父级元素--也就是文章的这一段突出显示,试试这个:

    $(document).ready(function() {
    	$("a").hover(function() {
    		$(this).parents("p").addClass("highlight");
    	}, function() {
    		$(this).parents("p").removeClass("highlight");
    	});
    });

    测试效果可以看到,移到文章某段的链接时,它所在的段全用上highlight样式,移走之后又恢复原样。

    (译者Keel注:highlight是core.css中定义的样式,你也可以改变它,注意这里有第二个function()这是hover方法的特点,请在API文档中查阅hover,上面也有例子说明)

    在我们继续之前我们先来看看这一步: jQuery会让代码变得更短从而更容易理解和维护,下面是$(document).ready(callback)的缩写法:

    $(function() {
    	// code to execute when the DOM is ready
    });

    应用到我们的Hello world例子中,可以这样:

    $(function() {
    	$("a").click(function() {
    		alert("Hello world!");
    	});
    });

    现在,我们手上有了这些基础的知识,我们可以更进一步的探索其它方面的东西,就从AJAX开始!

    本章的相关链接:

    Rate me:使用AJAX

    在这一部分我们写了一个小小的AJAX应用,它能够rate一些东西(译Keel注:就是对某些东西投票),就像在youtube.com上面看到的一样。

    首先我们需要一些服务器端代码,这个例子中用到了一个PHP文件,读取rating参数然后返回rating总数和平均数。看一下rate.php代码.

    虽然这些例子也可以不使用AJAX来实现,但显示我们不会那么做,我们用jQuery生成一个DIV容器,ID是"rating".

    $(document).ready(function() {
    	// generate markup
    	var ratingMarkup = ["Please rate: "];
    	for(var i=1; i <= 5; i++) {
    		ratingMarkup[ratingMarkup.length] = "<a href='#'>" + i + "</a> ";
    	}
    	// add markup to container and applier click handlers to anchors
    	$("#rating").append( ratingMarkup.join('') ).find("a").click(function(e) {
    		e.preventDefault();
    		// send requests
    		$.post("rate.php", {rating: $(this).html()}, function(xml) {
    			// format result
    			var result = [
    				"Thanks for rating, current average: ",
    				$("average", xml).text(),
    				", number of votes: ",
    				$("count", xml).text()
    			];
    			// output result
    			$("#rating").html(result.join(''));
    		} );
    	});
    });

    这段代码生成了5个链接,并将它们追加到id为"rating"容器中,当其中一个链接被点击时,该链接标明的分数就会以rating参数形式发送到rate.php,然后,结果将以XML形式会从服务器端传回来,添加到容器中,替代这些链接。

    如果你没有一个安装过PHP的webserver,你可以看看这个在线的例子.

    不使用javascript实现的例子可以访问 softonic.de 点击 "Kurz bewerten!"

    更多的AJAX方法可以从这里 找到,或者看看API文档 下面的AJAX filed under AJAX.

    (译者Keel注:这个在线实例从国内访问还是比较慢的,点击后要等一会儿才能看到结果,可以考虑对它进行修改,比如加上loading,投票后加上再投票的返回链接等。此外,这个例子中还是有很多需要进一步消化的地方,看不懂的地方请参考API文档。)

    一个在使用AJAX载入内容时经常发生的问题是:当载入一个事件句柄到一个HTML文档时,还需要在载入内容上应用这些事件,你不得不在内容加载完成后应用这些事件句柄,为了防止代码重复执行,你可能用到如下一个function:

    // lets use the shortcut
    $(function() {
    	var addClickHandlers = function() {
    		$("a.clickMeToLoadContent").click(function() {
    			$("#target").load(this.href, addClickHandlers);
    		});
    	};
    	addClickHandlers();
    });

    现在,addClickHandlers只在DOM载入完成后执行一次,这是在用户每次点击具有clickMeToLoadContent 这个样式的链接并且内容加载完成后.

    请注意addClickHandlers函数是作为一个局部变量定义的,而不是全局变量(如:function addClickHandlers() {...}),这样做是为了防止与其他的全局变量或者函数相冲突.

    另一个常见的问题是关于回调(callback)的参数。你可以通过一个额外的参数指定回调的方法,简单的办法是将这个回调方法包含在一个其它的function中:

    // get some data
    var foobar = ...;
    // specify handler, it needs data as a paramter
    var handler = function(data) {
      ...
    };
    // add click handler and pass foobar!
    $('a').click( function(event) { handler(foobar); } );
    
    // if you need the context of the original handler, use apply:
    $('a').click( function(event) { handler.apply(this, [foobar]); } );

    用到简单的AJAX后,我们可以认为已经非常之“web2.0”了,但是到现在为止,我们还缺少一些酷炫的效果。下一节将会谈到这些效果.

    本章的相关链接:

    Animate me(让我生动起来):使用FX

    一些动态的效果可以使用 show()  hide()来表现:

    $(document).ready(function() {
    	$("a").toggle(function() {
    		$(".stuff").hide('slow');
    	}, function() {
    		$(".stuff").show('fast');
    	});
    });

    你可以与 animate()联合起来创建一些效果,如一个带渐显的滑动效果:

    $(document).ready(function() {
    	$("a").toggle(function() {
    		$(".stuff").animate({
    			height: 'hide',
    			opacity: 'hide'
    		}, 'slow');
    	}, function() {
    		$(".stuff").animate({
    			height: 'show',
    			opacity: 'show'
    		}, 'slow');
    	});
    });

    很多不错的效果可以访问interface plugin collection. 这个站点提供了很多demos和文档

    这些效果插件是位于jQuery插件列表的前面的,当然也有很多其他的插件,比如我们下一章讲到的表格排序插件。

    本章的相关链接:

    Sort me(将我有序化):使用tablesorter插件(表格排序)

    这个表格排序插件能让我们在客户端按某一列进行排序,引入jQuery和这个插件的js文件,然后告诉插件你想要哪个表格拥有排序功能。

    要测试这个例子,先在starterkit.html中加上像下面这一行的代码:

    <script src="lib/jquery.tablesorter.js" type="text/javascript"></script>

    然后可以这样调用不着:

    $(document).ready(function() {
    	$("#large").tableSorter();
    });

    现在点击表格的第一行head区域,你可以看到排序的效果,再次点击会按倒过来的顺序进行排列。

    这个表格还可以加一些突出显示的效果,我们可以做这样一个隔行背景色(斑马线)效果:

    $(document).ready(function() {
    	$("#large").tableSorter({
    		stripingRowClass: ['odd','even'],	// Class names for striping supplyed as a array.
    		stripRowsOnStartUp: true		// Strip rows on tableSorter init.
    	});
    });

    关于这个插件的更多例子和文档可以在 tablesorter首页找到.

    几乎所有的特件都是这样用的:先include插件的js文件,然后在某些元素上使用插件定义的方法,当然也有一些参数选项是可以配置的

    经常更新的插件列表可以从jQuery官方站 on the jQuery site找到.

    当你更经常地使用jQuery时,你会发现将你自己的代码打包成插件是很有用处的,它能方便地让你的公司或者其他人进行重用.下一章我们将谈到如何构建一个自己的插件.

    本章的相关链接:

    Plug me:制作自己的插件

    写一个自己的jQuery插件是非常容易的,如果你按照下面的原则来做,可以让其他人也容易地结合使用你的插件.

    1. 为你的插件取一个名字,在这个例子里面我们叫它"foobar".
    2. 创建一个像这样的文件:jquery.[yourpluginname].js,比如我们创建一个jquery.foobar.js
    3. 创建一个或更多的插件方法,使用继承jQuery对象的方式,如:
      jQuery.fn.foobar = function() {
      	// do something
      };
    4. 可选的:创建一个用于帮助说明的函数,如:
      jQuery.fooBar = {
      	height: 5,
      	calculateBar = function() { ... },
      	checkDependencies = function() { ... }
      };

      你现在可以在你的插件中使用这些帮助函数了:

      jQuery.fn.foobar = function() {
      	// do something
      	jQuery.foobar.checkDependencies(value);
      	// do something else
      };
    5. 可选的l:创建一个默认的初始参数配置,这些配置也可以由用户自行设定,如:
      jQuery.fn.foobar = function(options) {
      	var settings = {
      		value: 5,
      		name: "pete",
      		bar: 655
      	};
      	if(options) {
      		jQuery.extend(settings, options);
      	}
      };

      现在可以无需做任何配置地使用插件了,默认的参数在此时生效:

      $("...").foobar();

      或者加入这些参数定义:

      $("...").foobar({
      	value: 123,
      	bar: 9
      });

    如果你release你的插件, 你还应该提供一些例子和文档,大部分的插件都具备这些良好的参考文档.

    现在你应该有了写一个插件的基础,让我们试着用这些知识写一个自己的插件.

    很多人试着控制所有的radio或者checkbox是否被选中,比如:

    $("input[type='checkbox']").each(function() {
    	this.checked = true;
    	// or, to uncheck
    	this.checked = false;
    	// or, to toggle
    	this.checked = !this.checked;
    });
    注:在jQuery1.2及以上版本中,选择所有checkbox应该使用 input:checkbox , 因此以上代码第一行可写为: 
    $('input:checkbox').each(function() {

    无论何时候,当你的代码出现each时,你应该重写上面的代码来构造一个插件,很直接地:

    $.fn.check = function() {
    	return this.each(function() {
    		this.checked = true;
    	});
    };

    这个插件现在可以这样用:

    $('input:checkbox').check();
    
    注:在jQuery1.2及以上版本中,选择所有checkbox应该使用 input:checkbox 原文为:$("input[type='checkbox']").check();

    现在你应该还可以写出uncheck()和toggleCheck()了.但是先停一下,让我们的插件接收一些参数.

    $.fn.check = function(mode) {
    	var mode = mode || 'on'; // if mode is undefined, use 'on' as default
    	return this.each(function() {
    		switch(mode) {
    		case 'on':
    			this.checked = true;
    			break;
    		case 'off':
    			this.checked = false;
    			break;
    		case 'toggle':
    			this.checked = !this.checked;
    			break;
    		}
    	});
    };

    这里我们设置了默认的参数,所以将"on"参数省略也是可以的,当然也可以加上"on","off", 或 "toggle",如:

    $("input[type='checkbox']").check();
    $("input[type='checkbox']").check('on');
    $("input[type='checkbox']").check('off');
    $("input[type='checkbox']").check('toggle');

    如果有多于一个的参数设置会稍稍有点复杂,在使用时如果只想设置第二个参数,则要在第一个参数位置写入null.

    从上一章的tablesorter插件用法我们可以看到,既可以省略所有参数来使用或者通过一个 key/value 对来重新设置每个参数.

    作为一个练习,你可以试着将 第四章 的功能重写为一个插件.这个插件的骨架应该是像这样的:

    $.fn.rateMe = function(options) {
    	var container = this; // instead of selecting a static container with $("#rating"), we now use the jQuery context
    
    	var settings = {
    		url: "rate.php"
    		// put more defaults here
    		// remember to put a comma (",") after each pair, but not after the last one!
    	};
    
    	if(options) { // check if options are present before extending the settings
    		$.extend(settings, options);
    	}
    
    	// ...
    	// rest of the code
    	// ...
    
    	return this; // if possible, return "this" to not break the chain
    });

    Next steps(下一步)

    如果你想做更好的javascript开发,建议你使用一个叫 FireBug的firefox插件. 它提供了断点调试(比alert强多了)、观察DOM变化等很多漂亮的功能

    如果你还有未解决的问题,或者新的想法与建议,你可以使用jQuery的邮件列表 jQuery mailing list.

    关于这个指南的任何事情,你可以写mail给作者或者发表评论在他的日志:blog.

    关于这个指南的翻译任何事情,你可以写mail给我.或者在 BLOG中留言.

    还有什么...

    大大感谢John Resig创造了这么好的library! 感谢jQuery community 为John提供了如此多的咖啡和其他的一切!

    © 2006, Jörn Zaefferer - last update: 2006-09-12

    中文版翻译:Keel 上次更新:2006-12-13 -- 最后更新: 2009-3-10 访问本站首页

  • ASP.NET发布网站解决方案deploy

    发布网站的时候有三个选项,很多人都不知道到底是怎么用的,简单说说:

    对于想了解发布网站那些选项的人来说这个文章是不错的,当然这个文章不是我写的。 
      
    第一个选项指定发布后是不是可以修改aspx文件,如果勾选,则发布后的网站行为基本与ASP.NET 1.1一致,只要没有增删修改控件,可以直接在服务器上修改aspx文件不用重新发布网站。 

    第二个选项指定是不是将每个aspx文件都编译成一个DLL文件,这样,就可以在修改了哪个aspx网页(包括aspx和cs文件),只需要更新一个DLL文件就行了,不用整站全部更新。

    1.允许更新此预编辑站点

    选中这一项后,编译出来的包括aspx文件和dll,与2003下一样。
    不选中这一项,编译出来的aspx中没有界面信息,只有一句静态文本,就是不允许发布后修改页面

    为了不让订阅网站的用户在第一次打开页面时感受到明显的延迟,可以使用"完全预编译(full pre-compilation)"方式.
    如果是想此编译方式具有最大的安全性,应去掉"允许更新此预编译站点(Allow this precompiled site to be updateable". 这样代码文件(code,即cs文件)和内容文件(content file,即aspx)都会预编译

    2,使用固定命名和单页程序集

    会案照画面的类名编译出很多名字固定的dll。

    3.对预编辑程序集启用强命名
    在多数情况下,完全预编译方式正是所需要的方式,但是有时候因为内容文件变化不大,你可能希望在网站发布后,不用每次把所有的代码与内容文件全部编译,也许内容文件就不用再次编译,只需编译代码文件即可,这种情况下,就选中"允许更新此预编译站点",这种方式称为"只预编译代码文件(pre- compilation of code only)"方式.此方式与"完全预编译方式"相比较,只有一点区别,即内容文件仍是原始版本,而不是存根 (stub)版本,其它效果相同.在内容文件发布后也可以对其进行编辑,其变动在以后的请求到来时起作用,对于访问此站的用户来说是透明的.


    附:
    强命名(strong names) 如果一个assembly需要共享,则其必须使用强命名.一个强命名唯一标识了一个assembly.有四部分组成: 1,assembly名称(不包含文件扩展名);2,版本; 3,culture; 4,密钥对(即一个公钥和一个私钥),保存在key文件中,所以说 key文件就是同时包含了公钥和私钥的文件,在使用强命名时当然是需要此key文件的.创建key文件用命令: sn -k KeyPair.snk

    延迟签名:显然,每个公司的私钥都必须非常安全地保存,然后这就带来一个进退两难的问题:在开发和测试一个共享的assembly时,需要使用强命名方式, 而创建强命名时肯定需要访问私钥的,但是又不能把公司的私钥提供给参与项目开发的且需要创建强命名的所有开发人员,怎么办? 这时就要用"延迟签名"技术.此种方式下,在创建强命名assembly时,只需要提供公钥,因为公钥提供给所有的人是允许的,也是安全的,利用公钥,开发人员可以进行程序的开发和测试工作,直到准备进行最终的build时,才同时使用公钥和私钥. 延迟签名需要从key文件中把公钥解出来,形成独立的公钥文件,通过使用命令: sn -p KeyPair.snk PublicKey.snk即可.这样,publicKey.snk只包含公钥,就可以用了.

    出现问题

    visual studio2005将网站开发和网站发布的目录分开,本身是个很好的设定
    但每一次发布网站,网站dll文件都会生成随机的名字,页面继承的类都会继承随机的名字
    这就造成了,哪怕是一点很小的修改,在发布网站之后,也必须重新上传所有的页面
    如果采用“固定命名和单页程序集”的方式,又会生成太多dll文件,看着就心烦

    解决方法

    安装: 
    http://download.microsoft.com/download/9/4/9/9496adc4-574e-4043-bb70-bc841e27f13c/WebDeploymentSetup.msi

    右键在当前解决方案里生成一个:Add Web Deployment Project 项目, 在该项目里就可以设置生成DLL的命名方式了,以后生成该项目就可以生成相应的网站!原有的发布网站的功能可以退休了!


    参考微软原文

    http://msdn2.microsoft.com/en-us/asp.net/aa336619.aspx

    使用WebDeployment Project改善VS2005发布网站问题 (一) 基础

    VS2005发布网站时不会像VS2003一样生成规则的DLL文件、而生成的DLL文件名含有随机数且不能一个项目生成一个DLL文件、让人有一些遗憾、为了做到像vs2003一样,微软发布了WebDeployment Project插件可解决此问题:
    下载地址

    1、下载后安装、右键选择vs2005中的项目、会出现一个选项"Add Web Deployment Project"

    2、选择添加一个WebDeployment Project(输入程序集名称和发布地址)

    3、此时解决方案中会多出一个项目(http://www.cnblogs.com/chy710)

    4、右键选择该项目设置相应属性



    5、右键选择该项目选择“生成”、此时会发布网站到指定的目录、生成DLL文件同vs2003一样、更新时只需上传DLL文件

    我运行发现这个错,

    错误 1 “aspnet_merge.exe”已退出,代码为 1。 C:"Program Files"MSBuild"Microsoft"WebDeployment"v8.0"Microsoft.WebDeployment.targets 574 9 DaishuSite

    后来网上查了下。是类存在同名的缘故。

    将VS 2005的工具-选项-项目和解决方案-生成并运行,设置“MSBuild 项目生成输出详细信息”,选择“详细”。再次生成Web Deployment Projects项目,“输出”框内的信息就变得非常丰富了;这样就可以查看哪里出错了

    Posted Jun 07 2010, 10:25 AM by slash with no comments
    Filed under: ,
  • ASP.NET建立静态缓存页面-staticpage

    方法A:使用 HttpModule 技术拦截页面访问,导向静态缓存页
    步骤:
    1、创建一个新的HtppModule,拦截对aspx类型页面的访问,判断是否有静态缓存页

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    
     
    using System; 
    using System.IO; 
    using System.Data; 
    using System.Configuration; 
    using System.Web; 
    using System.Web.Security; 
    using System.Web.UI; 
    using System.Web.UI.WebControls; 
    using System.Web.UI.WebControls.WebParts; 
    using System.Web.UI.HtmlControls; 
     
    /// <summary> 
    /// StaticFileCacheModule 的摘要说明 
    /// </summary> 
    public class StaticFileCacheModule:IHttpModule 
    { 
        public void Init(HttpApplication context) 
        { 
            context.BeginRequest += new EventHandler(context_BeginRequest); 
        } 
        void context_BeginRequest(object sender, EventArgs e) 
        { 
            HttpContext context = ((HttpApplication)sender).Context; 
            //判断是否需要处理 
            if (context.Request.AppRelativeCurrentExecutionFilePath.ToLower().EndsWith(".aspx")) 
            { 
                string fileUrl = "~/CacheFile/"+ GetFileName(context); 
                if (File.Exists(context.Server.MapPath(fileUrl))) 
                { 
                    //如果静态缓存文件存在,直接返回缓存文件 
                    context.RewritePath(fileUrl, false); 
                } 
            } 
        } 
        public static string GetFileName(HttpContext context) 
        { 
            //我们的缓存文件名由页面文件名加上查询字符串组成 
            return context.Request.AppRelativeCurrentExecutionFilePath 
                    .ToLower() 
                    .Replace(".aspx", "") 
                    .Replace("~/","") 
                    .Replace("/","_"+ 
                   context.Request.Url.Query 
                    .Replace("?","_") 
                    .Replace("&","_")+".html"; 
             
        } 
        public void Dispose() {} 
    } 

    此 HttpMudole 类文件放入 App_Code 目录
    2、创建一个Page子类,重写Render方法,在其中将页面生成的最终结果保存在指定目录下
    需要建立缓存的页面,继承此类即可。
     

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    
     
    using System; 
    using System.IO; 
    using System.Data; 
    using System.Configuration; 
    using System.Web; 
    using System.Web.Security; 
    using System.Web.UI; 
    using System.Web.UI.WebControls; 
    using System.Web.UI.WebControls.WebParts; 
     using System.Web.UI.HtmlControls; 
     /// <summary> 
     /// StaticFileCachePageBase 的摘要说明 
     /// </summary> 
     public class StaticFileCachePageBase : System.Web.UI.Page  
     { 
         protected string GetFileName() 
         { 
             //我们的缓存文件名由页面文件名加上查询字符串组成 
             return Request.AppRelativeCurrentExecutionFilePath 
                     .ToLower() 
                     .Replace(".aspx", "") 
                     .Replace("~/", "") 
                     .Replace("/", "_") + 
                    Request.Url.Query 
                     .Replace("?", "_") 
                     .Replace("&", "_") + ".html"; 
         } 
         protected override void Render(HtmlTextWriter writer) 
         { 
             StringWriter sw = new StringWriter(); 
             HtmlTextWriter htmlw = new HtmlTextWriter(sw); 
             //调用Render方法,把页面内容输出到StringWriter中 
             base.Render(htmlw); 
             htmlw.Flush(); 
             htmlw.Close(); 
             //获得页面内容 
             string pageContent = sw.ToString(); 
             string fullpath = Server.MapPath("~/CacheFile/")+ GetFileName(); 
             string path = Path.GetDirectoryName(fullpath); 
             if (!Directory.Exists(path)) 
             { 
                 Directory.CreateDirectory(path); 
             } 
             //把页面内容保存到静态文件中 
             using (StreamWriter stringWriter = File.AppendText(fullpath)) 
             { 
                 stringWriter.Write(pageContent); 
             } 
             //将页面内容输出到浏览器 
             Response.Write(pageContent); 
         } 
     } 

    方法B、不拦截所有的aspx页面,由具有静态缓存功能的网页自己判断是否该加载静态页
    需要建立静态缓存的页面,将默认的继承 Page 修改为继承此 StaticFileCachePageBase 类即可
     (可以用在只有少数页面需要缓冲的情况下)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    
     
    using System; 
    using System.IO; 
    using System.Data; 
    using System.Configuration; 
    using System.Web; 
    using System.Web.Security; 
    using System.Web.UI; 
    using System.Web.UI.WebControls; 
    using System.Web.UI.WebControls.WebParts; 
     using System.Web.UI.HtmlControls; 
     /// <summary> 
     /// StaticFileCachePageBase 的摘要说明 
     /// </summary> 
     public class StaticFileCachePageBase : System.Web.UI.Page  
     { 
         protected override void OnPreInit(EventArgs e) 
         { 
             string fileUrl = "~/CacheFile/" + GetFileName(); 
             if (File.Exists(Server.MapPath(fileUrl))) 
             { 
                 //如果静态缓存文件存在,直接返回缓存文件 
                 Server.Transfer(fileUrl); 
             } 
             base.OnPreInit(e); 
         } 
         protected string GetFileName() 
         { 
             //我们的缓存文件名由页面文件名加上查询字符串组成 
             return Request.AppRelativeCurrentExecutionFilePath 
                     .ToLower() 
                     .Replace(".aspx", "") 
                     .Replace("~/", "") 
                     .Replace("/", "_") + 
                    Request.Url.Query 
                     .Replace("?", "_") 
                     .Replace("&", "_") + ".html"; 
         } 
         protected override void Render(HtmlTextWriter writer) 
         { 
             StringWriter sw = new StringWriter(); 
             HtmlTextWriter htmlw = new HtmlTextWriter(sw); 
             //调用Render方法,把页面内容输出到StringWriter中 
             base.Render(htmlw); 
             htmlw.Flush(); 
             htmlw.Close(); 
             //获得页面内容 
             string pageContent = sw.ToString(); 
             string fullpath = Server.MapPath("~/CacheFile/")+ GetFileName(); 
             string path = Path.GetDirectoryName(fullpath); 
             if (!Directory.Exists(path)) 
             { 
                 Directory.CreateDirectory(path); 
             } 
             //把页面内容保存到静态文件中 
             using (StreamWriter stringWriter = File.AppendText(fullpath)) 
             { 
                 stringWriter.Write(pageContent); 
             } 
             //将页面内容输出到浏览器 
             Response.Write(pageContent); 
         } 
     } 

    Posted Jun 04 2010, 11:51 AM by slash with no comments
    Filed under:
  • JQuery 简介

    一、简介
    1.1、概述
    随着WEB2.0及ajax思想在互联网上的快速发展传播,陆续出现了一些优秀的Js框架,其中比较著名的有Prototype

    、YUI、jQuery、mootools、Bindows以及国内的JSVM框架等,通过将这些JS框架应用到我们的项目中能够使程序员

    从设计和书写繁杂的JS应用中解脱出来,将关注点转向功能需求而非实现细节上,从而提高项目的开发速度。
    jQuery是继prototype之后的又一个优秀的Javascript框架。它是由 John Resig 于 2006 年初创建的,它有助于简化

    JavaScript™ 以及Ajax 编程。有人使用这样的一比喻来比较prototype和jQuery:prototype就像Java,而jQuery就像ruby.

    它是一个简洁快速灵活的JavaScript框架,它能让你在你的网页上简单的操作文档、处理事件、实现特效并为Web

    页面添加Ajax交互。
    它具有如下一些特点:
    代码简练、语义易懂、学习快速、文档丰富。
    jQuery是一个轻量级的脚本,其代码非常小巧,最新版的JavaScript包只有20K左右。
    jQuery支持CSS1-CSS3,以及基本的xPath。
    jQuery是跨浏览器的,它支持的浏览器包括IE 6.0+, FF 1.5+, Safari 2.0+, Opera 9.0+。
    可以很容易的为jQuery扩展其他功能。
    能将JS代码和HTML代码完全分离,便于代码和维护和修改。
    插件丰富,除了jQuery本身带有的一些特效外,可以通过插件实现更多功能,如表单验证、tab导航、拖放效果、

    表格排序、DataGrid,树形菜单、图像特效以及ajax上传等。
    jQuery的设计会改变你写JavaScript代码的方式,降低你学习使用JS操作网页的复杂度,提高网页JS开发效率,无

    论对于js初学者还是资深专家,jQuery都将是您的首选。
    jQuery适合于设计师、开发者以及那些还好者,同样适合用于商业开发,可以说jQuery适合任何JavaScript应用的

    地方,可用于不同的Web应用程序中。
    官方站点:http://jquery.com/  中文站点:http://jquery.org.cn/
    1.2、目的
    通过学习本文档,能够对jQuery有一个简单的认识了解,清楚JQuery与其他JS框架的不同,掌握jQuery的常用语法

    、使用技巧及注意事项。
    二、使用方法
    在需要使用JQuery的页面中引入JQuery的js文件即可。
    例如:<script type="text/javascript" src="js/jquery.js"></script>
    引入之后便可在页面的任意地方使用jQuery提供的语法。
    三、学习教程及参考资料
    请参照《jQuery中文API手册》和http://jquery.org.cn/visual/cn/index.xml
    推荐两篇不错的jquery教程:《jQuery的起点教程》和《使用 jQuery 简化 Ajax 开发》(说明:以上文档都放在了【

    附件】中)
    四、语法总结和注意事项
     
    1、关于页面元素的引用
     
    通过jquery的$()引用元素包括通过id、class、元素名以及元素的层级关系及dom或者xpath条件等方法,且返回的对

    象为jquery对象(集合对象),不能直接调用dom定义的方法。
     
    2、jQuery对象与dom对象的转换
     
    只有jquery对象才能使用jquery定义的方法。注意dom对象和jquery对象是有区别的,调用方法时要注意操作的

    是dom对象还是jquery对象。
    普通的dom对象一般可以通过$()转换成jquery对象。
    如:$(document.getElementById("msg"))则为jquery对象,可以使用jquery的方法。
    由于jquery对象本身是一个集合。所以如果jquery对象要转换为dom对象则必须取出其中的某一项,一般可通过索

    引取出。
    如:$("#msg")[0],$("div").eq(1)[0],$("div").get()[1],$("td")[5]这些都是dom对象,可以使用dom中的方法,但不能

    再使用Jquery的方法。
    以下几种写法都是正确的:
     
    $("#msg").html();
    $("#msg")[0].innerHTML;
    $("#msg").eq(0)[0].innerHTML;
    $("#msg").get(0).innerHTML;
     
    3、如何获取jQuery集合的某一项
     
    对于获取的元素集合,获取其中的某一项(通过索引指定)可以使用eq或get(n)方法或者索引号获取,要注

    意,eq返回的是jquery对象,而get(n)和索引返回的是dom元素对象。对于jquery对象只能使用jquery的方法,而dom

    对象只能使用dom的方法,如要获取第三个<div>元素的内容。有如下两种方法:
     
    $("div").eq(2).html();              //调用jquery对象的方法
    $("div").get(2).innerHTML;       //调用dom的方法属性
     
    4、同一函数实现set和get
     
    Jquery中的很多方法都是如此,主要包括如下几个:
     
     
    $("#msg").html();              //返回id为msg的元素节点的html内容。
    $("#msg").html("<b>new content</b>");      
    //将“<b>new content</b>” 作为html串写入id为msg的元素节点内容中,页面显示粗体的new content
     
    $("#msg").text();              //返回id为msg的元素节点的文本内容。
    $("#msg").text("<b>new content</b>");      
    //将“<b>new content</b>” 作为普通文本串写入id为msg的元素节点内容中,页面显示<b>new content</b>
     
    $("#msg").height();              //返回id为msg的元素的高度
    $("#msg").height("300");       //将id为msg的元素的高度设为300
    $("#msg").width();              //返回id为msg的元素的宽度
    $("#msg").width("300");       //将id为msg的元素的宽度设为300
     
    $("input").val(");       //返回表单输入框的value值
    $("input").val("test");       //将表单输入框的value值设为test
     
    $("#msg").click();       //触发id为msg的元素的单击事件
    $("#msg").click(fn);       //为id为msg的元素单击事件添加函数
     
     
    同样blur,focus,select,submit事件都可以有这两种调用方法
    5、集合处理功能
    对于jquery返回的集合内容无需我们自己循环遍历并对每个对象分别做处理,jquery已经为我们提供的很方便的方

    法进行集合的处理。
    包括两种形式:
    6、扩展我们需要的功能
    $.extend({
           min: function(a, b){return a < b?a:b; },
           max: function(a, b){return a > b?a:b; }
    });       //为jquery扩展了min,max两个方法
    使用扩展的方法(通过“$.方法名”调用):
    alert("a=10,b=20,max="+$.max(10,20)+",min="+$.min(10,20));
    7、支持方法的连写
    所谓连写,即可以对一个jquery对象连续调用各种不同的方法。
    例如:
    $("p").click(function(){alert($(this).html())})
    .mouseover(function(){alert('mouse over event')})
    .each(function(i){this.style.color=['#f00','#0f0','#00f']Idea});
    8、操作元素的样式
    主要包括以下几种方式:
    $("#msg").css("background");              //返回元素的背景颜色
    $("#msg").css("background","#ccc")       //设定元素背景为灰色
    $("#msg").height(300); $("#msg").width("200");       //设定宽高
    $("#msg").css({ color: "red", background: "blue" });//以名值对的形式设定样式
    $("#msg").addClass("select");       //为元素增加名称为select的class
    $("#msg").removeClass("select");       //删除元素名称为select的class
    $("#msg").toggleClass("select");       //如果存在(不存在)就删除(添加)名称为select的class
    9、完善的事件处理功能
    Jquery已经为我们提供了各种事件处理方法,我们无需在html元素上直接写事件,而可以直接为通过jquery获取的

    对象添加事件。
    如:
    $("#msg").click(function(){alert("good")})    //为元素添加了单击事件
    $("p").click(function(i){this.style.color=['#f00','#0f0','#00f']Idea})   //为三个不同的p元素单击事件分别设定不同的处理
    jQuery中几个自定义的事件:
    (1)hover(fn1,fn2):一个模仿悬停事件(鼠标移动到一个对象上面及移出这个对象)的方法。当鼠标移动到一

    个匹配的元素上面时,会触发指定的第一个函数。当鼠标移出这个元素时,会触发指定的第二个函数。
    //当鼠标放在表格的某行上时将class置为over,离开时置为out。
    $("tr").hover(function(){
    $(this).addClass("over");
    },
           function(){
           $(this).addClass("out");
    });
    (2)ready(fn):当DOM载入就绪可以查询及操纵时绑定一个要执行的函数。
    $(document).ready(function(){alert("Load Success")})
    //页面加载完毕提示“Load Success”,不同于onload事件,onload需要页面内容加载完毕(图片等),而ready只要页

    面html代码下载完毕即触发。与$(fn)等价
    (3)toggle(evenFn,oddFn): 每次点击时切换要调用的函数。如果点击了一个匹配的元素,则触发指定的第一个函

    数,当再次点击同一元素时,则触发指定的第二个函数。随后的每次点击都重复对这两个函数的轮番调用。
           //每次点击时轮换添加和删除名为selected的class。
           $("p").toggle(function(){
                  $(this).addClass("selected");  
           },function(){
                  $(this).removeClass("selected");
           });
    (4)trigger(eventtype): 在每一个匹配的元素上触发某类事件。
    例如:
           $("p").trigger("click");       //触发所有p元素的click事件
    (5)bind(eventtype,fn),unbind(eventtype): 事件的绑定与反绑定
    从每一个匹配的元素中(添加)删除绑定的事件。
    例如:
    $("p").bind("click", function(){alert($(this).text());});
    //为每个p元素添加单击事件
    $("p").unbind();       //删除所有p元素上的所有事件
    $("p").unbind("click")       //删除所有p元素上的单击事件
    10、几个实用特效功能
    其中toggle()和slidetoggle()方法提供了状态切换功能。
    如toggle()方法包括了hide()和show()方法。
    slideToggle()方法包括了slideDown()和slideUp方法。
    11、几个有用的jQuery方法
    $.browser.浏览器类型:检测浏览器类型。有效参数:safari, opera, msie, mozilla。如检测是

    否ie:$.browser.isie,是ie浏览器则返回true。
    $.each(obj, fn):通用的迭代函数。可用于近似地迭代对象和数组(代替循环)。

    $.each( [0,1,2], function(i, n){ alert( "Item #" + i + ": " + n ); });
    等价于:
    var tempArr=[0,1,2];
    for(var i=0;i<tempArr.length;i++){
           alert("Item #"+i+": "+tempArrIdea);
    }
    也可以处理json数据,如
    $.each( { name: "John", lang: "JS" }, function(i, n){ alert( "Name: " + i + ", Value: " + n ); });
    结果为:
    Name:name, Value:John
    Name:lang, Value:JS
    $.extend(target,prop1,propN):用一个或多个其他对象来扩展一个对象,返回这个被扩展的对象。这是jquery实现的

    继承方式。
    如:
    $.extend(settings, options);      
    //合并settings和options,并将合并结果返回settings中,相当于options继承setting并将继承结果保存在setting中。
    var settings = $.extend({}, defaults, options);
    //合并defaults和options,并将合并结果返回到setting中而不覆盖default内容。
    可以有多个参数(合并多项并返回)
    $.map(array, fn):数组映射。把一个数组中的项目(处理转换后)保存到到另一个新数组中,并返回生成的新数组


    如:
    var tempArr=$.map( [0,1,2], function(i){ return i + 4; });
    tempArr内容为:[4,5,6]
    var tempArr=$.map( [0,1,2], function(i){ return i > 0 ? i + 1 : null; });
    tempArr内容为:[2,3]
    $.merge(arr1,arr2):合并两个数组并删除其中重复的项目。
    如:
    $.merge( [0,1,2], [2,3,4] )       //返回[0,1,2,3,4]
    $.trim(str):删除字符串两端的空白字符。
    如:
    $.trim("  hello, how are you?   ");    //返回"hello,how are you? "
    12、解决自定义方法或其他类库与jQuery的冲突
    很多时候我们自己定义了$(id)方法来获取一个元素,或者其他的一些js类库如prototype也都定义了$方法,如果同

    时把这些内容放在一起就会引起变量方法定义冲突,Jquery对此专门提供了方法用于解决此问题。
    使用jquery中的jQuery.noConflict();方法即可把变量$的控制权让渡给第一个实现它的那个库或之前自定义的$方法

    。之后应用Jquery的时候只要将所有的$换成jQuery即可,如原来引用对象方法$("#msg")改为jQuery("#msg")。
    如:
    jQuery.noConflict();
    // 开始使用jQuery
    jQuery("div   p").hide();
    // 使用其他库的 $()
    $("content").style.display = 'none';
      
    本文附件
    $("p").each(function(i){this.style.color=['#f00','#0f0','#00f']Idea})      
    //为索引分别为0,1,2的p元素分别设定不同的字体颜色。
    $("tr").each(function(i){this.style.backgroundColor=['#ccc','#fff'][i%2]})      
    //实现表格的隔行换色效果
    $("p").click(function(){alert($(this).html())})             
    //为每个p元素增加了click事件,单击某个p元素则弹出其内容

    Posted Jun 04 2010, 11:46 AM by slash with no comments
    Filed under:
  • ORACLE 全角数字转半角数字Convert

    数据库表 test 字段 id  name age

    全角数字:123456

    半角数字:123456

     

    length和lengthb的区别:

    length(123456)             6

    lengthb(123456)    12

     

    to_single_byte函数用法:

    to_single_byte(123456)   123456

     

    查找所有全角的数字:

    select age from test where lengthB(age) >6

     

    替换全角的为半角的:

    update test  t1 set t1.age = (select to_single_byte(t2.age) from test  t2 where t1.id = t2.id)

     

    Posted Jun 01 2010, 04:23 PM by slash with no comments
    Filed under:
  • URLRewrite在IIS6和IIS7中的使用配置

    之前使用URLRewrite在IIS6中一切正常,但是在IIS7却不能正常使用,提示“无法找到资源”,这时需要在web.config中system.webServer节点中的handlers节点进行配置,如下:

    IIS6中的配置:


     1   <configuration>
     2 
     3       <configSections>
     4           <section name="RewriterConfig" type="URLRewriter.Config.RewriterConfigSerializerSectionHandler, URLRewriter"/>
     5       </configSections>
     6       <RewriterConfig>
     7           <Rules>
     8               <RewriterRule>
     9                   <LookFor>~/Index\.aspx</LookFor>
    10                  <SendTo>~/Content/Index.aspx</SendTo>
    11              </RewriterRule>
    12          </Rules>
    13      </RewriterConfig>
    14  
    15      <system.web>
    16          <httpHandlers>
    17              <add verb="*" path="*.aspx" type="URLRewriter.RewriterFactoryHandler, URLRewriter"/>
    18          </httpHandlers>
    19      </system.web>
    20 
    21   </configuration>

      如果在IIS7中使用,需在web.config中加入:


     1 <configuration>
     2 
     3          <system.webServer>
     4          <validation validateIntegratedModeConfiguration="false"/>
     5          <modules>
     6              <add type="URLRewriter.ModuleRewriter, URLRewriter" name="ModuleRewriter" />
     7          </modules>
     8          <system.webServer>
     9 
    10 </configuration>

    加入以上代码后,URLRewrite就可以同时在IIS6和IIS7中正常使用了。

  • asp.net UrlRewrite 技术的实现

    首先在以下地址:

    download.microsoft.com/download/0/4/6/0463611e-a3f9-490d-a08c-877a83b797cf/MSDNURLRewriting.msi

     下载 MS 的 URLRewriter.dll,放到你的web程序的bin下。

    注:以上地址下载的是微软的一个完整的 URLrewrite 技术示例。下载后是一个 MSDNURLRewriting.msi 文件,安装在本地机上,安装后,在安装目录内有三个文件夹,分别是:ActionlessForm ,RewriterTester,URLRewriter 这三个目录。 其中 URLRewriter 文件夹便是一个完整的 URLRewrite 的项目示例。此项目中的 BIN 目录中有两 个 dll,分别为
    ActionlessForm.dll  URLRewriter.dll ,这两个 dll 就是项目 ActionlessForm  和 URLRewriter 产生的 dll 类库,是示例项目 RewriterTester 实现 URLRwrite 技术所用到的类库文件。

    如何把此技术应用到你自己的项目中去,其实很简单:

    首先,把 ActionlessForm.dll  URLRewriter.dll 两个 dll 文件放到你自己项目中的 bin 目录下。
    然后,修改你的 web.config 文件,完整的 web.config 文件如下:

    (只需在普通的 web.config 文件中填加两个地方)
    ----------------------------------------------
    1、

    在  </configSections> 标签上面填加:
    <section name="RewriterConfig" type="URLRewriter.Config.RewriterConfigSerializerSectionHandler, URLRewriter" />

    2、

      <httpModules>
          <add type="URLRewriter.ModuleRewriter, URLRewriter" name="ModuleRewriter" />
      </httpModules>


    --------------------------------------------

    <?xml version="1.0" encoding="utf-8"?>
    <!-- 
        注意: 除了手动编辑此文件以外,您还可以使用 
        Web 管理工具来配置应用程序的设置。可以使用 Visual Studio 中的
         “网站”
    ->“Asp.Net 配置”选项。
        设置和注释的完整列表在 
        machine.config.comments 中,该文件通常位于 
        \Windows\Microsoft.Net\Framework\v2.x\Config 中
    -->
    <configuration>

      
    <configSections>
        
    <!-- The <configSections> element must contain a <section> tag for the <RewriterConfig> section element.
                The type of the section handler 
    is RewriterConfigSerializerSectionHandler, which is responsible for
                deserializing the 
    <RewriterConfig> section element into a RewriterConfig instance -->
        
    <section name="RewriterConfig" type="URLRewriter.Config.RewriterConfigSerializerSectionHandler, URLRewriter" />
      
    </configSections>

      
    <RewriterConfig>
        
    <Rules>
          
    <!-- Rules for Blog Content Displayer -->
          
    <RewriterRule>
            
    <LookFor>~/(\d{4})/(\d{2})/(\d{2})\.aspx</LookFor>
            
    <SendTo>~/ShowBlogContent.aspx?year=$1&amp;month=$2&amp;day=$3</SendTo>
          
    </RewriterRule>
          
    <RewriterRule>
            
    <LookFor>~/(\d{4})/(\d{2})/Default\.aspx</LookFor>
            
    <SendTo><![CDATA[~/ShowBlogContent.aspx?year=$1&month=$2]]></SendTo>
          
    </RewriterRule>
          
    <RewriterRule>
            
    <LookFor>~/(\d{4})/Default\.aspx</LookFor>
            
    <SendTo>~/ShowBlogContent.aspx?year=$1</SendTo>
          
    </RewriterRule>


          
    <!-- Rules for Product Lister -->
          
    <RewriterRule>
            
    <LookFor>~/Products/Default\.aspx</LookFor>
            
    <SendTo>~/ListCategories.aspx</SendTo>
          
    </RewriterRule>
          
    <RewriterRule>
            
    <LookFor>~/Products/Beverages\.aspx</LookFor>
            
    <SendTo>~/ListProductsByCategory.aspx?CategoryID=1</SendTo>
          
    </RewriterRule>
          
    <RewriterRule>
            
    <LookFor>~/Products/Condiments\.aspx</LookFor>
            
    <SendTo>~/ListProductsByCategory.aspx?CategoryID=2</SendTo>
          
    </RewriterRule>
          
    <RewriterRule>
            
    <LookFor>~/Products/Confections\.aspx</LookFor>
            
    <SendTo>~/ListProductsByCategory.aspx?CategoryID=3</SendTo>
          
    </RewriterRule>
          
    <RewriterRule>
            
    <LookFor>~/Products/Dairy\.aspx</LookFor>
            
    <SendTo>~/ListProductsByCategory.aspx?CategoryID=4</SendTo>
          
    </RewriterRule>
          
    <RewriterRule>
            
    <LookFor>~/Products/GrainsCereals\.aspx</LookFor>
            
    <SendTo>~/ListProductsByCategory.aspx?CategoryID=5</SendTo>
          
    </RewriterRule>
          
    <RewriterRule>
            
    <LookFor>~/Products/MeatPoultry\.aspx</LookFor>
            
    <SendTo>~/ListProductsByCategory.aspx?CategoryID=6</SendTo>
          
    </RewriterRule>
          
    <RewriterRule>
            
    <LookFor>~/Products/Produce\.aspx</LookFor>
            
    <SendTo>~/ListProductsByCategory.aspx?CategoryID=7</SendTo>
          
    </RewriterRule>
          
    <RewriterRule>
            
    <LookFor>~/Products/Seafood\.aspx</LookFor>
            
    <SendTo>~/ListProductsByCategory.aspx?CategoryID=8</SendTo>
          
    </RewriterRule>
        
    </Rules>
      
    </RewriterConfig>
      
      
        
    <system.web>

          
    <httpModules>
            
    <add type="URLRewriter.ModuleRewriter, URLRewriter" name="ModuleRewriter" />
          
    </httpModules>

          
    <!--<httpHandlers>
            
    <add verb="*" path="*.aspx" type="URLRewriter.RewriterFactoryHandler, URLRewriter" />
        
    </httpHandlers>-->
          
            
    <!-- 
                设置 compilation debug
    ="true" 将调试符号插入
                已编译的页面中。但由于这会 
                影响性能,因此只在开发过程中将此值 
                设置为 
    true
            
    -->
            
    <compilation debug="false" />
            
    <!--
                通过 
    <authentication> 节可以配置 ASP.NET 使用的 
                安全身份验证模式,
                以标识传入的用户。 
            
    -->
            
    <authentication mode="Windows" />
            
    <!--
                如果在执行请求的过程中出现未处理的错误,
                则通过 
    <customErrors> 节可以配置相应的处理步骤。具体说来,
                开发人员通过该节可以配置
                要显示的 html 错误页
                以代替错误堆栈跟踪。

            
    <customErrors mode="RemoteOnly" defaultRedirect="GenericErrorPage.htm">
                
    <error statusCode="403" redirect="NoAccess.htm" />
                
    <error statusCode="404" redirect="FileNotFound.htm" />
            
    </customErrors>
            
    -->
        
    </system.web>
    </configuration>


    然后,你的 aspx 程序就会按照你 web.config 文件中的 正则表达式,转换url 请求地址,实现 urlrewrit 技术。
    比如:

    1、http://localhost:4789/GuanTestURLRewrit/2003/07/18.aspx 
    按照 web.config 文件中的正则,此 url 地址为被 重写到以下真实存在的地址
    http://localhost:4789/GuanTestURLRewrit/ShowBlogContent.aspx?year=2003&month=7&day=18

    2、http://localhost:4789/GuanTestURLRewrit/2003/default.aspx
    按照 web.config 文件中的正则,此 url 地址为被 重写到以下真实存在的地址
    http://localhost:4789/GuanTestURLRewrit/ShowBlogContent.aspx?year=2003

    3、http://localhost:4789/GuanTestURLRewrit/Products/Confections.aspx
    按照 web.config 文件中的正则,此 url 地址为被 重写到以下真实存在的地址
    http://localhost:4789/GuanTestURLRewrit/ListProductsByCategory.aspx?CategoryID=3


    可以自己定义自己的正则表达式实现不同的 url 重写规则

    如果您想把 aspx 重写成 html 后辍名,那么则需要改动一下你的 web.config 文件,
    <httpHandlers>
            <add verb="*" path="*.aspx" type="URLRewriter.RewriterFactoryHandler, URLRewriter" />
         <add verb="*" path="*.html" type="URLRewriter.RewriterFactoryHandler, URLRewriter" />
    </httpHandlers>
    这样好像还不行,那是因为在IIS里面无法解析.html后缀名(具体我也不知道怎么叫...)
     然后这样操作:
                 右键点我的电脑-->管理-->展开'服务和应用程序'-->internet信息服务-->找到你共享的目录-->右键点击属性 -->点击'配置'-->
    映射下面 -->找到.aspx的可执行文件路径 复制路径-->粘贴路径-->扩展名为".html"-->然后把检查文件是否存在的勾去掉 这样就可以了

    引用 ActionlessForm.dll 文件,是因为当页面中有Post数据(如Post文本)。那么这时重写后的URL就会变为:http://localhost/Test/2004/12/News.aspx?ID=12 真实的 url,露出原始的地址了,这显然是不完善的,

    附:为什么URL就会变为:http://localhost/Test/2004/12/News.aspx?ID=12
    其实很简单,因为在web.config中有这样的一句:
    <SendTo>~/Default.aspx?ID=$2</SendTo>
    在没有替换form之前,你查看页面的源码就可以看到,你的form的Action是到(以上面的例子):Default.aspx?ID=12
    即.aspx页面最后生成的HTML是:
    <form id="Form1" name="Form1" method="post" action="Default.aspx?ID=12"></form>

    解决方述问题方法:

    首先把ActionlessForm.dll拷入你的项目中的bin目录,然后在你的VS.net的项目中引用这个dll。再在你原有的(即没重写的).aspx页面中

    第一步:把这句加于代码顶部:
    <%@ Register TagPrefix="skm" Namespace="ActionlessForm" Assembly="ActionlessForm" %>
    第二步:
    <form id="Form1" method="post" runat="server">和</form>
    替换成:
    <skm:Form id="Form1" method="post" runat="server">和</skm:Form>

    这样,当此页面有回发数据时,则不会跳到真实的 url 上去。


    还有,如果想用URL重写后的格式为以目录形式即不用加Default.aspx:
    http://localhost/Test/2004/12
    则要新建相应的目录和文件Default.aspx。
    如上例子:http://localhost/Test/2004/12
    则要新建2004目录和在此目录下新建12目录,再在12目录下新建Default.aspx文件。文件内容可为空。

    至于为什么,是因为IIS如没有找到目录或文件时会报错。 





    另外需要注意的是,要在项目中添加 2003,2004 还有月份 01,02.03 .....12 这样的目录,然后在第一个目录下添加 default.aspx ,内容只为一句话:<%@ Page %> 即可,防止用户输入目录回车后,找不到文件,出错。



     

  • ASP.Net缓存总结1

    提高性能最好最快的办法当然是通过缓存来改善,对于任何一个web开发者都应该善用缓存。Asp.net下的缓存机制十分强大,用好缓存机制可以让我们极大的改善web应用的性能,下面是一些总结的缓存的知识点,与大家分享交流:

    1.页面缓存
     


           要实现页面输出缓存,只要将一条 OutputCache 指令添加到页面即可。  

            <%@ OutputCache CacheProfile=" " NoStore="True | False" Duration="#ofseconds" Shared="True | False" Location="Any | Client | Downstream | Server | None | ServerandClient "SqlDependency="database/table name pair | CommandNotification " VaryByControl="controlname"VaryByCustom="browser | customstring" VaryByHeader="headers" VaryByParam="parametername" %>  

                 CacheProfile

                 用于定义与该页关联的缓存设置的名称。是可选属性,默认值为空字符("")。需要注意的是,包含在用户控件中的@ OutputCache指令不支持此属性。在页面中指定此属性时,属性值必须与Web.config文件<outputCacheSettings>配置节下的outputCacheProfiles元素中的一个可用项的名称匹配。如果此名称与配置文件项不匹配,将引发异常。

                 NoStore

                 该属性定义一个布尔值,用于决定是否阻止敏感信息的二级存储。需要注意的是,包含在用户控件中的@ OutputCache指令不支持此属性。将此属性设置为true等效于在请求期间执行代码“Response.Cache.SetNoStore();”。

                Duration

                 用于设置页面或者用户控件缓存的时间。单位是秒。通过设置该属性,能够为来自对象的HTTP响应建立了一个过期策略,并将自动缓存页或用户控件输出。需要注意的是,Duration属性是必需的,否则将会引起分析器错误。

                 Shared

                 该属性定义一个布尔值,用于确定用户控件输出是否可以由多个页共享。默认值为false。注意,包含在ASP.NET页中的@ OutputCache指令不支持此属性。

                Location

                 用于指定输出缓存项的位置。其属性值是OutputCacheLocation枚举值,它们是Any、Client、Downstream、None、Server和ServerAndClient。默认值是Any,表示输出缓存可用于所有请求,包括客户端浏览器、代理服务器或处理请求的服务器上。需要注意的是,包含在用户控件中的@ OutputCache指令不支持此属性。

                 SqlDependency

                 该属性标识一组数据库/表名称对的字符串值,页或控件的输出缓存依赖于这些名称对。需要注意:SqlCacheDependency类监视输出缓存所依赖的数据库中的表,因此,当更新表中的项时,使用基于表的轮询将从缓存中移除这些项。当通知(在SQL Server 2005中)与CommandNotification值一起使用时,最终将使用SqlDependency类向SQL Server 2005服务器注册查询通知。另外,SqlDependency属性的CommandNotification值仅在ASP.NET页中有效。控件只能将基于表的轮询用于@ OutputCache指令。

                 VaryByControl

                 该属性使用一个分号分隔的字符串列表来更改用户控件的输出缓存。这些字符串代表在用户控件中声明的ASP.NET服务器控件的ID属性值。除非已经包含了VaryByParam属性,否则在@ OutputCache指令中,该属性是必需的。

                 VaryByCustom

                 用于自定义输出缓存要求的任意文本。如果赋予该属性值是browser,缓存将随浏览器名称和主要版本信息的不同而异。如果输入了自定义字符串,则必须在应用程序的Global.asax文件中重写HttpApplication.GetVaryByCustomString方法。

                VaryByHeader

                 该属性中包含由分号分隔的HTTP标头列表,用于使输出缓存发生变化。当将该属性设为多标头时,对于每个指定的标头,输出缓存都包含一个请求文档的不同版本。VaryByHeader属性在所有HTTP 1.1缓存中启用缓存项,而不仅限于ASP.NET缓存。用户控件中的@ OutputCache指令不支持此属性。

                 VaryByParam

                 该属性定义了一个分号分隔的字符串列表,用于使输出缓存发生变化。默认情况下,这些字符串与用GET方法属性发送的查询字符串值对应,或与用POST方法发送的参数对应。当将该属性设置为多参数时,对于每个指定的参数,输出缓存都包含一个请求文档的不同版本。可能的值包括“none”、“*”和任何有效的查询字符串或POST参数名称。值得注意的是,在输出缓存ASP.NET页时,该属性是必需的。它对于用户控件也是必需的,除非已经在用户控件的@ OutputCache指令中包含了VaryByControl属性。如果没有包含,则会发生分析器错误。如果不需要使缓存内容随任何指定参数发生变化,则可将该值设为“none”。如果要使输出缓存根据所有参数值发生变化,则将属性设置为“*”。 
          
           创建页面输出缓存文件依赖

           
    示例代码:Response.AddFileDependency(MapPath("test.xml"));
           如需要建立依赖多文件关系,则使用AddFileDependencies()方法。
           
           使用编程方式设置页面缓存过期
           
           
    示例代码:HttpResponse.RemoveOutputCacheItem(Page.ResolveUrl("~/test.aspx"));
           此方法只接受一个"虚拟绝对"路径,因此需用Page.ResolveUrl()方法转换
           
           使用编程方式设置多个页面缓存过期(创建键依赖(key dependency)
           
           示例代码:
           缓存页面:PageLoad: 
            Cache.Insert(“key”,DateTime.Now);
            Response.AddCacheItemDependency("key");
           通过此法向多个页面添加依赖项
           移除依赖项:PageLoad:
           Cache.Remove("key");

           以编程方式操作页面输出缓存

           
    操作由Response.Cache属性暴露的HttpCachePolicy类对象的方法。

           创建页面输出缓存配置

        <system.web>
              <caching>
                    <outputCacheSettings>
                        <outputCacheProfiles>
                                <add name="CacheProfile1" duration="60" />
                        </outputCacheProfiles>
                    </outputCacheSettings>
              </caching>
           </system.web>

    2.部分页面缓存

           缓存后替换
           
           采用声明方式,使用Substitution控件,设置MethodName属性所需的方法,此方法必须是静态方法,因为当前页输出缓存时,页面实例还没被创建。注:AdRotator内部使用了缓存后替代。
           
           以编程方式设置缓存后替换,使用Response.WriteSubstitution()方法,好处:1,此方法引用的方法不一定是当前类的方法,可以是另一个类的实力或静态方法。2,可以在自定义控件中使用此方法实现缓存后替换。
           
           部分页面缓存:用户控件缓存
           
           给用户控件添加<%@ OutputCache%>指令。此指令包含一个Shared属性,可设置共享用户控件的输出缓存

           以编程方式设置用户控件缓存

           当用户控件中包括<%@ OutputCache%>指令时,可以通过用户控件的CachePolicy属性所暴露的ControlCachePolicy类的实例的属性控制修改空间如何缓存。

           创建用户控件缓存的文件依赖

           可以使用CacheControlPolicy.Dependency属性在一个缓存了的用户控件和文件系统中一个文件间创建一个依赖,示例代码:
           PageLoad:
           CacheDependency depend=new CacheDependency(MapPath("~/test.xml"));
           this.CachePolicy.Dependency=depend;
           
           缓存动态载入的用户控件

           
    可以使用Page.LoadControl()方法载入用户控件,当具有缓存特性的用户控件被载入时,Asp.net Framework自动一个PartialCachingControl类的实例包装用户控件。示例代码:
           PageLoad:
           PartialCachingControl cacheme=(PartialCachingControl)Page.LoadControl("test.ascx");
           Cacheme.CachePolicy.SetExpires(DateTime.Now.AddSeconds(10));
           PlaceHolder1.Controls.Add(cacheme);
           Lable1.Text=cacheme.CachePolicy.Duration.ToString();

    3.使用DataSource缓存

           SqlDataSource、ObjectDataSource、XmlDataSource控件都包括了用于缓存DataSource承载的属性,好处是数据源控件可以在数据更新时自动重新载入数据。并且可以在多个页面间共享相同的数据,通过一些属性的组合来识别:SelectCommand、SelectParameters、ConnectionString。如果属性相同,即共享相同的缓存数据。

           通过设置属性设置缓存过期策略

           
    包括绝对缓存(EnableCaching="True" CacheDuration=“xxx”)和Sliding缓存(EnableCaching="True" CacheExpirationPolicy="Sliding" CacheDuration=“xxx”)
           
           使用ObjectDataSource控件缓存
           
           通过设置控件的EnableCaching、CacheExpirationPolicy、CacheDuration属性以及SelectMethod所制定的方法名来完成。

           使用XmlDataSource控件缓存

           设置DataFile属性创建一个文件依赖。

           创建数据源控件键值依赖
           
           操作步骤
           1、设置数据源控件的CacheKeyDependency属性(key);
           2、在Global.asax创建初始化的(key)缓存项目。代码如下:
           Void Application_Start(Object Sender,EventArgs e)
            {
                 HttpContext context=HttpContext.Current;
                 context.Cache.Insert(
                 "key",DateTime.Now,null,DateTime.MaxValue,Cache.NoSlidingExpiration,CacheItemPriority.NotRemovable,null
                );
            }
           3、在用于更改数据的页面上移除缓存项目(key);
           如在DetailsView控件的ItemInserted事件中重新插入缓存项目,此时每个依赖于这个键值(key)的DataSource会自动重新载入数据,代码如下:
           protected void DetailsView_ItemInserted(object sender,DetailsViewInsertedEventArgs e)
           {
               Cache.Insert("key",Datetime.Now);
            }
           注:以上key值采用当前时间并非必须。

    4.Cache对象

           几乎可以给缓存添加任何对象,例如,可以添加自定义控件,DataSet,DataTable,ArrayList和List到缓存。注意:使用从缓存中返回的任何项目,应该总是要检查项目是否为空,如果一个项目已经被删除了,则当将来试图从缓存中读取时,就会返回null。
           详细信息查看msdnCache 成员
           添加数据缓存到Cache对象示例代码:
            void Page_Load()
            {
                 DataTable dt=(DataTable)Cache["dtkey"];
                  if(dt==null)
                    {
                          dt=getdtFromDB();   //此处调用方法从数据库中返回数据项DataTable
                          Cache.Insert("dtKey",dt,null,DateTime.Now.AddHours(1),Cache.NoSlidingExpiration);   //此处使用绝对过期策略添加项目
                     }
                    GridView1.DataSource=dt;
                    GridView1.DataBind();
            } 
             private DataTable getdtFromDB()
           {
              //略......
            }

           使用依赖添加项目

           
    Asp.net Framework包括三种缓存依赖
           1、CacheDependency——用于创建一个文件依赖或缓存键值依赖。
           2、SqlCacheDependency——用于创建一个对于Microsoft SQL Server数据库表或SQL Server 2005数据库查询的依赖。
           3、AggregateCacheDependency——用于使用多个CacheDependency对象创建依赖,例如,可以用该对象组合文件和Sql依赖。

           CacheDependency类是基类,其他两个类都是从该类继承。

           指定缓存项目优先级
           
           可以指定CacheItemPriority枚举类型任意值。

           配置缓存
            
           详细信息查看Msdn Caching元素

    5.使用SQL缓存依赖

           
    Asp.net Framework支持两种类型的SQL缓存依赖:拉和推。第一种模式使用表轮询的 ASP.NET 实现,第二种模式使用 SQL Server 2005 的查询通知功能。可以对任何最近版本的Ms SQL Server,包括Ms SQL server 2005 Express、Ms SQL Server 2000 和 Ms SQL Server 7.0,使用拉SQL缓存依赖。第二种类型推缓存依赖则只能用于Ms SQL Server 2005和Ms SQL server 2005 Express,因为他们依赖SQL Server的Service Broker。
           
           使用拉SQL缓存依赖

            实质上拉SQL缓存依赖使用数据库tigger,当表被修改时,tigger被触发,名为AspNet_SqlCacheTablesForChangeNotification的数据表的一行数据被更新,来记录修改情况,Asp.net Framework使用一个后台线程,来定期拉数据表的修改信息。如果有修改,则依赖于数据表的缓存项目被移除。
           配置拉SQL缓存依赖:
           1、必须对一个或多个数据库表启用SQL缓存依赖。
                 
                 可以使用框架中的SqlCacheDependencyAdmin类来配置SQL数据库支持拉SQL缓存依赖,由于调用该类的方法需要创建表、存储过程、trigger,出于安全考虑,Asp.net进程并不应该被赋予这些权限,而是通过一个命令行工具来使用此类。
                 aspnet_regsql 详细信息访问Msdn ASP.NET SQL Server 注册工具 (Aspnet_regsql.exe)

                 简要步骤: 1、启用特定数据库的SQL缓存依赖。
                 aspnet_regsql -c "Data Source=localhost;integrated Security=True;Initial Catalog=Pubs" -ed
                                    2、启用特定表的SQL缓存依赖。
                 aspnet_regsql -c "Data Source=localhost;integrated Security=True;Initial Catalog=Pubs" -ed -t Titles

           2、必须在Web配置文件中配置SQL缓存依赖。
                 <!-- caching section group -->
                    <caching>
                    <sqlCacheDependency enabled = "true" pollTime = "1000" >   //通过pollTime 的设置,定时拉数据库的修改
                        <databases>
                          <add name="Northwind" 
                             connectionStringName="NorthwindConnectionString1"
                           pollTime = "1000" 
                          />
                        </databases>
                    </sqlCacheDependency>
                    </caching>

          a、 对页面输出缓存使用拉SQL缓存依赖:<%@ OutputCache%>指令指定sqlDependency属性值:库名和表名(Mydatabase:Mytable);
           
           b、对DataSource控件使用拉SQL缓存依赖:为DataSource控件sqlDependency属性指定值:库名和表名(Mydatabase:Mytable);

           c、对Cache对象使用拉SQL缓存依赖
           
           void Page_Load()
            {
                 DataTable dt=(DataTable)Cache["dtkey"];
                  if(dt==null)
                    {
                          dt=getdtFromDB();   //此处调用方法从数据库中返回数据项DataTable
                          SqlCacheDependency sqlDepend=new SqlCacheDependecy("Mydatabase","Mytable");
                          Cache.Insert("dtKey",dt,sqlDepend); 
                     }
                    GridView1.DataSource=dt;
                    GridView1.DataBind();
            } 
             private DataTable getdtFromDB()
           {
              //略......
            }
           
           使用推SQL缓存依赖

           通过Service Broker可以在数据库中的数据变更时自动给应用程序发送一个消息。
           好处:Asp.net应用程序不必定时拉数据库的修改。
           缺点:查询类型有诸多限制(如必须使用两部分的表明:abo.mytabel,查询必须包含一个显示的列名表明:不能使用*,不能引用视图、临时表等,不能包含子查询、外连接、子连接,不能引用大对象、不能使用DISTINCT、COMPUTE、COMPUTE BY、INSERT关键字、不能包含许多聚合函数 等等)

           1.为推SQL缓存依赖配置数据库
           启用Ms SQL Server 2005 Service Broker:
           a、可以通过:Select name ,is_broker_enabled from sys_databases,查询是否对特定的数据库激活。
           b、通过:Alter Database MyBase Set ENABLE_BROKER,启用。
           c、为本地AspNet帐号赋予需要的权限,如:Grant Subscribe Query Notifications To “yourserver\Aspnet”

           2.为推SQL缓存依赖配置应用程序
           void Application_Start(object sender, EventArgs e)
           {
                 string conString=WebConfigurationManager.ConnectionStrings["MyContention1"].ConnectionString;
                 SqlDependency.Start(conString);
                 HttpContext context=HttpContext.Current;
                 context.Cache.Insert(
                 "key",DateTime.Now,null,DateTime.MaxValue,Cache.NoSlidingExpiration,CacheItemPriority.NotRemovable,null
                );
            }

          a、对页面输出缓存使用推SQL缓存依赖
           当缓存整个Asp.net页面时,可以使用推Sql缓存依赖。如果包含在页面上的任何Sql命令的结果有变动,页面就会自动从缓存中过期。
           SqlCommand对象包含一个NotificationAutoEnlist属性,该属性默认值为true。当NotificationAutoEnlist启用时,页面和命令间自动创建一个推缓存依赖。注意SqlDataSource的SelectCommand必须符合查询要求。
           
        b、对DataSource控件使用推SQL缓存依赖
           只需要设置SqlcacheDependency属性即可。设置SqlCacheDependency=“CommandNotification”
           
        c、对Cache对象使用推SQL缓存依赖
           void Page_Load()
            {
                 DataTable dt=(DataTable)Cache["dtkey"];
                  if(dt==null)
                    {
                          string conString=WebConfigurationManager.ConnectionStrings["MyContention1"].ConnectionString;
                          SqlDatadapter dad=new SqlDataAdapter("Select a,b From dbo.Mytable",conString); //注意查询符合要求
                          SqlCacheDependency sqlDepend=new SqlCacheDependecy(dad.SelectCommand);
                          dt=new DataTable();
                          dad.Fill(dt);
                          Cache.Insert("dtKey",dt,sqlDepend); 
                     }
                    GridView1.DataSource=dt;
                    GridView1.DataBind();
            }
           注意,SqlCacheDependency类的示例被创建了。一个SqlCommand对象被传递给SqlCacheDependency类的构造函数。如果SqlCommand的结果变化了,则这个DataTable会自动从缓存中失效。这些命令的顺序很重要。必须在执行该命令之前创建SqlCacheDependency对象。如果在创建SqlCacheDependency对象之前调用Fill()方法,则依赖会被忽略。

           至此,有关缓存的总结,已经结束,本章只包含了一些知识要点,具体应用还要是情况对待。

    Posted May 07 2010, 02:36 PM by slash with no comments
    Filed under:
  • ASP.NET缓存初探 使用得当是关键

    缓存是在内存存储数据的一项技术,也是ASP.NET中提供的重要特性之一,对于程序员来讲,了解ASP.NET缓存的工作原理对于其设计程序是非常有用的。

      ASP.NET需要被缓存的对象多种多样,包括从数据库中提取出来的数据,以及aspx页面生成的静态页,甚至是编译好的程序集。合理利用缓存能让ASP.NET的性能大幅提升,下面将对ASP.NET中的缓存机制进行简单概述。

      缓存的分类

      在ASP.NET中,大部分缓存机制是保存在cache对象中,也就是服务器内存的一部分。当用户请求数据时,如果数据已经被缓存,则用户所提取的数据直接从服务端返回,而不是从数据库等底层数据库提取。这对性能的提升不得不说很有帮助。下面来看ASP.NET中几种缓存机制。

      程序集缓存

      简单的说,这种缓存是ASP.NET自带的,无需开发人员进行参与的缓存方式。即当第一次请求服务器时,Page类以及相关程序集被编译,当下次请求时,访问缓存后的编译而不是重新编译。CLR会自动检测代码的改变,如果代码改变后,当下次访问时,相关代码会被重新编译。

      数据源缓存

      数据源缓存,顾名思义,也就是利用数据源控件对获取的数据进行缓存的方式。这些控件包括SqlDataSource,ObjectDataSource等,作为抽象类的DataSourceControl暴漏了如下属性用于缓存:

      DataSourceControl暴露的缓存

      CacheDuration获取或设置以秒为单位的一段时间,数据源控件就在这段时间内缓存 SelectMethod 属性检索到的数据;CacheExpirationPolicy获取或设置缓存的到期行为,该行为与持续时间组合在一起可以描述数据源控件所用缓存的行为;CacheKeyDependency获取或设置一个用户定义的键依赖项,该键依赖项链接到数据源控件创建的所有数据缓存对象;EnableCaching 获取或设置一个值,该值指示 ObjectDataSource 控件是否启用数据缓存。

      而使用起来就非常简单了,只需要将缓存的相关属性进行设置即可。比如我想要当前数据源缓存10秒,只需要设置EnableCaching属性和CacheDuration属性如下,这种方式的工作原理可以用下图表示:

      设置EnableCaching属性和CacheDuration属性

      SQL Cache Dependency

      大家应该注意到了上面的数据源控件还暴漏了CacheKeyDependency属性,这是用于实现SQL Cache Dependency的方式,关于Dependency,其实就是在数据库表内容改变时,将相应的缓存进行更新,正如Dependency这个词的意思一样,是缓存依赖底层数据库。下面就要说到两种实现SQL Cache Dependency的方法了。

    方法一:使用轮流查询机制(polling-based)

      这种方式实现机制是在sql server中插入以AspNet_SqlCacheNotification_Trigger开头的一个特殊的表和5个存储过程,当被监测的表数据发生改变时,则一个名为AspNet_SqlCacheTablesForChangeNotification的表被更新,而ASP.NET程序会根据用户设置的间隔时间每隔一定时间检查一下数据库内容是否更新,如果更新,则将缓存中的数据进行跟新。

      使用起来就很简单了,可以在页面头部的OutputCache指令中设置,会社DataSource空间中进行设置,设置格式为:“数据库名:表名”.里面的表名即是需要监测是否改变的表名,示例如下:

      如果需要添加多个表,则用”;”进行分割

     


      SqlDependency="database:table;database:table"

     

      方法二:使用通知机制(notification-based)

      使用通知机制配置起来要简便很多,但是sql server的版本需要9.0以上,也就是sql server 2005,使用这种方式需要将sql server的通知服务开启,使用通知机制可以对页面进行缓存,也可以对datasouce控件进行缓存,对页面进行缓存代码如下:

     


    <%@ OutputCache Duration="30" VaryByParam="none" SqlDependency="CommandNotification" %> 

     

     

      注意SqlDependency必须设置成CommandNotification,对于datasource控件,也是同样:

     


    <asp:SqlDataSource ID="SqlDataSource1" runat="server"     
    ConnectionString="<%$ ConnectionStrings:AdventureWorksConnectionString %>"     
    SelectCommand="SELECT top 10 * FROM [Person].[Contact]" EnableCaching="true" CacheDuration="10" SqlCacheDependency="CommandNotification"></asp:SqlDataSource> 

     

     


     

    输出缓存(output Caching)

      输出缓存是页面级别的缓存,是将aspx页面内容在第一次请求后生成的静态页放入缓存,在不过期时间内每一次请求时从缓存中返回静态页,而不是重新走完ASP.NET的生命周期。可以将可以通过在页面头部加入OutputCache指令实现,也可以通过HttpCachePolicy类实现,输出缓存可以缓存整个页面,也可以缓存部分页面,缓存页面的一部分是通过用户控件来实现,下面来看通过OutputCache指令实现页面缓存,前面已经看到,这种方式十分简单,下面说一下OutputCache的重点属性:

      Duration

      页面过期的时间,单位为秒。超过过期时间后,则在下一次请求时页面会重新生成并缓存。

     


    VaryByHeader 

    VaryByCustom 

    VaryByParam 

    VaryByControl

     VaryByContentEncodings

     

      这些属性都是为了保存页面的多个版本,比如说一个页面用于显示产品,则根据产品id的不同,缓存同一个页面的不同版本。

      CacheProfile

      这个选项有些像连接字符串,作用只是将具体的缓存选项变成对于选项的引用,比如我们在Web.Config放入如下代码:


        
    <caching>   
            <outputCacheSettings>   
              <outputCacheProfiles>   
                <add name="CacheProfile"   
                      enabled="true"   
                      duration="60"   
                       varyByParam="product:id"/>   
              </outputCacheProfiles>   
            </outputCacheSettings>   
    </caching>
     

     

      则在引用时只需要在页面头部设置如下:

      


    <%@ OutputCache CacheProfile="CacheProfile" %> 
     

     

      因为服务器内存是有限的,所以通过将DiskCacheable属性设置为true,则可以将缓存页面放入硬盘中,这样即使服务器崩溃重启,缓存依然存在。

      缓存部分页面

      缓存页面的一部分实现原理和缓存整个页面毫无二致,都是在页面头部加入OutputCache指令,唯一的不同是缓存部分页面是在用户控件中进行的。这部分就不在多说了。

      使用HttpCachePolicy缓存页面

      前面已经说了通过OutputCache指令在页面头部设置缓存选项,另一种替代方法是使用HttpCachePolicy类,这个类的实例是Response.Cache.如果使用HttpCachePolicy设置缓存,则需要在页面移除OutputCache指令。比如:

     


     <%@ OutputCache  Duration="30" VaryByParam="state;city" %> 

     

      和下面代码是等价的:

     


     Response.Cache.SetExpires(DateTime.Now.AddSeconds(30)); 

       Response.Cache.VaryByParams["state"] = true; 

       Response.Cache.VaryByParams["city"] = true;

     

      对象缓存

      对象缓存是将继承与System.Object的对象缓存在服务器的内存中,通过Page类的Cache属性可以访问到Cache集合。Cache内可以放任何类型的对象,但是要小心使用Cache,因为Cache是占用服务器内存,如果使用不当,也许会拖累性能。使用Cache的例子:

     


     //save object into Cache Cache["table"] = GridView1; 

      //get object from Cache GridView gv = (GridView)Cache["table"];

     

      要注意的是,再提取缓存中的对象时,一定别忘了强制转换。

    Posted May 07 2010, 02:29 PM by slash with no comments
    Filed under:
  • 信凤姐,得自信


    PS达人将凤姐PS成阿凡凤·姐和《知音》《故事会》封面

     

    近期,凤姐其人其事其语录就像一阵飓风,强劲地登陆互联网。猫扑、天涯等论坛头版被其占领,豆瓣有了她的小组,百度有了她的贴吧,PS、段子恶搞成风。对于“向前三百年,向后三百年,总共六百年无人超过”的凤姐来说,究竟当今世上谁能符合她的择偶标准呢?昨日罗玉凤在接受一家视频网站访问时说:“我想强调一点,奥巴马是非常符合我心目中择偶标准的。”

    凤姐说,奥巴马符合她的择偶标准

    昨日,罗玉凤接受了一家知名视频网站访问。主持人对凤姐苛刻的征婚条件十分感兴趣,想知道在凤姐的心目中,世上究竟有谁符合她的标准。凤姐淡淡地说:“有一天我看到一本奥巴马演讲的书,我只看了一页,觉得还可以。”对于奥巴马这样的大人物,凤姐表示可以放宽一点要求,不必苛求奥巴马是清华北大毕业,也不是非要经济学硕士才行,只要名校毕业就行。于是,网友们终于“内牛满面”地看到有人符合凤姐的要求了,凤姐说:“我想强调一点,奥巴马是非常符合我心目中择偶标准的。”

    凤姐的择偶要求虽然苛刻到了极致,但是却只求才不求财,她表示:“即使两个人都一无所有,只要在一起,就能产生无限的想象力。”

    凤姐:爱因斯坦发明电灯

    凤姐曾经曰过:“往前推三百年,往后推三百年,总共六百年没有人超过我。”网友向她提问:“为什么是三百年,而不是五百年呢?”凤姐显得比较谦虚:“我这个人目光短浅,只能看到前后三百年的事情。”看到前三百年可以理解,看到后三百年又如何解释呢?凤姐的回答颇为神秘:“ 对于后三百年,我可以通过现在的事情进行推断。”

    接着,网友想尽了前三百年中的名人,最后搬出爱因斯坦,让凤姐比一比。“爱因斯坦不及我,不及我,差远了。”凤姐连想都没想,直接把爱因斯坦PK掉,“我不能发明电灯,我也不知道相对论,爱因斯坦的确影响了人类,但是他从宏观上对人类的调整,还是不如我。”主持人满头大汗问凤姐:“爱因斯坦究竟哪里比不上你呢?”凤姐答:“没有爱因斯坦发明电灯,还有其他人能发明电灯,他的才能可以复制,但是我的才能没人能复制。”网友也满头大汗,因为电灯是爱迪生发明的。

    “自信哥”敢于求婚

    这两天的猫扑、天涯等论坛头版上,多条帖子都被凤姐占领。凤姐的风采,引人尽折腰。同时,豆瓣有了她的小组,名字很霸气,叫“信凤姐,我自信”,创建没几天就有近400人参加,小组成员名叫“凤凰”。

    凤姐雷人要求也有“自信哥”,敢于向凤姐求婚。一位猫扑网友,便洋洋洒洒写下了“古今六百年,只有我能配上玉凤姐”的帖子。他说:“往前推三百年,往后推三百年,总计六百年,只有我才能配上凤姐,你们这些人不要笑,且听我细说。”

    他细说道:“本人自幼熟读唐诗二首:李白的《静夜思》与骆宾王的《鹅》。你们一般人是不可能背下来的,在俺村古今六百年,只有俺会背这两首诗。”至于身高方面,“自信哥”自称身高1米59,“比玉凤姐足足高了13厘米,俺村的老奶奶们都夸俺一表人才,所以我自信与玉凤姐的标准一致,可谓高大英俊。”

    《知音》和《故事会》都火了

    随着凤姐成为网络红人,她的玉照也不可避免地被网友ps,其中一张被ps成《阿凡达》的最为经典,网友取名为“阿凡凤·姐”。当然,凤姐所谓的社科人文书籍《知音》《故事会》也遭网友ps,封面竟是凤姐。在各大论坛上,到处可见网友回帖:“为了做一个有国际视野的人,我正在研读《知音》和《故事会》。”还有网友表示:“据可靠消息,《知音》和《故事会》这两本社会人文类的书近日销量大增。”但也有网友遇到了困难,“我也想提高自己的层次,向凤姐看齐,可是《知音》和《故事会》太难读懂了。”

    恶搞最为彻底的,算是猫扑网友的帖子“我在火车上揍了一偷看我《故事会》的小伙子”。帖子中写道:“在回家的火车上,我在座位上津津有味地读着我心爱的《故事会》,突然我感觉到后面有人在偷看我的书。我转头一看,原来是一个大概二十来岁的小伙子,他没买到座票站在我后面。”然后楼主就把小伙子揍了,并讲解说:“《故事会》,前三百年,后三百年,共六百年都很少有人能看懂,比《易经》更深奥,被称为天下第一奇书。《故事会》和《知音》一样,都是社会文学类巨著,是我这种高级白领才能读懂的。”最后,“小伙子流下了激动的泪水,他握着我的手,目光中充满了执着和坚定,‘你相信我,我以后一定会每天都背唐诗,争取早日能读懂《故事会》!’”

    罗玉凤-简介 

    罗玉凤[1],女,1985年8月9日生人,重庆綦江赶水镇人,身高1.46米,大专学历,在上海家乐福超市工作,月收入千余元,自称9岁博览群书、20岁达到顶峰,“前后300年无人超越”,曾在上海地铁站发过成千上万份征婚传单,也曾在电视台情感类节目上公布七大极为苛刻的征婚条件,誓嫁1.8米的名校硕士生,并且长得要阳光、帅气。因各种雷言囧语层出不穷、开出令人咋舌的高标准征婚条件,罗玉凤一“炮”而红,引起各路媒体和广大网民的关注,被网友戏称为“宇宙无敌超级第一自信”。罗玉凤自嘲称:青春就是拿来挥霍

    的,她现在正在挥霍青春。

    罗玉凤是重庆綦江赶水镇人,在綦江师范学校获得中师文凭,在重庆教育学院汉语言文学专业获得大专文凭。她自称懂诗画、会弹琴,精通古汉语,自称“9岁起博览群书,20岁达到顶峰,智商前300年后300年无人能及”。现主要研读经济类和《知音》、《故事会》等人文社科类书籍。罗玉凤还透露,7岁时她父母离婚。在离开重庆前曾谈过4次恋爱,但没有具体进展,都不了了之。

      罗玉凤自称,她曾经在重庆奉节黄泗小学做了两年的老师,学校开始让她教毕业班语文,一学期后,又将她调整到三年级,随后又调整到一年级。这所小学的老师表示对罗玉凤没有印象,只觉得她比较爱干净。

      到上海之后,为了找工作,她曾经投出了一万多份简历。从总经理到服务员,几乎各行各业她都试过。罗玉凤认为,从事金融业最适合自己,所以最终她选择在上海一个家乐福超市工作,月收入不过千余元。但走红后,她声称已经离开了该工作单位。

      罗玉凤仍在上海家乐福南方店作收银员

    罗玉凤其人其事

    罗玉凤,成名江苏卫视《人间》。在一次《人间》征婚节目中,罗玉凤口出雷言,“我九岁博览群书,二十岁达到顶峰。我现在都是看社会人文类的书,例如《知音》《故事会》……往前推三百年,往后推三百年,总共六百年没有人超过我。”她自称身高1米46,在网上单征男友,条件是“必须为清华北大硕士、身高1米76到1米83”。该期《人间》节目视频被网友发到网络上后,凤姐在网络迅速走红。

     

    罗玉凤-囧语  1.以我的智商和以我的能力的话,往前推三百年,往后推三百年,总共六百年之内不会有第二个人超过我。

      2.爱因斯坦绝对没我聪明,他发明电灯的嘛!

      3.必须具备国际视野,有征服世界的欲望。奥巴马才符合我的征婚标准。(谈择偶)

      4.本人找伴侣,一不求帅,二不求富。

      5.9岁博览群书,20岁到达顶峰,往前300年往后推300年,没有人会超过我。在智力上他们是不可能比我强的,那就在身高和外貌上弥补吧……

      6.他太老了,而且身高也不够。他也不可能是北大清华,更不可能是经济学专业,我不会选他(陈坤)。

      7.这个标准不高,这个标准很低。

      8.看到其他女的就他妈花痴一样。(疯了!上电视说脏话)

      9.过了三十岁自己滚蛋。

      10.男人过了三十岁就没看点了,就人老珠黄了。

      11.你给我十万。

      12.吾日三省吾身(凤姐念错字了)省应该读 xǐng,她读shěng。

      13.我用的是A4纸,因为A4是非常标准的纸。

      14.因为上海是一个经济中心,我这个人对征服经济世界蛮有兴趣的。

      15.我经常看的都是人文社会的书,例如《知音》、《故事会》。

      16.我一般按长相将人分五等。我是第三等。(主持人指着她前男朋友问:他是几等)他啊,没有等(捂嘴笑)……

      17.我在家乐福超市工作,世界500强。

      18.世界上有一半的男人看到我就想逃跑,另一半我看到他就想逃跑。

      19.我这个人有点洁癖,以前读书时衣服每天要洗,现在基本上过两三天就洗一次,洗头也这样,现在很多时间都浪费在这上面。

      20.罗指着台下众人说:你们这些普通院校的,如果撇开这上面的七条,你们肯定有人会愿意娶我……

      21.真的美女真多啊。我对面的一位男孩,开始很仔细的打量我,我想我应该还没有出名到他已经看过我的视频吧。有次有人对我说你上电视了,我说什么电视?人家说东方卫视啊。我打开电视,东方卫视正在播奥巴马的新闻。我觉得我和奥巴马之间还有很大的距离。做人,自知之明还是有的。

      22.你看看你的身高你的长相,我觉得你配不上我,我们之间差距太大了,带回家的话,我家里的人肯定会嘲笑我的,他们会说,罗玉凤,你找的男朋友怎么这么丢脸啊,连话都不会讲,我觉得我们还是算了吧,我一定能够找到一个比你好的多的男朋友。

      23.像我这样的一个人,独自在外面闯荡。很引人注目,可是自己想想。论交际,论人际关系,论工作能力。实在是不但女人,就是男人,也很难和我相比。论健康状况,却是一天不如一天的。我想我不能在这么下去了。

      24.我最喜欢的诗人是顾城 顾城(强调) 你知道吧???

      25.要说我写诗的风格嘛,比较像顾城,写文章嘛,人家都说我像鲁迅。

      26.山东走出去的我可以考虑一下,目前还在山东工作的不予考虑。

      27.凤姐:爱因斯坦宏观上不如我……

      激动网主持人:你指的宏观是?

      凤姐:把全人类更上一层吧。

      28.我弟弟长得很阳光帅气。

      你弟弟长得和你很像?

      对,很像!

      30.我在上海的时候,一直会有人在我肩膀上摸一摸什么的,有目的性的佧我油。

      31.(与主持人对话)

      —如果说他(指陈坤,凤姐的偶像之一)向你求婚了,跪在你面前了,玉凤你嫁给我吧,你会同意吗?

      —我不会同意的。首先他年龄三十三了,我觉得这个太老了哦。然后他身高可能应该也不足,然后他不是北大清华的,他根本就不可能是经济学专业的。

      32.中国人民银行、花旗银行、渣打银行、汇丰银行、交通银行、中国人寿等金融公司驻中国区首席执行官向我表达爱意,愿意与我结婚,而本人觉得他们年老色衰,所以不愿意。

      33.我的七大要求全国还能找到100个我认为不高,全国找到1个是还差一点,全国一个都找不出我认为是刚好。

      34.征婚者:“你从哪里来?”  罗玉凤:“我从地球来。”  征婚者:“好巧我也是。”

      35.我爱干净,比较洁癖,男朋友看到女的别他妈花痴样。

      36.主持人:你觉得爱因斯坦也不及你聪明么?

      罗玉凤:不及我不及我,差远了,他是一名科学家。

      主持人:对,他是一名科学家,但是你知道他所知道的东西么?

      罗玉凤:我不知道他知道的东西,我不能发明电灯。

      37.罗玉凤在谈自己最喜欢的一首诗,是凤姐自己写的:天还没有黑,天已经黑了。

      38.我平常接触的朋友多,因为我交际面广嘛。

      39.凭我的智慧,我的相貌,我完全可以找一个比他(指前男友)更优秀的男朋友。

      40.你去死!

      41.很多人都说我漂亮。我也知道我漂亮。

      42.我喜欢蓝莓的味道。蓝莓是一种优雅的水果,即使我毫无姿态地坐在路边的水泥地,拣起没有洗过的蓝莓塞进嘴里,我依然认为我是优雅的,因为优雅的蓝莓。

     
    罗玉凤-征婚标准

      本人找伴侣。一不求帅。二不求富。但求同甘苦,共患难。

      本人对伴侣要求如下:[3]

      第一,必须为北京大学或清华大学硕士毕业生。必须本科硕士连读,中途无跳级,不留级,不转校。在外参加工作后再回校读书者免。

      第二,必须为经济学专业毕业。非经济学专业毕业则必须精通经济学。或对经济学有浓厚的兴趣。

      第三,必须具备国际视野,但是无长期定居国外甚至移民的打算。

      第四,身高176--183左右。长得越帅越好。

      第五,无生育史。过往所有女友均无因自身而致的堕胎史。

      第六,东部沿海户籍,即江,浙,沪三地户籍或广东,天津,山东。北京,东北三省,内蒙古等地户籍。西南地区即重庆。贵州。云南。西藏,湖南,湖北等地籍贯者不予考虑。

      第七,年龄25--28岁左右。即06届,07届,08届,09届毕业生。有一至两年的工作经验,06级毕业生需年龄在28岁左右,09级毕业生则需聪明过人。且具备丰富的社会实践经验。就职于国家机关,国有企事业单位者不愿考虑。但就职于中石油,中石化等世界顶尖型企业或银行者又比较喜欢。现自主创业者要商榷一番了。

      本人85年旧历8月初9日生。新历生日为9月23日。身高146。平时穿高跟鞋153。体重40kg.先就读于綦江师范学校获中师文凭。

      后连读重庆教育学院获汉语言文学专业大专文凭。懂诗画,唱歌,弹琴,刺绣等。最擅长诗歌与散文。并精通古汉语。博览群书。较为狂妄。无堕胎史,无生育史。交过几个不了了之的男朋友。具体进展却无。主要要求男方身家清白,聪慧过人。

  • 使用 BlackBerry 8800 半年啦~

    从认识黑莓到现在, 半年; 从使用黑莓到现在, 半年. 没错, 我几乎是认识后不久就买了的, 对于一个对自己完全陌生的品牌, 这样的行动, 很大胆吧~~ 嘿嘿, 事实证明, 我是对的, 黑莓, 满足了我的需求, 或者说, 是欲望.

    不知道黑莓是啥的同学呢, 先看看维基百科吧: 黑莓
    这个解释不错吧 :-) 嘿嘿.. 跟同学们开了个小小的玩笑~~ 其实, wiki 上的解释在这里~~

    好了, 言归正传. 我买的是 BlackBerry 8800(ZY倒置版), 实物照片如下(真的是我买的实物啊!!)

    BlackBerry 8800

    P.S.: 由于使用有一段时间了, 轨迹球已经洁白不再了…. 背景呢, 是我们学校的课桌啦~~

    至于黑莓手机有什么特点啊什么的, 已经给广大博友写到烂了, 我就不再重复了, 就只写我对 BlackBerry 8800 的感觉. 有想知道其他型号的情况同学, 可以直接问 google 哦~~ 先给出 8800 的参数(仅显示我认为有用的)啦~

    手机型号: BlackBerry 8800
    手机制式: GSM (毕竟 3G 咱还用不起.. 就不去考虑了)
    网络连接: GSM/GPRS/EDGE (有 wi-fi 功能的是 8820~考虑到资金问题….)
    内存容量: 64 M (拿来装软件的..)
    电池类型: 锂电池 (….)
    屏幕分辨率: 320*240像素 (还是比较大的了^_^)
    主屏颜色: 26万色 (肃静.. 路过……)
    主屏尺寸: 2.4英寸 (同屏幕分辨率..)
    主屏材质: TFT
    视频支持: 3GP(H.263)、MP4、RealVideo、WMV等
    音频支持: MP3、AMR-NB、AAC、AAC+、eAAC+、WAV、WMA等
    浏览器    : HTML 浏览器
    GPS 与 蓝牙: 支持
    存储卡    : microSD(T-Flash)

    这样.. 我决定采用先抑后扬的手法来写我对我的 bb 半年来的感觉吧~

    我对 BB8800 的不满:

    • 娱乐功能太弱
      (哼..  谁让你个小 P 孩没钱买 8300 的..)
      恩.. 是的, 8800 的定位是商务机, 而娱乐机是 8300, 所以, 这点以及音质不佳等娱乐功能上的瑕疵都不能强求. 但是.. 但是…. RIM 啊.. 你是怎么想的啊, 为什么 8800 在放歌的时候, 长按增大音量键可以返回上一首, 而长按减小音量键却是重新开始正在播放的歌曲…… 这点就是你的不对啦!!
    • 重启的时间里, 一定要准备好一首歌
      因为.. 重启(因为安装了软件或者插拔电池而重启, 并不是普通的关机)需要 3-5 分钟.. 听一首歌吧~ 听完就重启好了….
    • 不能自动锁键盘
      非得用户使用第三方软件……
    • 蓝牙模式太繁琐
      在想要文件的时候, 除了对没有匹配过的机子进行匹配外, 还需要手动进入多媒体, 然后选择从蓝牙设备接收, 之后才能接收文件, 接收到信号后, 还得确认是否接收….
      虽然官方的解释是安全第一!! 但是.. 能不能给要求没那么高的用户简化的步骤呢?? 毕竟.. 遇到蓝牙攻击的概率不高吧.. 遇到了, 我要去买彩票!!
    • “多媒体” 没有快捷键
      很多很多有用没有的自带功能都有快捷键, 唯独 “多媒体” 没有.. 真 “人性化” 啊..
    • 插 TF 卡的位置
      非得掰开整个后盖才能取出来.. 挫, 真挫!!
    • 开/锁键盘的过程不是互逆的
      开键盘是: A+拨号键, 而锁键盘直接在大屏幕按 K 就可以了..
      而我一些同学, 开锁之后, 想解锁的时候, 直接 A+拨号键…. 打电话给了我联系人(A 是联系人的快捷键)的第一位…. 我爸………

    在说优点前, 来张背面的大图~~ 顺便看看 TF 卡的位置…..

    BlackBerry 8800

    好了, 接下来, 就说它的可爱的地方吧^_^

    • 自动补全
      在浏览器里面输入网址的时候, 可以直接用空格键来输入网址中的 “.”;
      在输入 email 的时候, 第一次按空格键, 将出现 “@”, 第二次输入就会出现 “.”;
      在发英文短信的时候, 输入 “ive” 然后空格, 就会变成 “I’ve”, 诸如此类.
    • 信号控制
      可以在连接管理里面选择开关通讯信号及蓝牙, 不用关机就可以达到避免电话短信的烦扰(考试的时候.. 嘿嘿), 多好啊~
    • 软件快捷键
      在大屏幕或者主屏幕, 直接按键(前提是关闭了屏幕拨号)就可以进入某个程序, 比如 “M” 进入短信(Massage), 很棒的.
    • 主屏幕软件图标可以隐藏
      我把短信的图标给隐藏了…….. 嘿嘿.
    • 多任务执行
      超超超喜欢的功能!! 顾名思义, 就是能够同时执行多个软件啦~ 比如, 我开个 Opera Mini 国际版, 再开个 Opera Mini 中国版一起来冲浪…. 嘿嘿. 还有就是开个字典软件, 英语课装 B 专用..
    • 轨迹球
      这个太太好玩了!! 嘿嘿…. 转来转去也巨方便~ RIM 的无敌创新啊!! 唯一不好的是.. 洁白的轨迹球会慢慢的, 慢慢的, 变黄… 唉, 机老珠黄啊!!
    • LED 指示灯
      有电话/短信/任务等的时候, 红灯提醒; 蓝牙连接的时候, 蓝灯提醒; 剩余电量 <= 5% 的时候, 黄灯.. 巨人性化啊!! 而且, 有软件可以调节等的颜色, 多彩缤纷哦~~
    • 剩余电量实在不多的时候, 强制性关闭通讯信号
      这样就避免了由于电池没电而关机了^^ 别忘了…. 因电池没电而重启是要去听一首歌的..
    • BrickBreaker 太好玩!!
      这直接导致我的手机好长一段时间都不在我手上..
    • 可以装 B
      我刚买会宿舍的时候, 同学们都问: 这是什么啊?
      我答: 黑砖头.
    • 与美国总统有交集
      大家都知道, 奥巴马同学用的是 BlackBerry 8700.. 跟他套近乎去吧~

    差不多就这些了吧.. 接下来, 给大家看看我现在用的主题吧(背景是我们凤凰木的海报^^~)

    这个主题很熟悉吧?? 没错, 就是深受大众喜爱的 T-Mobile 主题吧~ 是 BlackBerry 8800 的默认主题. 想不到吧, 我最喜欢的主题竟然是这个~~ 我把好些可以用快捷键进入和对我没用的图标都隐藏了, 呵呵, 简约美呀!!

    最后呢, 就来介绍下现在我装的软件吧.

    • Opera Mini 国际版 + 中国版
      这个就不用多说了吧, 官方版本, 爽!!
    • BerryDict
      采用 Dict.cn 来进行在线查询的简约版字典.
      可自选 cmnet/cmwap 接入点
    • TwitterBerry
      个人最喜欢的黑莓 Twitter 客户端
    • BBFetion && BerryMail-Fetion2
      前者可以直接通过代理连接, 即使你的 APN 是 cmnet, 界面简单, 功能也简单
      后者只能当 APN 为 cmwap 的时候使用, 界面豪华, 功能也豪华
    • Google Mobile App
      Google 手机软件管理程序, 包括 Gmail, Google Map, Google Sync(手机联系人与 Gmail 及手机日程及 Google Calendar 同步的软件
    • gCalc!
      一款无比强大的计算器.. 可以绘图!!
    • Documents To Go
      可以让黑莓打开并编辑 doc/ppt/xls 文件.. 强大无比..
    • BBFileScout
      一款超级强大的黑莓文件管理器, 可以压缩及解压 zip 文件
    • JadBuilder
      根据存储卡中的 cod 文件生成 jad 文件保存在相应目录

    历史上用过的软件其实有很多, 但是, 能在历经半年后存活下来的, 就只有这些了, 可见, 是精品软件来的啊!!(自恋 + 自大..)

    入手半年了才写关于黑莓的文章.. 呼.. 总算又了了一件事, 最后呢, 给大家看张夜景吧~ 撤退咯.

    BlackBerry 8800

  • 虚拟桌面工具-Simpledesktops

    类似Linux 的 多桌面。

     用法好简单:热键ALT + 【1,2,3,4】对应4 个桌面。

  • IIS 6 关于 Service Unavailable 解决方法

    今收到用户疑问,网页打不开 出现Service Unavailable 提示,解决方法:临时对应开启被自动禁用的应用程序池,后续优化网站。

     

    问:为什么我的网站有时会出现"Service Unavailable"的提示。
      答: 出现这种情况是由于您的网站超过了系统资源限制或者是IIS连接数(由于我们虚拟主机是采用2003的操作系统,2003的操作系统在提示IIS过多时并非像2000系统提示"链接人数过多",而是提示"Service Unavailable",如果出现这种情况,您可以选择升级主机,或者查看是否有人盗链。

      可以经常更改您放软件、图片的目录)造成的,主要是程序占用资源太多。比如同样是100人在线的论坛,雷傲论坛所占的资源就是动网论坛所占资源的10倍以上;另外,一些死循环程序,或者不优化的程序都会占用太多的系统资源,而系统资源明显是有限的。由于我们的新虚拟主机是采用WINDOWS2003的操作系统,各网站之间是以独立进程运行的,不会相互影响。

      如果一个网站的程序占资源太多或者发生太多的错误,系统日志就会提示:"应用程序池 'west263pool11' 被自动禁用,原因是为此应用程序池提供服务的进程中出现一系列错误,或者提示:应用程序池 'hui999' 超过了其作业限制设置。有关更多信息,请参阅在 http://go.microsoft.com/fwlink/events.asp 的帮助和支持中心。 这时,访问这个网站就会提示:Service Unavailable。一般系统会在30秒左右恢复正常,多刷新几次就能正常访问了。

      如果经常出现类似的错误,请及时优化网站程序,或者升级你的虚拟主机至更高的款型,以获得更多的系统资源。

  • Oracle Temporary Tables(Oracle 临时表)

    Temporary Tables临时表
    1简介
       ORACLE数据库除了可以保存永久表外,还可以建立临时表temporary tables。这些临时表用来保存一个会话SESSION的数据,或者保存在一个事务中需要的数据。当会话退出或者用户提交commit和回滚 rollback事务的时候,临时表的数据自动清空,但是临时表的结构以及元数据还存储在用户的数据字典中。
       临时表只在oracle8i以及以上产品中支持。
    2详细介绍
       Oracle临时表分为 会话级临时表 和 事务级临时表。
    会话级临时表是指临时表中的数据只在会话生命周期之中存在,当用户退出会话结束的时候,Oracle自动清除临时表中数据。
    事务级临时表是指临时表中的数据只在事务生命周期中存在。当一个事务结束(commit or rollback),Oracle自动清除临时表中数据。
    临时表中的数据只对当前Session有效,每个Session都有自己的临时数据,并且不能访问其它Session的临时表中的数据。因此,临时表不需要DML锁。
       当一个会话结束(用户正常退出 用户不正常退出 ORACLE实例崩溃)或者一个事务结束的时候,Oracle对这个会话的表执行 TRUNCATE 语句清空临时表数据.但不会清空其它会话临时表中的数据.
    你可以索引临时表和在临时表基础上建立视图.同样,建立在临时表上的索引也是临时的,也是只对当前会话或者事务有效.   临时表可以拥有触发器.
    3建立临时表
       临时表的定义对所有会话SESSION都是可见的,但是表中的数据只对当前的会话或者事务有效.
       建立方法:
    1) ON COMMIT DELETE ROWS 定义了建立事务级临时表的方法.
    CREATE GLOBAL TEMPORARY TABLE admin_work_area
            (startdate DATE,
             enddate DATE,
             class CHAR(20))
          ON COMMIT DELETE ROWS;
    EXAMPLE:
    SQL> CREATE GLOBAL TEMPORARY TABLE admin_work_area
      2          (startdate DATE,
      3           enddate DATE,
      4           class CHAR(20))
      5        ON COMMIT DELETE ROWS;
    SQL> create table permernate( a number);
    SQL> insert into admin_work_area values(sysdate,sysdate,'temperary table');
    SQL> insert into permernate values(1);
    SQL> commit;
    SQL> select * from admin_work_area;
    SQL> select  * from permernate;
    A
    1
    2)ON COMMIT PRESERVE ROWS 定义了创建会话级临时表的方法.
    CREATE GLOBAL TEMPORARY TABLE admin_work_area
            (startdate DATE,
             enddate DATE,
             class CHAR(20))
         ON COMMIT PRESERVE ROWS;
    EXAMPLE:

    会话1:
    SQL> drop table admin_work_area;
    SQL> CREATE GLOBAL TEMPORARY TABLE admin_work_area
      2          (startdate DATE,
      3           enddate DATE,
      4           class CHAR(20))
      5       ON COMMIT PRESERVE ROWS;
    SQL> insert into permernate values(2);
    SQL> insert into admin_work_area values(sysdate,sysdate,'session temperary');
    SQL> commit;
    SQL> select * from permernate;

             A
    ----------
             1
             2

    SQL> select * from admin_work_area;

    STARTDATE  ENDDATE    CLASS
    ---------- ---------- --------------------
    17-1ÔÂ -03 17-1ÔÂ -03 session temperary

    会话2:

    SQL> select * from permernate;

             A
    ----------
             1
             2

    SQL> select * from admin_work_area;

    未选择行.

    会话2看不见会话1中临时表的数据.
    Posted May 13 2009, 10:04 AM by slash with no comments
    Filed under:
  • 某一类的数量占总数的比例的函数--ratio_to_report

    drop table test;

    create table test(code varchar2(10),qty number);

    insert into test values('a',1);
    insert into test values('a',3);
    insert into test values('b',2);
    insert into test values('b',3);
    insert into test values('b',4);
    insert into test values('c',3);
    insert into test values('c',3);
    insert into test values('c',2);

    select code,qty,ratio_to_report(qty) over( partition by code) from test;

     

    Result:

    a     1                                         0.25                                    
    a     3                                         0.75                                    
    b     2                                         0.222222222222222222222222222222222222222
    b     3                                         0.333333333333333333333333333333333333333
    b     4                                         0.444444444444444444444444444444444444444
    c     3                                         0.375                                   
    c     3                                         0.375                                   
    c     2                                         0.25                                    

     

    很好很强大!

  • 福布斯:美国年轻人最喜欢的15大网站

    福布斯网站发表文章指出,大约3/4的美国人都有自己的网上生活,每个人的网上生活千差万别,然而,最近市场调研公司Forrester Research的一份消费者网上生活调查显示,网上生活按年龄段不同显示出不同趋势。

    美国第X代,也就是在29岁到42之间的人群,一周里在网上花的个人时间大约有8小时,基本都是收发邮件或者浏览一些产品介绍等,而像博客、社交网络等用得很少。

      但是,大约占网民数80%的美国第Y代年轻人(18到28岁之间),他们每周大约有10多个小时的个人时间在网上度过,他们是网络最流行应用的体验者和促进者。博客、视频、社交网站,他们都是常客。以下是美国第Y代的年轻人最常去的15大网站:

      1,Hulu.com

      类型:视频

      在Hulu上,可以看到最流行、最热门的电视剧。但是,需要忍受一下视频上出现的广告。



      2,Songza.com

      类型:音乐

      Songza就像一个可以定制的广播电台,你可以输入你想听的歌曲的名字,Songza会把那些歌曲按播放列表排列出来并播放。可惜的是,你不能下载那些歌曲。



      3,Veoh.com

      类型:视频

      Veoh自2006年创立,迪斯尼前CEO迈克尔•埃斯纳(Michael Eisner)加入其董事会是当年的一大新闻。Veoh上不仅有用户们自己上传的视频,还有经过版权授权的视频,相比YouTube来说,Veoh做得更专业一些。



      4,Digg.com

      类型:社区型内容

      Digg创立于2004年,Digg的思路是让用户推荐自己感兴趣的内容,凭着这种独特的思路,这种社区型内容风靡全球。当你看到自己感兴趣的内容,只要"Digg"它就可以在社区内进行分享。



      5,Stumbleupon.com

      类型:社区型内容

      用户根据他们分享的内容,找到志同道合的圈子,这就是Stumbleupon的思路。Stumbleupon在2006年被eBay收购,从此用户数猛增。




    6,Thesixtyone.com

      类型:社区型音乐

      这是以美国著名的一条高速公路61命名的网站,旨在扮演"大众音乐发现者"的角色。音乐人可以在线提交自己的音乐作品,而网站用户可以根据自己 的喜好,通过给予"撞击(bump)"来评价音乐作品。但用户手中的"bump"必须通过参与一些活动才能获得,比如每天登录也能获得"bump"。 Thesixtyone.com首页的音乐家可能都不太有名,但都是由用户自己评选出来的。



      7,Friendfeed.com

      类型:信息聚合

      用户在很多社交网站上都有各自的Profiles,比如YouTube、StumbleUpon和Twitter等,要想管理这些 Profiles也是件麻烦的事情。Friendfeed就是专注于这些信息的聚合,它跟踪用户好友的实时动态,比如发表了新的博客、上传了新的视频等, 将这些信息不间断的报告给用户。Friendfeed成立于今年二月,但已经和Google、亚马逊、Digg等建立了合作关系。



      8,Viewzi.com

      类型:搜索

      Viewzi将Google和雅虎上最佳的搜索结果组合在一起,而且还可以还提供模板允许让用户设置搜索结果的显示方式。



      9,Twitter.com

      类型:社交网站

      微型博客Twitter可以让用户用简单的话告诉圈子里的人你正在做什么、想什么。当Twitter和手机结合的时候,这种实时更新显得更有用。



      10,Adultswim.com

      类型:办公室娱乐网站

      当你疲于工作的时候,给自己放松一下,Adultswim.com上有大量的搞笑短片以及有趣的网页游戏。




     11,Wegame.com

      类型:视频

      Wegame一定是游戏狂热分子的杰作,用户可以上传一些游戏视频片段供人欣赏。



      12,Funnyordie.com

      类型:视频

      Funnyordie上有很多用户自己上传的原创视频,另外,Funnyordie允许用户对某个视频给出"搞笑"或者"下架"的评价。



      13,Stitcher.com

      类型:网络收音机网站

      网络收音机在当今这个新媒体时代有点没有得到它该有的地位。然而据研究公司Forrester Research的数据,约35%的美国第Y代人都在收听这个网络收音机。



      14,Lifehacker.com

      类型:博客

      Lifehacker博客提供很多生活上的how-to指导,比如提供节约时间、金钱等建议。



      15,Gizmodo.com

      类型:博客

      根据市场调研公司Forrester Research研究,大部分美国人在购买一些大件商品之前都会在网上研究这些商品。Gizmodo就是提供电子产品的一些研究信息博客。(柯柯编译)

    Posted Apr 28 2009, 09:51 AM by slash with no comments
    Filed under:
  • Google Update 宣布开源

     在安装了谷歌拼音输入法、谷歌浏览器、谷歌软件精选后,通常会有一个名为“Google Update”的进程,用于及时更新谷歌的客户端软件。虽然,这省却了用户自己手动更新的麻烦,但却让不少用户厌烦和担忧——不仅占用内存,还会有隐私泄露的隐患。今天,为了消除用户的担忧,谷歌宣布将Google Update项目开源,项目名为奥马哈(Omaha)。

      谷歌在官方博客中表示:

      Google Update采用Apache License 2.0许可证,以奥马哈(Omaha)为代号发布源代码。Omaha的功能主要是在不干扰用户并且不中断功能的情况下自动更新,在后台自动检查有无更新,这一切都是为了让用户有更好的体验。

      现在,谷歌公开了Google Update源代码,允许用户可以自己控制更新进程。不过,具体怎么做,还得懂行的用户自己动手了……

  • 三种主要的嵌入式数据库

     什么是嵌入式数据库


    嵌入式数据库与非嵌入式数据库的差别,在于运行模式的差别。并不是运行在嵌入式手持设备上的数据库就

    是嵌入式数据库,那种数据库我们通常称做嵌入式移动数据库。理论上讲,嵌入式设备一样可以运行网络数

    据库的服务端程序。

    嵌入式数据库是指运行在本机上、不用启动服务端的轻型数据库,它与应用程序紧密集成,被应用程序所启

    动,并伴随应用程序的退出而终止。

    从这个意义上讲,似乎所有单机数据库都可以算嵌入式数据库,比如Access,Paradox,DBF等等,因为

    它们都不用启动数据库服务器即可使用。然而,我们通常不将上述数据库归入嵌入式数据库,而只将它们归

    入“桌面数据库”,甚至“文件型数据库”,因为这些数据库的完备性、存储容量及性能方面存在较大的缺陷。

    嵌入式数据库支持的数据都是TB文件级别,更由于嵌入式数据库具备高性能的特点,可以预测,单机数据库

    的未来将是嵌入式数据库的天下。

    嵌入式数据库三雄
    目前,嵌入式数据库市场主要由三个产品分割:SQLite,Birkeley DB,Firebird嵌入服务器版,巧的是,

    这三个数据库产品都是开源软件。

     

    SQLite

    主页:http://www.sqlite.org

    SQLite诞生于2000年5月,这几年增长势头迅猛无比,目前版本是3.3.8。

    SQLite的特点如下:

    1、无需安装配置,应用程序只需携带一个动态链接库。

    2、非常小巧,For Windows 3.3.8版本的DLL文件才374KB。

    3、ACID事务支持,ACID即原子性、一致性、隔离性、和持久性(Atomic、Consistent、Isolated、和

    Durable)。

    4、数据库文件可以在不同字节顺序的机器间自由的共享,比如可以直接从Windows移植到Linux或MAC。

    5、支持数据库大小至2TB。

     

     

    Berkeley DB

    主页:http://www.oracle.com/database/berkeley-db/index.html

    Berkeley DB是由美国Sleepycat Software公司开发的一套开放源码的嵌入式数据库的程序库,它于

    1991年发布,号称“为应用程序开发者提供工业级强度的数据库服务”,可谓是老牌悍将。Sleepycat现已被

    甲骨文(ORACLE)公司收购。

    Berkeley DB的特点如下:

    1、嵌入式,无需安装配置。

    2、为多种编程语言提供了API接口,其中包括C、C++、Java、Perl、Tcl、Python和PHP等等。

    3、轻便灵活。它可以运行于几乎所有的UNIX和Linux系统及其变种系统、Windows操作系统以及多种嵌

    入式实时操作系统之下。

    4、可伸缩。它的Database library才几百KB大小,但它能够管理规模高达256TB的数据库。它支持高并

    发度,成千上万个用户可同时操纵同一个数据库。

     

     

    Firebird嵌入服务器版(Embedded Server)

    主页:http://www.firebirdsql.org

    从Interbase开源衍生出的Firebird,充满了勃勃生机。虽然它的体积比前辈Interbase缩小了几十倍,但

    功能并无阉割。为了体现Firebird短小精悍的特色,开发小组在增加了超级服务器版本之后,又增加了嵌入

    版本,最新版本为2.0。

    Firebird的嵌入版有如下特色:

    1、数据库文件与Firebird网络版本完全兼容,差别仅在于连接方式不同,可以实现零成本迁移。

    2、数据库文件仅受操作系统的限制,且支持将一个数据库分割成不同文件,突破了操作系统最大文件的限

    制,提高了IO吞吐量。

    3、完全支持SQL92标准,支持大部分SQL-99标准功能。

    4、丰富的开发工具支持,绝大部分基于Interbase的组件,可以直接使用于Firebird。

    5、支持事务、存储过程、触发器等关系数据库的所有特性。

    6、可自己编写扩展函数(UDF)。

    嵌入式数据库特性对比
    产品名称

    SQLite

    Berkeley DB

    Firebird嵌入服务器版


    当前版本

    3.3.8

    4.5.20

    2.0


    速度

    最快






    稳定性








    数据库容量

    2TB

    256TB

    64TB


    SQL支持

    大部份SQL- 92

    不支持

    完全SQL-92与大部份SQL-99


    Win32平台下最小体积

    374KB

    840KB

    3.68MB


    数据操纵

    SQL

    仅应用程序接口

    SQL


    开发接口

    C, C++, PHP, Java, Delphi, Python .net(有些是第三方厂商开发的)

    从以上对比中,我们可以看到,最短小精悍的是SQLite,它的性能也是最高的,Berkeley DB比较特殊,

    因为它不是用SQL语言来操纵数据的,Firebird嵌入版的体积对比之下显得稍大了些,但它对关系数据库特

    性的支持是最好的,如果要考虑到今后或许要将数据库升级成网络版本,就要选Firebird了。

    嵌入式数据库开发布署举例
    例1、用Delphi开发基于SQLite的单机版应用程序

    因为SQLite自带C、C++、Java接口,所以我这里举用Delphi开发的例子。

    使用组件:第三方组件ASQLite。

    下载地址:http://www.aducom.com/cen/download.php?list.2

    选择后边是.D的,就是for Delphi的组件。

    下载解压之后,即可开始用Delphi打开dpk组件包编译安装。

    如果您的Delphi版本比较高,比如是Delphi 2006,那么就编译asqlite3.dpk与asqlite3pkg.dpk,之后

    再安装asqlite3pkg.dpk包即可。

    然后在Delphi组件面板里可以看得到ASQLite组件,如图:

     

    现在就可以用它开发你的程序了,基于SQLite小巧的原因,特别推荐你在布署远程应用时用它,比如监控啊

    ,木马啊什么的。

    OK,为了使我们的例子更详细,下面讲讲怎么用ASQLite组件创建、连接数据库,并建立一个数据表。

    新建一个窗体,放一个TASQLite3DB,命名为DB1,将它的Database(数据库)设为test.sqb,放一个

    TASQLite3Query,命名为Query1,将它的Connection指向DB1,然后放入两个按钮,第一个按钮的作

    用是创建或连接数据库,第二个按钮的作用是建立数据表。

    如下图:

     

    “连接”按钮事件的代码如下:

    DB1.Open;

    执行完这条语句之后,如果应用程序当前目录下不存在test.sqb文件,则自动创建并连接它。

    “建表”按钮事件的代码如下:

      Query1.StartTransaction;

      try

        Query1.Close;

        Query1.SQL.Text:='create table student(sname vchar(30),age integer)';

        Query1.ExecSQL;

        Query1.Commit;

      except

        Query1.RollBack;

      end;

    执行完这些语句之后,在Test.sqb数据库里将创建数据表student。

    现在,请将您下载的SQLite的动态链接库sqlite3.dll文件复制到应用程序目录下。

    之后,可正常运行。

    布署时,也应将sqlite3.dll文件一起打包。

    例2、使用C++语言开发基于Firebird嵌入版的应用程序。

    由于Firebird衍生于Interbase,所以Delphi对它的支持最好,IBX,DBX,Fibplus等都可以直接使用它

    ,只要注意将接口文件改为fbembed.dll即可。在此不再多言。

    对于C++这种最通用的语言,我们有一个更好的组件可以选择:IBPP。

    IBPP主页:http://www.ibpp.org/

    IBPP是用C++封装的Firebird接口,最新版本2.5.2.2。

    只要在C++里引用all_in_one.cpp文件,就可以使用它的功能。

    可以用IBPP:atabase类连接数据库,用IBPP::Transaction类控制事务,IBPP::Statement类可以获取

    数据集。下面展示一段代码,功能为:先连接d:\demo.fdb文件,然后从student表里选择所有记录,遍历

    所有记录之后,显示最后一条记录的sn与 sname字段。为了使演示更直观,省去了异常处理。

    示例代码:

    #define IBPP_WINDOWS  //运行于Windows平台的预先声明

    #include "ibpp/all_in_one.cpp"

    ……

    IBPP:atabase db1;

    db1=IBPP:atabaseFactory("","d:/demo.fdb","sysdba","masterkey";

    db1->Connect();

    IBPP::Transaction tr1=IBPP::TransactionFactory(db1,IBPP::amWrite,

    IBPP::ilConcurrency,IBPP::lrWait, IBPP::tfNoAutoUndo);

    tr1->Start();

    IBPP::Statement st1=IBPP::StatementFactory(db1,tr1);

    st1->Prepare("select * from student";

    st1->Execute();

    std::string sn,sname;

    st1->Fetch();

    st1->Get(1,sn);

    st1->Get(2,sname);

    tr1->Commit();

    Label1->Caption=sn.c_str();

    Label2->Caption=sname.c_str();

    最后2行语句为显示结果的,不同开发平台应该使用不同的方法演示,请勿直接复制源代码。

    布署时,还应该带上如下文件:fbembed.dll,ib_util.dll,icudt30.dll,icuin30.dll,icuuc30.dll,为

    了更通用,还可以带上VC++ 7.1的运行库msvcp71.dll,msvcr71.dll两个文件。

  • substr substrb

    substr与substrb 字符串截取函数

     substr 按字符来截取,substrb按字节来截取。多字节字符集下需要注意。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    
     
    
     SCOTT>select substr('abcdefg',1,3) from dual;
    
    SUBSTR('ABCDEFG',1,3)                                                           
    ----------------------------------------------------------------                
    abc                                                                              
    
    SCOTT>select substrb('abcdefg',1,3) from dual;
    
    SUBSTRB('ABCDEFG',1,3)                                                          
    --------------------------------                                                
    abc                                                                              
    
    SCOTT>select substr('中华人民共和国',1,3) from dual;
    
    SUBSTR('中华人民共和国',1,3)                                                    
    ----------------------------------------------------------------                
    中华人                                                                            
    
    SCOTT>select substrb('中华人民共和国',1,3) from dual;
    
    SUBSTRB('中华人民共和国',1,3)                                                   
    --------------------------------                                                
    中     

     

    类似的还有,

    length与lengthb 长度计算函数 

    Instr与Instrb 字符串查找函数 instr(原字符串,查的字符串,起始位置,第几个匹配) 返回字符串位置,找不到返回0 .

    Posted Apr 02 2009, 10:45 AM by slash with no comments
    Filed under: ,
  • 存在就更新,不存在就插入的sql语句实现1

     存在就更新,不存在就插入的sql语句实现

    语法为:  
    MERGE INTO table  
    USING data_source  
    ON (condition)  
    WHEN MATCHED THEN update_clause  
    WHEN NOT MATCHED THEN insert_clause;



    CREATE TABLE bonuses (employee_id NUMBER, bonus NUMBER DEFAULT 100);

    INSERT INTO bonuses(employee_id)
       (SELECT e.employee_id FROM employees e, orders o
       WHERE e.employee_id = o.sales_rep_id
       GROUP BY e.employee_id);

    SELECT * FROM bonuses;

    EMPLOYEE_ID      BONUS
    ----------- ----------
            153        100
            154        100
            155        100
            156        100
            158        100
            159        100
            160        100
            161        100
            163        100

    MERGE INTO bonuses D
       USING (SELECT employee_id, salary, department_id FROM employees WHERE department_id = 80) S
       ON (D.employee_id = S.employee_id)
       WHEN MATCHED THEN UPDATE SET D.bonus = D.bonus + S.salary*.01
         DELETE WHERE (S.salary > 8000)
       WHEN NOT MATCHED THEN INSERT (D.employee_id, D.bonus)
         VALUES (S.employee_id, S.salary*0.1)
         WHERE (S.salary <= 8000);

    EMPLOYEE_ID      BONUS
    ----------- ----------
            153        180
            154        175
            155        170
            159        180
            160        175
            161        170
            179        620
            173        610
            165        680
            166        640
            164        720
            172        730
            167        620
            171        740

    Posted Mar 11 2009, 03:09 PM by slash with no comments
    Filed under:
  • Oracle SQL Loader的语法1

    SQL*LOADER是ORACLE的数据加载工具,通常用来将操作系统文件迁移到ORACLE数据库中。SQL*LOADER是大型数据
    仓库选择使用的加载方法,因为它提供了最快速的途径(DIRECT,PARALLEL)。现在,我们抛开其理论不谈,用实例来使您快速掌握SQL*LOADER的使用方法。
    首先,我们认识一下SQL*LOADER。
    在NT下,SQL*LOADER的命令为SQLLDR,在UNIX下一般为sqlldr/sqlload。
    如执行:d:\oracle>sqlldr
    SQL*Loader: Release 8.1.6.0.0 - Production on 星期二 1月 8 11:06:42 2002
    (c) Copyright 1999 Oracle Corporation. All rights reserved.
    用法: SQLLOAD 关键字 = 值 [,keyword=value,…]
    有效的关键字:
    userid -- ORACLE username/password
    control -- Control file name
    log -- Log file name
    bad -- Bad file name
    data -- Data file name
    discard -- Discard file name
    discardmax -- Number of discards to allow (全部默认)
    skip -- Number of logical records to skip (默认0)
    load -- Number of logical records to load (全部默认)
    errors -- Number of errors to allow (默认50)
    rows -- Number of rows in conventional path bind array or between direct path data saves
    (默认: 常规路径 64, 所有直接路径)
    bindsize -- Size of conventional path bind array in bytes(默认65536)
    silent -- Suppress messages during run (header,feedback,errors,discards,partitions)
    direct -- use direct path (默认FALSE)
    parfile -- parameter file: name of file that contains parameter specifications
    parallel -- do parallel load (默认FALSE)
    file -- File to allocate extents from
    skip_unusable_indexes -- disallow/allow unusable indexes or index partitions(默认FALSE)
    skip_index_maintenance -- do not maintain indexes, mark affected indexes as unusable(默认FALSE)
    commit_discontinued -- commit loaded rows when load is discontinued(默认FALSE)
    readsize -- Size of Read buffer (默认1048576)
    PLEASE NOTE: 命令行参数可以由位置或关键字指定
    。前者的例子是 ’sqlload scott/tiger foo’;后者的例子是 ’sqlload control=foo userid=scott/tiger’.位置指定参数的时间必须早于但不可迟于由关键字指定的参数。
    例如,’SQLLOAD SCott/tiger control=foo logfile=log’, 但’不允许

    sqlload scott/tiger control=foo log’,即使允许 参数 ‘log’ 的位置正确。
    d:\oracle>
    我们可以从中看到一些基本的帮助信息,这里,我用到的是中文的WIN2000 ADV SERVER。
    我们知道,SQL*LOADER只能导入纯文本,所以我们现在开始以实例来讲解其用法。
    一、已存在数据源result.csv,欲倒入ORACLE中FANCY用户下。
    result.csv内容:
    1,默认 Web 站点,192.168.2.254:80:,RUNNING
    2,other,192.168.2.254:80:test.com,STOPPED
    3,third,192.168.2.254:81:thirdabc.com,RUNNING
    从中,我们看出4列,分别以逗号分隔,为变长字符串。
    二、制定控制文件result.ctl
    result.ctl内容:
    load data
    infile 'result.csv'
    into table resultxt
    (resultid char terminated by ',',
    website char terminated by ',',
    ipport char terminated by ',',
    status char terminated by whitespace)
    说明:
    infile 指数据源文件 这里我们省略了默认的 discardfile result.dsc badfile result.bad
    into table resultxt 默认是INSERT,也可以into table resultxt APPEND为追加方式,或REPLACE
    terminated by ‘,’ 指用逗号分隔
    terminated by whitespace 结尾以空白分隔
    三、此时我们执行加载:
    D:\>sqlldr userid=fancy/testpass control=result.ctl log=resulthis.out
    SQL*Loader: Release 8.1.6.0.0 - Production on 星期二 1月 8 10:25:42 2002
    (c) Copyright 1999 Oracle Corporation. All rights reserved.
    SQL*Loader-941: 在描述表RESULTXT时出现错误
    ORA-04043: 对象 RESULTXT 不存在
    提示出错,因为数据库没有对应的表。
    四、在数据库建立表
    create table resultxt
    (resultid varchar2(500),
    website varchar2(500),
    ipport varchar2(500),
    status varchar2(500))
    /
    五、重新执行加载
    D:\>sqlldr userid=fancy/k1i7l6l8 control=result.ctl log=resulthis.out
    SQL*Loader: Release 8.1.6.0.0 - Production on 星期二 1月 8 10:31:57 2002
    (c) Copyright 1999 Oracle Corporation. All rights reserved.
    达到提交点,逻辑记录计数2
    达到提交点,逻辑记录计数3
    已经成功!我们可以通过日志文件来分析其过程:resulthis.out内容如下:
    SQL*Loader: Release 8.1.6.0.0 - Production on 星期二 1月 8 10:31:57 2002
    (c) Copyright 1999 Oracle Corporation. All rights reserved.
    控制文件: result.ctl
    数据文件: result.csv
    错误文件: result.bad
    废弃文件: 未作指定
    :
    (可废弃所有记录)
    装载数: ALL
    跳过数: 0
    允许的错误: 50
    绑定数组: 64 行,最大 65536 字节
    继续: 未作指定
    所用路径: 常规
    表RESULTXT
    已载入从每个逻辑记录
    插入选项对此表INSERT生效
    列名 位置 长度 中止 包装数据类型
    ---------- ---- -- -- -- -------
    RESULTID FIRST * , CHARACTER
    WEBSITE NEXT * , CHARACTER
    IPPORT NEXT * , CHARACTER
    STATUS NEXT * WHT CHARACTER
    表RESULTXT:
    3 行载入成功
    由于数据错误, 0 行没有载入。
    由于所有 WHEN 子句失败, 0 行没有载入。
    由于所有字段都为空的, 0 行没有载入。
    为结合数组分配的空间: 65016字节(63行)
    除绑定数组外的内存空间分配: 0字节
    跳过的逻辑记录总数: 0
    读取的逻辑记录总数: 3
    拒绝的逻辑记录总数: 0
    废弃的逻辑记录总数: 0
    从星期二 1月 08 10:31:57 2002开始运行
    在星期二 1月 08 10:32:00 2002处运行结束
    经过时间为: 00: 00: 02.70
    CPU 时间为: 00: 00: 00.10(可
    六、并发操作
    sqlldr userid=/ control=result1.ctl direct=true parallel=true
    sqlldr userid=/ control=result2.ctl direct=true parallel=true
    sqlldr userid=/ control=result2.ctl direct=true parallel=true
    当加载大量数据时(大约超过10GB),最好抑制日志的产生:
    SQL>ALTER TABLE RESULTXT nologging;
    这样不产生REDO LOG,可以提高效率。然后在CONTROL文件中load data上面加一行:unrecoverable 此选项必须要与DIRECT共同应用。
    在并发操作时,ORACLE声称可以达到每小时处理100GB数据的能力!其实,估计能到1-10G就算不错了,开始可用结构 相同的文件,但只有少量数据,成功后开始加载大量数据,这样可以避免时间的浪费。
    有关SQLLDR的问题
    控制文件:input.ctl,内容如下:
    load data           --1、控制文件标识
    infile 'test.txt'       --2、要输入的数据文件名为test.txt
    append into table test    --3、向表test中追加记录
    fields terminated by X'09'  --4、字段终止于X'09',是一个制表符(TAB)
    (id,username,password,sj)   -----定义列对应顺序

    其中append为数据装载方式,还有其他选项:
    a、insert,为缺省方式,在数据装载开始时要求表为空
    b、append,在表中追加新记录
    c、replace,删除旧记录,替换成新装载的记录
    d、truncate,
    删除旧记录,替换成新装载的记录

  • Oracle SQL*Loader 使用指南

    SQL*Loader是Oracle数据库导入外部数据的一个工具.它和DB2的Load工具相似,但有更多的选择,它支持变化的加载模式,

    可选的加载及多表加载.
    如何使用 SQL*Loader 工具
    我们可以用Oracle的sqlldr工具来导入数据。例如:
    sqlldr scott/tiger control=loader.ctl
    控制文件(loader.ctl) 将加载一个外部数据文件(含分隔符). loader.ctl如下:
    load data
    infile 'c:\data\mydata.csv'
    into table emp
    fields terminated by "," optionally enclosed by '"'
    ( empno, empname, sal, deptno )

    mydata.csv 如下:
    10001,"Scott Tiger", 1000, 40
    10002,"Frank Naude", 500, 20
    下面是一个指定记录长度的示例控制文件。"*" 代表数据文件与此文件同名,即在后面使用BEGINDATA段来标识数据。
    load data
    infile *
    replace
    into table departments
    ( dept position (02:05) char(4),
    deptname position (08:27) char(20)
    )
    begindata
    COSC COMPUTER SCIENCE
    ENGL ENGLISH LITERATURE
    MATH MATHEMATICS
    POLY POLITICAL SCIENCE

    Unloader这样的工具
    Oracle 没有提供将数据导出到一个文件的工具。但是,我们可以用SQL*Plus的select 及 format 数据来输出到一个文件


    set echo off newpage 0 space 0 pagesize 0 feed off head off trimspool on
    spool oradata.txt
    select col1 || ',' || col2 || ',' || col3
    from tab1
    where col2 = 'XYZ';
    spool off

    另外,也可以使用使用 UTL_FILE PL/SQL 包处理:
    rem Remember to update initSID.ora, utl_file_dir='c:\oradata' parameter
    declare
    fp utl_file.file_type;
    begin
    fp := utl_file.fopen('c:\oradata','tab1.txt','w');
    utl_file.putf(fp, '%s, %s\n', 'TextField', 55);
    utl_file.fclose(fp);
    end;
    /

    当然你也可以使用第三方工具,如SQLWays ,TOAD for Quest等。

    加载可变长度或指定长度的记录
    如:
    LOAD DATA
    INFILE *
    INTO TABLE load_delimited_data
    FIELDS TERMINATED BY "," OPTIONALLY ENCLOSED BY '"'
    TRAILING NULLCOLS
    ( data1,
    data2
    )
    BEGINDATA
    11111,AAAAAAAAAA
    22222,"A,B,C,D,"

    下面是导入固定位置(固定长度)数据示例:
    LOAD DATA
    INFILE *
    INTO TABLE load_positional_data
    ( data1 POSITION(1:5),
    data2 POSITION(6:15)
    )
    BEGINDATA
    11111AAAAAAAAAA
    22222BBBBBBBBBB

    跳过数据行:
    可以用 "SKIP n" 关键字来指定导入时可以跳过多少行数据。如:
    LOAD DATA
    INFILE *
    INTO TABLE load_positional_data
    SKIP 5
    ( data1 POSITION(1:5),
    data2 POSITION(6:15)
    )
    BEGINDATA
    11111AAAAAAAAAA
    22222BBBBBBBBBB

    导入数据时修改数据:
    在导入数据到数据库时,可以修改数据。注意,这仅适合于常规导入,并不适合 direct导入方式.如:
    LOAD DATA
    INFILE *
    INTO TABLE modified_data
    ( rec_no "my_db_sequence.nextval",
    region CONSTANT '31',
    time_loaded "to_char(SYSDATE, 'HH24:MI')",
    data1 POSITION(1:5) ":data1/100",
    data2 POSITION(6:15) "upper(:data2)",
    data3 POSITION(16:22)"to_date(:data3, 'YYMMDD')"
    )
    BEGINDATA
    11111AAAAAAAAAA991201
    22222BBBBBBBBBB990112

    LOAD DATA
    INFILE 'mail_orders.txt'
    BADFILE 'bad_orders.txt'
    APPEND
    INTO TABLE mailing_list
    FIELDS TERMINATED BY ","
    ( addr,
    city,
    state,
    zipcode,
    mailing_addr "decode(:mailing_addr, null, :addr, :mailing_addr)",
    mailing_city "decode(:mailing_city, null, :city, :mailing_city)",
    mailing_state
    )

    将数据导入多个表:
    如:
    LOAD DATA
    INFILE *
    REPLACE
    INTO TABLE emp
    WHEN empno != ' '
    ( empno POSITION(1:4) INTEGER EXTERNAL,
    ename POSITION(6:15) CHAR,
    deptno POSITION(17:18) CHAR,
    mgr POSITION(20:23) INTEGER EXTERNAL
    )
    INTO TABLE proj
    WHEN projno != ' '
    ( projno POSITION(25:27) INTEGER EXTERNAL,
    empno POSITION(1:4) INTEGER EXTERNAL
    )

    导入选定的记录:
    如下例: (01) 代表第一个字符, (30:37) 代表30到37之间的字符:
    LOAD DATA
    INFILE 'mydata.dat' BADFILE 'mydata.bad' DISCARDFILE 'mydata.dis'
    APPEND
    INTO TABLE my_selective_table
    WHEN (01) <> 'H' and (01) <> 'T' and (30:37) = '19991217'
    (
    region CONSTANT '31',
    service_key POSITION(01:11) INTEGER EXTERNAL,
    call_b_no POSITION(12:29) CHAR
    )

    导入时跳过某些字段:
    可用 POSTION(x:y) 来分隔数据. 在Oracle8i中可以通过指定 FILLER 字段实现。FILLER 字段用来跳过、忽略导入数据文

    件中的字段.如:
    LOAD DATA
    TRUNCATE INTO TABLE T1
    FIELDS TERMINATED BY ','
    ( field1,
    field2 FILLER,
    field3
    )

    导入多行记录:
    可以使用下面两个选项之一来实现将多行数据导入为一个记录:

    CONCATENATE: - use when SQL*Loader should combine the same number of physical records together to form one

    logical record.

    CONTINUEIF - use if a condition indicates that multiple records should be treated as one. Eg. by having a

    '#' character in column 1.

    SQL*Loader 数据的提交:
    一般情况下是在导入数据文件数据后提交的。
    也可以通过指定 ROWS= 参数来指定每次提交记录数。

    提高 SQL*Loader 的性能:
    1) 一个简单而容易忽略的问题是,没有对导入的表使用任何索引和/或约束(主键)。如果这样做,甚至在使用ROWS=参数时

    ,会很明显降低数据库导入性能。
    2) 可以添加 DIRECT=TRUE来提高导入数据的性能。当然,在很多情况下,不能使用此参数。
    3) 通过指定 UNRECOVERABLE选项,可以关闭数据库的日志。这个选项只能和 direct 一起使用。
    4) 可以同时运行多个导入任务.

    常规导入与direct导入方式的区别:
    常规导入可以通过使用 INSERT语句来导入数据。Direct导入可以跳过数据库的相关逻辑(DIRECT=TRUE),而直接将数据导

    入到数据文件中。


    ==========================================================================================================

    sql load的一点小总结
     
    sqlldr userid=lgone/tiger control=a.ctl
    LOAD DATA
    INFILE 't.dat' // 要导入的文件
    // INFILE 'tt.date' // 导入多个文件
    // INFILE * // 要导入的内容就在control文件里 下面的BEGINDATA后面就是导入的内容
     
    INTO TABLE table_name // 指定装入的表
    BADFILE 'c:\bad.txt' // 指定坏文件地址
     
    ************* 以下是4种装入表的方式
    APPEND // 原先的表有数据 就加在后面
    // INSERT // 装载空表 如果原先的表有数据 sqlloader会停止 默认值
    // REPLACE // 原先的表有数据 原先的数据会全部删除
    // TRUNCATE // 指定的内容和replace的相同 会用truncate语句删除现存数据
     
    ************* 指定的TERMINATED可以在表的开头 也可在表的内部字段部分
    FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"'
    // 装载这种数据: 10,lg,"""lg""","lg,lg"
    // 在表中结果: 10 lg "lg" lg,lg
    // TERMINATED BY X '09' // 以十六进制格式 '09' 表示的
    // TERMINATED BY WRITESPACE // 装载这种数据: 10 lg lg
     
    TRAILING NULLCOLS ************* 表的字段没有对应的值时允许为空
     
    ************* 下面是表的字段
    (
    col_1 , col_2 ,col_filler FILLER // FILLER 关键字 此列的数值不会被装载
    // 如: lg,lg,not 结果 lg lg
    )
    // 当没声明FIELDS TERMINATED BY ',' 时
    // (
    // col_1 [interger external] TERMINATED BY ',' ,
    // col_2 [date "dd-mon-yyy"] TERMINATED BY ',' ,
    // col_3 [char] TERMINATED BY ',' OPTIONALLY ENCLOSED BY 'lg'
    // )
    // 当没声明FIELDS TERMINATED BY ','用位置告诉字段装载数据
    // (
    // col_1 position(1:2),
    // col_2 position(3:10),
    // col_3 position(*:16), // 这个字段的开始位置在前一字段的结束位置
    // col_4 position(1:16),
    // col_5 position(3:10) char(8) // 指定字段的类型
    // )
     
    BEGINDATA // 对应开始的 INFILE * 要导入的内容就在control文件里
    10,Sql,what
    20,lg,show
     
    =====================================================================================
    //////////// 注意begindata后的数值前面不能有空格
     
    1 ***** 普通装载
    LOAD DATA
    INFILE *
    INTO TABLE DEPT
    REPLACE
    FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"'
    (DEPTNO,
    DNAME,
    LOC
    )
    BEGINDATA
    10,Sales,"""USA"""
    20,Accounting,"Virginia,USA"
    30,Consulting,Virginia
    40,Finance,Virginia
    50,"Finance","",Virginia // loc 列将为空
    60,"Finance",,Virginia // loc 列将为空
     
    2 ***** FIELDS TERMINATED BY WHITESPACE 和 FIELDS TERMINATED BY x'09' 的情况
    LOAD DATA
    INFILE *
    INTO TABLE DEPT
    REPLACE
    FIELDS TERMINATED BY WHITESPACE
    -- FIELDS TERMINATED BY x'09'
    (DEPTNO,
    DNAME,
    LOC
    )
    BEGINDATA
    10 Sales Virginia
     
    3 ***** 指定不装载那一列
    LOAD DATA
    INFILE *
    INTO TABLE DEPT
    REPLACE
    FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"'
    ( DEPTNO,
    FILLER_1 FILLER, // 下面的 "Something Not To Be Loaded" 将不会被装载
    DNAME,
    LOC
    )
    BEGINDATA
    20,Something Not To Be Loaded,Accounting,"Virginia,USA"
     
    4 ***** position的列子
    LOAD DATA
    INFILE *
    INTO TABLE DEPT
    REPLACE
    ( DEPTNO position(1:2),
    DNAME position(*:16), // 这个字段的开始位置在前一字段的结束位置
    LOC position(*:29),
    ENTIRE_LINE position(1:29)
    )
    BEGINDATA
    10Accounting Virginia,USA
     
    5 ***** 使用函数 日期的一种表达 TRAILING NULLCOLS的使用
    LOAD DATA
    INFILE *
    INTO TABLE DEPT
    REPLACE
    FIELDS TERMINATED BY ','
    TRAILING NULLCOLS // 其实下面的ENTIRE_LINE在BEGINDATA后面的数据中是没有直接对应
    // 的列的值的 如果第一行改为 10,Sales,Virginia,1/5/2000,, 就不用TRAILING NULLCOLS了
    (DEPTNO,
    DNAME "upper(:dname)", // 使用函数
    LOC "upper(:loc)",
    LAST_UPDATED date 'dd/mm/yyyy', // 日期的一种表达方式 还有'dd-mon-yyyy' 等
    ENTIRE_LINE ":deptno||:dname||:loc||:last_updated"
    )
    BEGINDATA
    10,Sales,Virginia,1/5/2000
    20,Accounting,Virginia,21/6/1999
    30,Consulting,Virginia,5/1/2000
    40,Finance,Virginia,15/3/2001
     
    6 ***** 使用自定义的函数 // 解决的时间问题
    create or replace
    function my_to_date( p_string in varchar2 ) return date
    as
    type fmtArray is table of varchar2(25);
     
    l_fmts fmtArray := fmtArray( 'dd-mon-yyyy', 'dd-month-yyyy',
    'dd/mm/yyyy',
    'dd/mm/yyyy hh24:mi:ss' );
    l_return date;
    begin
    for i in 1 .. l_fmts.count
    loop
    begin
    l_return := to_date( p_string, l_fmts(i) );
    exception
    when others then null;
    end;
    EXIT when l_return is not null;
    end loop;
     
    if ( l_return is null )
    then
    l_return :=
    new_time( to_date('01011970','ddmmyyyy') + 1/24/60/60 *
    p_string, 'GMT', 'EST' );
    end if;
     
    return l_return;
    end;
    /
     
    LOAD DATA
    INFILE *
    INTO TABLE DEPT
    REPLACE
    FIELDS TERMINATED BY ','
    TRAILING NULLCOLS
    (DEPTNO,
    DNAME "upper(:dname)",
    LOC "upper(:loc)",
    LAST_UPDATED "my_to_date( :last_updated )" // 使用自定义的函数
    )
    BEGINDATA
    10,Sales,Virginia,01-april-2001
    20,Accounting,Virginia,13/04/2001
    30,Consulting,Virginia,14/04/2001 12:02:02
    40,Finance,Virginia,987268297
    50,Finance,Virginia,02-apr-2001
    60,Finance,Virginia,Not a date
     
    7 ***** 合并多行记录为一行记录
    LOAD DATA
    INFILE *
    concatenate 3 // 通过关键字concatenate 把几行的记录看成一行记录
    INTO TABLE DEPT
    replace
    FIELDS TERMINATED BY ','
    (DEPTNO,
    DNAME "upper(:dname)",
    LOC "upper(:loc)",
    LAST_UPDATED date 'dd/mm/yyyy'
    )
    BEGINDATA
    10,Sales, // 其实这3行看成一行 10,Sales,Virginia,1/5/2000
    Virginia,
    1/5/2000
    // 这列子用 continueif list="," 也可以
    告诉sqlldr在每行的末尾找逗号 找到逗号就把下一行附加到上一行
     
    LOAD DATA
    INFILE *
    continueif this(1:1) = '-' // 找每行的开始是否有连接字符 - 有就把下一行连接为一行
    // 如 -10,Sales,Virginia,
    // 1/5/2000 就是一行 10,Sales,Virginia,1/5/2000
    // 其中1:1 表示从第一行开始 并在第一行结束 还有continueif next 但continueif list最理想
    INTO TABLE DEPT
    replace
    FIELDS TERMINATED BY ','
    (DEPTNO,
    DNAME "upper(:dname)",
    LOC "upper(:loc)",
    LAST_UPDATED date 'dd/mm/yyyy'
    )
    BEGINDATA // 但是好象不能象右面的那样使用
    -10,Sales,Virginia, -10,Sales,Virginia,
    1/5/2000 1/5/2000
    -40, 40,Finance,Virginia,13/04/2001
    Finance,Virginia,13/04/2001
     
    8 ***** 载入每行的行号
     
    load data
    infile *
    into table t
    replace
    ( seqno RECNUM //载入每行的行号
    text Position(1:1024))
    BEGINDATA
    fsdfasj //自动分配一行号给载入 表t 的seqno字段 此行为 1
    fasdjfasdfl // 此行为 2 ...
     
    9 ***** 载入有换行符的数据
    注意: unix 和 windows 不同 \\n & /n
    &lt; 1 > 使用一个非换行符的字符
    LOAD DATA
    INFILE *
    INTO TABLE DEPT
    REPLACE
    FIELDS TERMINATED BY ','
    TRAILING NULLCOLS
    (DEPTNO,
    DNAME "upper(:dname)",
    LOC "upper(:loc)",
    LAST_UPDATED "my_to_date( :last_updated )",
    COMMENTS "replace(:comments,'\n',chr(10))" // replace 的使用帮助转换换行符
    )
    BEGINDATA
    10,Sales,Virginia,01-april-2001,This is the Sales\nOffice in Virginia
    20,Accounting,Virginia,13/04/2001,This is the Accounting\nOffice in Virginia
    30,Consulting,Virginia,14/04/2001 12:02:02,This is the Consulting\nOffice in Virginia
    40,Finance,Virginia,987268297,This is the Finance\nOffice in Virginia
     
    &lt; 2 > 使用fix属性
    LOAD DATA
    INFILE demo17.dat "fix 101"
    INTO TABLE DEPT
    REPLACE
    FIELDS TERMINATED BY ','
    TRAILING NULLCOLS
    (DEPTNO,
    DNAME "upper(:dname)",
    LOC "upper(:loc)",
    LAST_UPDATED "my_to_date( :last_updated )",
    COMMENTS
    )
    demo17.dat
    10,Sales,Virginia,01-april-2001,This is the Sales
    Office in Virginia
    20,Accounting,Virginia,13/04/2001,This is the Accounting
    Office in Virginia
    30,Consulting,Virginia,14/04/2001 12:02:02,This is the Consulting
    Office in Virginia
    40,Finance,Virginia,987268297,This is the Finance
    Office in Virginia
     
    // 这样装载会把换行符装入数据库 下面的方法就不会 但要求数据的格式不同
     
    LOAD DATA
    INFILE demo18.dat "fix 101"
    INTO TABLE DEPT
    REPLACE
    FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"'
    TRAILING NULLCOLS
    (DEPTNO,
    DNAME "upper(:dname)",
    LOC "upper(:loc)",
    LAST_UPDATED "my_to_date( :last_updated )",
    COMMENTS
    )
    demo18.dat
    10,Sales,Virginia,01-april-2001,"This is the Sales
    Office in Virginia"
    20,Accounting,Virginia,13/04/2001,"This is the Accounting
    Office in Virginia"
    30,Consulting,Virginia,14/04/2001 12:02:02,"This is the Consulting
    Office in Virginia"
    40,Finance,Virginia,987268297,"This is the Finance
    Office in Virginia"
     
    &lt; 3 > 使用var属性
    LOAD DATA
    INFILE demo19.dat "var 3"
    // 3 告诉每个记录的前3个字节表示记录的长度 如第一个记录的 071 表示此记录有 71 个字节
    INTO TABLE DEPT
    REPLACE
    FIELDS TERMINATED BY ','
    TRAILING NULLCOLS
    (DEPTNO,
    DNAME "upper(:dname)",
    LOC "upper(:loc)",
    LAST_UPDATED "my_to_date( :last_updated )",
    COMMENTS
    )
    demo19.dat
    07110,Sales,Virginia,01-april-2001,This is the Sales
    Office in Virginia
    07820,Accounting,Virginia,13/04/2001,This is the Accounting
    Office in Virginia
    08730,Consulting,Virginia,14/04/2001 12:02:02,This is the Consulting
    Office in Virginia
    07140,Finance,Virginia,987268297,This is the Finance
    Office in Virginia
     
    &lt; 4 > 使用str属性
    // 最灵活的一中 可定义一个新的行结尾符 win 回车换行 : chr(13)||chr(10)
     
    此列中记录是以 a|\r\n 结束的
    select utl_raw.cast_to_raw('|'||chr(13)||chr(10)) from dual;
    结果 7C0D0A
     
    LOAD DATA
    INFILE demo20.dat "str X'7C0D0A'"
    INTO TABLE DEPT
    REPLACE
    FIELDS TERMINATED BY ','
    TRAILING NULLCOLS
    (DEPTNO,
    DNAME "upper(:dname)",
    LOC "upper(:loc)",
    LAST_UPDATED "my_to_date( :last_updated )",
    COMMENTS
    )
    demo20.dat
    10,Sales,Virginia,01-april-2001,This is the Sales
    Office in Virginia|
    20,Accounting,Virginia,13/04/2001,This is the Accounting
    Office in Virginia|
    30,Consulting,Virginia,14/04/2001 12:02:02,This is the Consulting
    Office in Virginia|
    40,Finance,Virginia,987268297,This is the Finance
    Office in Virginia|
     
    ==============================================================================
    象这样的数据 用 nullif 子句
     
    10-jan-200002350Flipper seemed unusually hungry today.
    10510-jan-200009945Sdivad over three meals.
     
    id position(1:3) nullif id=blanks // 这里可以是blanks 或者别的表达式
    // 下面是另一个列子 第一行的 1 在数据库中将成为 null
    LOAD DATA
    INFILE *
    INTO TABLE T
    REPLACE
    (n position(1:2) integer external nullif n='1',
    v position(3:8)
    )
    BEGINDATA
    1 10
    20lg
    ------------------------------------------------------------
     
    如果是英文的日志 格式,可能需要修改环境变量 nls_lang or nls_date_format

    ==========================================================================================================
    Posted Mar 04 2009, 11:30 AM by slash with no comments
    Filed under:
  • 用URL传参带特殊字符

     用URL传参带特殊字符,特殊字符丢失
        用URL传参数的时候,用&符号连接,如果某一个参数中含"#$ ^ & * + ="这些符号的时候,在另一个页面getParameter就会取不到传过来的参数。


      比如在a.jsp中,我要跳转到b.jsp,在a.jsp中这样写:
           b.jsp?Parameter1=wks&Parameter2=happycosn。
      如果Parameter1中含有"#$ ^ & * + ="这些特殊字符,在b.jsp中就得不到。并且,我在a.jsp中已经通过encode编码了,在b.jsp中也用decode 进行了解码。这时候,只要所传入的参数没有特殊字符都可以在b.jsp页面取到,但是有特殊字符就取不到了。 像这样:
         b.jsp?Parameter1=wks#bamboo&Parameter2=happycosn+#zl
      这时候,所取到的参数就不会有bamboo和zl。

    解决问题如下: 
    (下面也是找的一些资料,测试过没问题。)

    有些符号在URL中是不能直接传递的,如果要在URL中传递这些特殊符号,那么就要使用他们的编码了。编码的格式为:%加字符的ASCII码,即一个百分号%,后面跟对应字符的ASCII(16进制)码值。例如 空格的编码值是"%20"。
    如果不使用转义字符,这些编码就会当URL中定义的特殊字符处理。

    下表中列出了一些URL特殊符号及编码 十六进制值
    1.+ URL 中+号表示空格 %2B
    2.空格 URL中的空格可以用+号或者编码 %20
    3./ 分隔目录和子目录 %2F
    4.? 分隔实际的 URL 和参数 %3F
    5.% 指定特殊字符 %25
    6.# 表示书签 %23
    7.& URL 中指定的参数间的分隔符 %26
    8.= URL 中指定参数的值 %3D

  • Oracle 数据库的绑定变量特性及应用1

     关键词:

    绑定变量(binding variable),共享池(shared buffer pool), SGA(system global area);

    在开发一个数据库系统前,有谁对Oracle 系统了解很多,尤其是它的特性,好象很少吧;对初学者来讲,这更是不可能的事情

    ;仅仅简单掌握了SQL的写法,就开始了数据库的开发,其结果只能是开发一个没有效率,也没有可扩展的系统;
    因此,我写这个主题也是希望让大家更多地掌握Oracle数据库的特性,从而在架构一个新系统时,能考虑系统的可扩展,可伸

    缩性,也兼顾系统的效率和稳定;

    使用绑定变量是Oracle数据库的特性之一;于是大家要问,为什么使用,怎样使用,它的使用限制条件是什么?我会按照这样

    的想法去解答大家的疑问,我也会以举例子的方式来回答这些问题;

    1. 为什么使用绑定变量?
    这是解决Oracle应用程序可伸缩性的一个关键环节;而Oracle的共享池就决定了开发人员必须使用绑定变量;如果想要

    Oracle 运行减慢,甚至完全终止,那就可以不用绑定变量;
    这里举例说明上述问题;
    为了查询一个员工代号是123,你可以这样查询:
    select * from emp where empno=’123’;
    你也可以这样查询:
    select * from emp where empno=:empno;

    象我们往常一样,你查询员工’123’一次以后,有可能再也不用;接着你有可能查询员工’456’,然后查询’789’等等;如

    果查询使用象第一个查询语句,你每次查询都是一个新的查询(我们叫它硬编码的查询方法);因此,Oracle每次必须分析,解

    析,安全检查, 优化等等;
    第二个查询语句提供了绑定变量:empno,它的值在查询执行时提供,查询经过一次编译后,查询方案存储在共享池中,可以用

    来检索和重用;在性能和伸缩性方面,这两者的差异是巨大的,甚至是惊人的;通俗点讲,就不是一个级别;
    第一个查询使用的频率越高,所消耗的系统硬件资源越大,从而降低了用户的使用数量;它也会把优化好的其它查询语句从

    共享池中踢出;就象一个老鼠坏了一锅汤似的,系统的整体性能降低; 而执行绑定变量,提交相同对象的完全相同的查询的

    用户(这句话,大家听起来比较难理解,随后我会给出详细的解释),一次性使用就可重复使用,其效率不言耳语; 打个形象的

    比喻来说,第一个查询就象一次性使用的筷子,而第二个查询象是铁筷子,只要洗干净,张三李四都能用,合理有效地使用了

    资源;

    下面举例子去详细论证上述的问题,不使用绑定变量为生病状况:

    这是一个未使用的绑定变量(吃药前):


    set echo on;(把执行结果显示出来)
    alter system flush shared_pool;
    这条语句是清空共项池,每次都必须使用,确保共享池是空的,以提高执行效率;
    set timing on(打开记时器.)

    declare
    type rc is ref cursor;
    l_rc rc;
    l_dummy all_objects.object_name%type;
    l_start number default dbms_utility.get_time;
    begin
    for i in 1 .. 1000
    loop
    open l_rc for
    'select object_name
    from all_objects
    where object_id = ' || i;
    fetch l_rc into l_dummy;
    close l_rc;
    end loop;
    dbms_output.put_line
    ( round( (dbms_utility.get_time-l_start)/100, 2 ) ||
    ' seconds...' );
    end;
    /
    PL/SQL 过程已成功完成。

    执行时间: 已用时间: 00: 00: 07.03


    这是一个使用的绑定变量(吃药后):
    set echo on

    alter system flush shared_pool;
    declare
    type rc is ref cursor;
    l_rc rc;
    l_dummy all_objects.object_name%type;
    l_start number default dbms_utility.get_time;
    begin
    for i in 1 .. 1000
    loop
    open l_rc for
    'select object_name
    from all_objects
    where object_id = :x'
    using i;
    fetch l_rc into l_dummy;
    close l_rc;
    end loop;
    dbms_output.put_line
    ( round( (dbms_utility.get_time-l_start)/100, 2 ) ||
    ' seconds...' );
    end;
    PL/SQL 过程已成功完成。

    执行时间: 已用时间: 00: 00: 00.75

    大家自己比较结果,相差就是一个数量级;使用绑定变量不仅仅是运行快,而且允许多个用户同时使用;

    上述的环境是在数据库Oracle 8.1.7, 操作系统: Windows Server 2003,内存 1G , CPU: P4 3.4GHZ ; 电脑配置不同,执

    行的结果是有差异的;

    2. 怎样使用绑定变量?
    下面举例说明:
    2.1.让Oracle自己绑定变量(也叫静态绑定变量)

    set serverout on;
    set timing on;
    declare
    l_sql varchar2(2000);
    l_count number;
    l_param1 varchar2(100);
    l_param2 varchar2(100);
    begin
    l_param1:='a';
    l_param2:='b';
    select count(*) into l_count from table1 where col_1=l_param1 and col_2=l_param2;
    dbms_output.put_line(l_count);
    end;
    /
    在上面的情况,Oracle会自己绑定变量,即,如果参数保存在一个数组中,select语句放在一个循环中,
    select 语句只会编译一次。

    2.2 .动态绑定变量
    set serverout on;
    set timing on;
    declare
    l_sql varchar2(2000);
    l_count number;
    l_param1 varchar2(100);
    l_param2 varchar2(100);
    begin
    l_param1:='a';
    l_param2:='b';
    l_sql:='select count(*) into :x from table1 where col_1=:y and col_2=:z ';
    Execute Immediate l_sql into l_count using l_param1,l_param2;
    dbms_output.put_line(l_count);
    end;
    /

    2.3. dbms_output的绑定变量使用
    Set echo on;
    Set serveroutput on;
    Set timming on;
    declare
    cursor_id integer;
    i number;
    xSql Varchar2(200);
    xOut varchar2(200);
    l_start number default dbms_utility.get_time;
    xRow integer;
    Begin
    cursor_id:=DBMS_Sql.open_cursor;
    For i in 1..1000 Loop
    DBMS_Sql.parse(cursor_id,'insert into t values(:username,:user_id,Sysdate)',DBMS_SQL.V7);
    DBMS_Sql.bind_variable(cursor_id,'username','test'||to_char(i));
    DBMS_Sql.bind_variable(cursor_id,'user_id',i);
    xRow:=DBMS_Sql.execute(cursor_id);
    --insert into t values('test'||to_char(i),i,Sysdate);
    --xSql:='insert into t values(:username,:user_id,Sysdate)';
    --execute immediate xSql using 'test'||to_char(i),i;
    End loop;
    DBMS_sql.close_cursor(cursor_id);
    dbms_output.put_line(round((dbms_utility.get_time-l_start)/100,2) ||'seconds...');
    --xOut:=to_char(round((dbms_utility.get_time-l_start)/100,2)) ||'seconds...';
    --xOut:='seconds...';
    --return xout;
    end;

    这里强烈推荐使用静态绑定变量,有兴趣的话可以自己比较;

    3. 我怎样知道正在使用绑定变量的方法;
    下面举例说明;
    创建一个Table;
    Create table t (xx int);
    执行下面的语句;
    Begin
    For I in 1..100 loop
    Execute immediate’insert into t values(‘|| t ||’)’;
    End loop;
    end;

    现在准备好了脚本,开始创建一个字符串中删除常数的一个函数,它采用的是SQL语句为:
    insert into t values(‘hello’,55);
    insert into t values(‘world’,56);
    将其转换为
    insert into t values(‘#’,@);
    所有相同的语句很显然是可见的(使用绑定变量);上述两个独特的插入语句经过转换后变成同样的语句; 完成的转换函数

    为:
    Create or replace function remove_constants(p_query in varchar2) return varchar2 as
    l_query long;
    l_char varchar2(1);
    l_in_quates boolean default false;
    begin
    for i in 1..length(p_query)
    loop
    l_char:=substr(p_query,i,1);
    if l_char='''' and l_in_quates then
    l_in_quates:=False;
    elsif l_char='''' and not l_in_quates then
    then
    l_in_quates:=true;
    l_query=:l_query||'#';
    end if

    if not l_in_quates then
    l_query=:l_query||l_char;
    end if;
    end loop;

    l_query:=tranlate(l_query,'0123456789','@@@@@@@@@');
    for i in 1..8 loop
    l_query:=replace(l_query,lpad('@',10-i,'@'),'@');
    l_query:=replace(l_query,lpad('',10-i,''),'');

    end loop;
    return upper(l_query);
    end;
    /

    接着我们建立一个临时表去保存V$SQLAREA里的语句,所有 Sql的执行结果都写在这里;
    建立临时表;
    create global temporary table sql_area_tmp on commit preserve rows as
    select sql_text,sql_text sql_text_wo_constants from
    v$sqlarea where 1=0;

    保存数据到临时表上;
    insert into sql_area_tmp(sql_text) select sql_text from v$sqlarea;

    对临时表中的数据进行更新;删除掉常数;
    Update sql_area_tmp set SQL_TEXT_WO_CONSTANTS= remove_constants(sql_text);

    现在我们要找到哪个糟糕的查询
    select SQL_TEXT_WO_CONSTANTS,count(*) from sql_area_tmp
    group by SQL_TEXT_WO_CONSTANTS
    having count(*)>10
    order by 2;

    SQL_TEXT_WO_CONSTANTS count(*)
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    INSERT INTO T VALUES(@) 100

    另外, 设定如下参数
    Alter session set sql_trace=true;
    Alter session set timed_statictics=True;
    Alter session set events ‘10046 trace name context forever,level <N>’;
    这里的’N’ 表示的是1,4,8,12,详细内容请参考相关文档
    Alter session set events ‘10046 trace name context off’;
    可以用 TKPROF 工具查看绑顶变量执行的结果,更详细内容请查看相关文档;

    4. 绑定变量在应用开发环境下的使用;
    4.1 在VB.Net or ASP.NET and VB中的的使用
    建议用Oracleclient DB的连接方法,OleDB不支持;下面是使用

    OracleClient连接的使用例子(这个代码执行只需要2秒不到);
    Begin
    Dim cn01 As New OracleConnection
    Dim CMD01 As New OracleCommand

    Dim Cmd As New OleDbCommand
    Dim i As Integer
    Try
    <add key="DBCONN_SFCFA" value="User ID=sfcfa;password=SFCFA;Data Source=CIM;" />
    xConnStr = System.Configuration.ConfigurationSettings.AppSettings("DBCONN_SFCFA")
    'cn01.ConnectionString()
    cn01.ConnectionString = xConnStr
    cn01.Open()

    TextBox1.Text = Now
    Application.DoEvents()

    xSql = "insert into t values(:username,:userid,sysdate) "
    For i = 1 To 1000
    CMD01 = New OracleClient.OracleCommand(xSql, cn01)
    CMD01.CommandType = CommandType.Text
    CMD01.Parameters.Add("username", "test" + CStr(i))
    CMD01.Parameters.Add("userid", i)
    CMD01.ExecuteNonQuery()
    CMD01.Parameters.Clear()
    Next i

    TextBox2.Text = Now

    Catch ex As OleDbException
    MsgBox(ex.Message)
    Catch ex As Exception
    MsgBox(ex.HelpLink + ex.Message)

    End Try
    End.

    OleDB(VB,ASP等)不支持绑定变量,或者我没有找到更好的方法去实现它;它有变量的概念但不支持绑定;网络上,有很多帖

    子说;他实现了绑定变量用VB or ASP;我按照他们的方法去试,发现他们与单纯传参数没有什么区别,请看下面的内容;


    OleDB(这个执行需要5秒;) :
    Dim xConnStr, xSql As String
    Dim Cn As New OleDbConnection

    Dim cn01 As New OracleConnection
    Dim CMD01 As New OracleCommand

    Dim Cmd As New OleDbCommand
    Dim i As Integer
    Try
    <add key="DBCONN_SFCFA" value="Provider=MSDAORA.1;User ID=sfcfa;password=SFCFA;Data Source=CIM;"/>
    xConnStr = System.Configuration.ConfigurationSettings.AppSettings("DBCONN_SFCFA")
    'Cn.ConnectionString()
    Cn.ConnectionString = xConnStr
    Cn.Open()

    TextBox1.Text = Now
    Application.DoEvents()

    xSql = "insert into t values(?,?,sysdate) "
    For i = 1 To 1000
    Cmd = New OleDbCommand(xSql, Cn)
    Cmd.CommandType = CommandType.Text
    Cmd.Parameters.Add("username", "test" + CStr(i))
    Cmd.Parameters.Add("userid", i)
    Cmd.ExecuteNonQuery()
    Cmd.Parameters.Clear()
    Next i

    TextBox2.Text = Now

    Catch ex As OleDbException
    MsgBox(ex.Message)
    Catch ex As Exception
    MsgBox(ex.HelpLink + ex.Message)

    End Try

    VB or ASP(耗时也是5秒左右…):

    Private Sub Command1_Click()
    Dim sConn As String
    Dim BVCS_CN As ADODB.Connection
    'Dim BVCS as ADODB.
    Dim xCMD As ADODB.Command
    Dim xPre As ADODB.Parameter
    Dim xSql As String
    Dim xSql01 As String
    Dim xRS As ADODB.Recordset

    On Error GoTo 1

    SetDBConnection = True
    Set BVCS_CN = New ADODB.Connection

    'BVCS_CN.Provider = "MSDAORA"
    'sConn = "DATA SOURCE=" & ServerName & ";"
    sConn = "Provider=MSDAORA.1;Password=sfcfa;User ID=sfcfa;Data Source=cim;"

    With BVCS_CN
    .Open sConn
    End With


    If BVCS_CN.State = 0 Then
    MsgBox "DB Connection is error"
    Exit Sub
    End If

    Text1.Text = Now
    DoEvents

    Set xCMD = New ADODB.Command

    Dim xTest As String


    Set xPre = New ADODB.Parameter

    'BVCS_CN

    For i = 1 To 1000

    With xCMD

    .ActiveConnection = BVCS_CN

    .CommandText = " Insert into TT(username,userid) values(?,?) "
    .CommandType = adCmdText
    .Parameters.Append .CreateParameter("username", adBSTR, adParamInput, 30, "test" + CStr(i))
    .Parameters.Append .CreateParameter("userid", adInteger, adParamInput, 4, i)
    .Prepared = True
    .Execute
    End With


    xCMD.Parameters.Delete 1
    xCMD.Parameters.Delete 0

    Next i


    Set xCMD = Nothing

    Text2.Text = Now

    Exit Sub
    1:
    Set xCMD = Nothing

    MsgBox Error$
    For Each objErr In BVCS_CN.Errors
    MsgBox objErr.Description
    Next
    BVCS_CN.Errors.Clear
    Exit Sub
    Resume Next
    End Sub

    4.2 在Delphi中的使用情况;
    这里特殊说明, Borland Delphi 4.0以上的版本已经开始完全支持绑定变量的概念,因此,它执行数据库的查询效率要好于

    其他开发工具;执行的结果不到2秒;
    procedure TForm1.Button1Click(Sender: TObject);
    Var
    i :Integer;
    begin

    edit1.text:=DatetimeToStr(now);
    For i := 1 to 1000 do
    //Begin
    With Query1 do
    Begin
    close;
    Sql.clear;
    Sql.add('Insert into t Values(:username,:user_id,sysdate) ');
    ParamByName('username').AsString :='test' ;
    ParamByName('user_id').AsInteger :=i ;
    execSql;
    End;
    //end;

    //edit2.text:=DateToStr(now);
    edit2.text:=DatetimetoStr(now);
    end;

    4.3. 在Java中的使用绑定变量

    String v_id = 'xxxxx';
    String v_sql = 'select name from table_a where id = ? '; //嵌入绑定变量
    stmt = con.prepareStatement( v_sql );
    stmt.setString(1, v_id ); //为绑定变量赋值
    stmt.executeQuery();
    在Java中,结合使用setXXX 系列方法,可以为不同数据类型的绑定变量进行赋值,从而大大优化了SQL 语句的性能。

    4.4 C#同VB.NET ,这里不在赘述;

    5. 绑定变量使用限制条件是什么?
    为了不重复解析相同的SQL语句,在第一次解析之后, ORACLE将SQL语句存放在内存中.这块位于系统全局区域SGA(system

    global area)的共享池(shared buffer pool)中的内存可以被所有的数据库用户共享. 因此,当你执行一个SQL语句(有时

    被称为一个游标)时,如果它和之前的执行过的语句完全相同, ORACLE就能很快获得已经被解析的语句以及最好的执行路径

    . ORACLE的这个功能大大地提高了SQL的执行性能并节省了内存的使用.可惜的是ORACLE只对简单的表提供高速缓冲(cache

    buffering) ,这个功能并不适用于多表连接查询(这句并不完全可信,有兴趣的可以自己琢磨).
    数据库管理员必须在init.ora中为这个区域设置合适的参数,当这个内存区域越大,就可以保留更多的语句,当然被共享的

    可能性也就越大了.
    当你向ORACLE 提交一个SQL语句,ORACLE会首先在这块内存中查找相同的语句.这里需要注明的是,ORACLE对两者采取的是

    一种严格匹配,要达成共享,SQL语句必须完全相同(包括空格,换行等).
    共享的语句必须满足三个条件:

    A. 字符级的比较:
    当前被执行的语句和共享池中的语句必须完全相同.
    例如:
    SELECT * FROM EMP;
    和下列每一个都不同
    SELECT * from EMP;
    Select * From Emp;
    SELECT * FROM EMP;


    B. 两个语句所指的对象必须完全相同:
    例如:
    用户 对象名 如何访问
    Jack sal_limit private synonym
    Work_city public synonym
    Plant_detail public synonym

    Jill sal_limit private synonym
    Work_city public synonym
    Plant_detail table owner

    考虑一下下列SQL语句能否在这两个用户之间共享.


    SQL 能否共享 原因
    select max(sal_cap) from sal_limit; 不能 每个用户都有一个private synonym - sal_limit , 它们是不同的对象
    select count(*0 from work_city where sdesc like 'NEW%'; 能两个用户访问相同的对象public synonym - work_city
    select a.sdesc,b.location from work_city a , plant_detail b where a.city_id = b.city_id 不能 用户jack 通过

    private synonym访问plant_detail 而jill 是表的所有者,对象不同.



    C. 两个SQL语句中必须使用相同的名字的绑定变量(bind variables)

    例如:

    第一组的两个SQL语句是相同的(可以共享),而第二组中的两个语句是不同的(即使在运行时,赋于不同的绑定变量相同的值

    )
    a.
    select pin , name from people where pin = :blk1.pin;
    select pin , name from people where pin = :blk1.pin;

    b.
    select pin , name from people where pin = :blk1.ot_ind;
    select pin , name from people where pin = :blk1.ov_ind;


    6. 总结
    不使用绑定变量是做着等死,使用绑定变量不一定不会死;没有任何的良药会包治百病,所以在如何合理有效地使用绑定变

    量仍就需要大家去摸索;

    [全文完]..

    相关文献:
    1. [Oracle 高级专家编程] 作者:Thomas Kytes 袁勤勇,张玉魁编译;清华大学出版社;
    2. ORACLE SQL性能优化系列 from www.dbasupport.com
    3. Oracle 绑定变量的用法 from 王者之剑(www.albertsong.com)

    Posted Feb 03 2009, 11:51 AM by slash with no comments
    Filed under:
  • 索引概念

    Oracle提供了大量索引选项。知道在给定条件下使用哪个选项对于一个应用程序的性能来说非常重要。一个错误的选择可能会引发死锁,并导致数据库性能急剧下降或进程终止。而如果做出正确的选择,则可以合理使用资源,使那些已经运行了几个小时甚至几天的进程在几分钟得以完成,这样会使您立刻成为一位英雄。这篇文章就将简单的讨论每个索引选项。主要有以下内容:

    1、基本的索引概念:

    查询DBA_INDEXES视图可得到表中所有索引的列表,注意只能通过USER_INDEXES的方法来检索模式(schema)的索引。访问USER_IND_COLUMNS视图可得到一个给定表中被索引的特定列。

    2、组合索引:

    当某个索引包含有多个已索引的列时,称这个索引为组合(concatented)索引。在 Oracle9i引入跳跃式扫描的索引访问方法之前,查询只能在有限条件下使用该索引。比如:表emp有一个组合索引键,该索引包含了empno、 ename和deptno。在Oracle9i之前除非在where之句中对第一列(empno)指定一个值,否则就不能使用这个索引键进行一次范围扫描。

    特别注意:在Oracle9i之前,只有在使用到索引的前导索引时才可以使用组合索引!

    3、Oracle ROWID:

    通过每个行的ROWID,索引Oracle提供了访问单行数据的能力。ROWID其实就是直接指向单独行的线路图。如果想检查重复值或是其他对ROWID本身的引用,可以在任何表中使用和指定rowid列。

    4、限制索引:

    限制索引是一些没有经验的开发人员经常犯的错误之一。在SQL中有很多陷阱会使一些索引无法使用。下面讨论一些常见的问题:

    4.1 使用不等于操作符(<>、!=):

    下面的查询即使在cust_rating列有一个索引,查询语句仍然执行一次全表扫描。

    select cust_Id,cust_name
    from  customers
    where cust_rating <> 'aa';

    把上面的语句改成如下的查询语句,这样,在采用基于规则的优化器而不是基于代价的优化器(更智能)时,将会使用索引。

    select cust_Id,cust_name
    from  customers
    where cust_rating < 'aa' or cust_rating > 'aa';

    特别注意:通过把不等于操作符改成OR条件,就可以使用索引,以避免全表扫描。

    4.2 使用IS NULL 或IS NOT NULL:

    使用IS NULL 或IS NOT NULL同样会限制索引的使用。因为NULL值并没有被定义。在SQL语句中使用NULL会有很多的麻烦。因此建议开发人员在建表时,把需要索引的列设成NOT NULL。如果被索引的列在某些行中存在NULL值,就不会使用这个索引(除非索引是一个位图索引,关于位图索引在稍后在详细讨论)。

    4.3 使用函数:

    如果不使用基于函数的索引,那么在SQL语句的WHERE子句中对存在索引的列使用函数时,会使优化器忽略掉这些索引。

    下面的查询不会使用索引(只要它不是基于函数的索引):

    select empno,ename,deptno
    from  emp
    where trunc(hiredate)='01-MAY-81';

    把上面的语句改成下面的语句,这样就可以通过索引进行查找。

    select empno,ename,deptno
    from  emp
    where hiredate<(to_date('01-MAY-81')+0.9999);

    4.4 比较不匹配的数据类型:

    比较不匹配的数据类型也是比较难于发现的性能问题之一。

    注意下面查询的例子,account_number是一个VARCHAR2类型,在account_number字段上有索引。下面的语句将执行全表扫描。

    select bank_name,address,city,state,zip
    from  banks
    where account_number = 990354;

    Oracle可以自动把where子句变成to_number(account_number)=990354,这样就限制了索引的使用,改成下面的查询就可以使用索引:

    select bank_name,address,city,state,zip
    from  banks
    where account_number ='990354';

    特别注意:不匹配的数据类型之间比较会让Oracle自动限制索引的使用,即便对这个查询执行Explain Plan也不能让您明白为什么做了一次“全表扫描”。

    5、选择性:

    使用USER_INDEXES视图,该视图中显示了一个distinct_keys列。比较一下唯一键的数量和表中的行数,就可以判断索引的选择性。选择性越高,索引返回的数据就越少。

    6、群集因子(Clustering Factor):

    Clustering Factor位于USER_INDEXES视图中。该列反映了数据相对于已索引的列是否显得有序。如果Clustering Factor列的值接近于索引中的树叶块(leaf block)的数目,表中的数据就越有序。如果它的值接近于表中的行数,则表中的数据就不是很有序。

    7、二元高度(Binary height):

    索引的二元高度对把ROWID返回给用户进程时所要求的I/O量起到关键作用。在对一个索引进行分析后,可以通过查询DBA_INDEXES的B-level列查看它的二元高度。二元高度主要随着表的大小以及被索引的列中值的范围的狭窄程度而变化。索引上如果有大量被删除的行,它的二元高度也会增加。更新索引列也类似于删除操作,因为它增加了已删除键的数目。重建索引可能会降低二元高度。

    8、快速全局扫描:

    在Oracle7.3后就可以使用快速全局扫描(Fast Full Scan)这个选项。这个选项允许Oracle执行一个全局索引扫描操作。快速全局扫描读取B-树索引上所有树叶块。初始化文件中的 DB_FILE_MULTIBLOCK_READ_COUNT参数可以控制同时被读取的块的数目。

    9、跳跃式扫描:

    从Oracle9i开始,索引跳跃式扫描特性可以允许优化器使用组合索引,即便索引的前导列没有出现在WHERE子句中。索引跳跃式扫描比全索引扫描要快的多。下面的程序清单显示出性能的差别:

    create index skip1 on emp5(job,empno);
    index created.

    select count(*)
    from emp5
    where empno=7900;

    Elapsed:00:00:03.13

    Execution Plan
    0 SELECT STATEMENT Optimizer
    =CHOOSE(Cost=4 Card=1 Bytes=5)
    1 0SORT(AGGREGATE)
    2 1 INDEX(FAST FULL SCAN)
    OF 'SKIP1'(NON-UNIQUE)

    Statistics

    6826 consistent gets
    6819 physical  reads

    select /*+ index(emp5 skip1)*/ count(*)
    from emp5
    where empno=7900;

    Elapsed:00:00:00.56

    Execution Plan
    0 SELECT STATEMENT Optimizer
    =CHOOSE(Cost=6 Card=1 Bytes=5)
    1 0SORT(AGGREGATE)
    2 1 INDEX(SKIP SCAN) OF
    'SKIP1'(NON-UNIQUE)

    Statistics

    21 consistent gets
    17 physical  reads

    10、索引的类型:

    B-树索引 位图索引 HASH索引 索引编排表

    反转键索引 基于函数的索引 分区索引 本地和全局索引

More Posts Next page »
Copyright SDT, 2006-2009. All rights reserved.