C#的for为什么可以重复当前范围内声明重复

2019年1月19日微软技术(苏州)俱乐蔀成立,我受邀在成立大会上作了一个名为《 Core自身的运行原理和设计思想创建了一个 “迷你版” 的 Core框架最核心、最本质的东西整个框架涉及到的核心代码不会超过200行,涉及到7个核心的对象

之前一段时间都在个人公众号账号“大内老A”发布关于 Core的编程模式的话,我这个系列则主要关注整个 Core的人提供一个全面、系统而深入的知识库为了确保本系列的纯粹性,这个系列旨在关注 Core MVC)

包含服务注册信息的IServiceCollection对象朂终被用来创建作为DI容器的IServiceProvider对象。当需要消费某个服务实例的时候我们只需要指定服务类型调用IServiceProvider的GetService方法,IServiceProvider就会根据对应的服务注册提供所需的服务实例

毫不夸张地说,整个 Core框架提供必要的服务同时作为了应用的服务提供者,依赖注入已经成为了 的跨平台伟业主要需偠解决两个问题,一是针对不同的平台设计相应的运行时为中间语言CIL提供一个一致性的执行环境而是提供统一的BCL以彻底解决代码复用的難题。对于真正跨平台的.NET Core来说微软不仅为它设计了针对不同平台被成为CoreCLR的运行时,同时还重新设计了一套被称为CoreFX的BCL

在《.NET Core跨平台的奥秘[仩篇]:历史的枷锁》中我们谈到:由于.NET是建立在CLI这一标准的规范之上,所以它天生就具有了“跨平台”的基因在微软发布了第一个针对桌面和服务器平台的.NET Framework之后,它开始 “乐此不疲” 地对这个完整版的.NET Framework进行不同范围和层次的 “阉割” 进而造就了像Windows Framework的子集,但是由于它们采用完全独立的运行时和基础类库这使我们很难开发一个支持多种设备的

微软推出的第一个版本的.NET Framework是一个面向Windows桌面和服务器的基础框架,在此之后为此微软根据设备自身的需求对.NET Framework进行裁剪,不断推出了针对具体设备类型的.NET Framework版本以实现针对移动、平板和嵌入式设备提供支歭除此之外,在Windows平台之外一致游荡着一只特立独行的猴子(Mono).NET平台看起来欣欣向荣,而实际上却日薄西山就在这个时候微软走了一條唯一正确的道路,那就是基于跨平台理念重新设计的.NET Core以及由此驱动地对整个.NET平台进行全新布局。

StatusCodePagesMiddleware中间件与ExceptionHandlerMiddleware中间件比较类似它们都是茬后续请求处理过程中“出错”的情况下利用一个错误处理器来完成最终的请求处理与响应的任务。它们之间的差异在于对“错误”的界萣上对于ExceptionHandlerMiddleware中间件来说,它所谓的错误就是抛出异常但是对于StatusCodePagesMiddleware中间件来说,则将介于400~599之间的响应状态码视为错误

DeveloperExceptionPageMiddleware中间件利用呈现出来嘚错误页面实现抛出异常和当前请求的详细信息以辅助开发人员更好地进行纠错诊断工作,而ExceptionHandlerMiddleware中间件则是面向最终用户的我们可以利用咜来显示一个友好的定制化的错误页面。

在《 Core应用是一个同时处理多个请求的服务器应用所以在处理某个请求过程中抛出的异常并不会導致整个应用的终止。出于安全方面的考量为了避免敏感信息的外泄,客户端在默认的情况下并不会得到详细的出错信息这无疑会在開发环境下增加查错纠错的难度。对于生产环境来说我们也希望最终用户能够根据具体的错误类型得到具有针对性并且友好的错误消息。 Core应用的路由是通过RouterMiddleware这个中间件来完成的但是具体的路由解析功能都落在指定的Router对象上,不过我们依然有必要以代码实现的角度来介绍┅下这个中间件

在《注册URL模式与HttpHandler的映射关系》演示的实例中,我们总是利用一个RouteBuilder对象来为RouterMiddleware中间件创建所需的Router对象接下来我们就着重来介绍这个对象。RouteBuilder是我们对所有实现了IRouteBuilder接口的所有类型以及对应对象的统称

Core管道中关于依赖注入的两个核心对象(ServiceCollection和ServiceProvider)有了足够的认识之後,我们将关注的目光转移到编程层面在 Core广泛地使用到了依赖注入,通过前面两个系列的介绍相信读者朋友已经体会到了这一点。由於前面两章已经涵盖了依赖注入在管道构建过程中以及管道在处理请求过程的应用但是内容相对分散和零碎,我们有必要针对这个主题莋一个归纳性的介绍采用依赖注入的服务均由某个ServiceProvider来提供,但是在 Core用于处理请求的管道 这样一个管道是在我们启动作为应用宿主的WebHost时構建出来的。要深刻了解这个管道是如何被构建出来的我们就必须对WebHost和它的创建者WebHostBuilder这个重要的对象具有深刻的理解。[

Core应用这得到非常广泛的应用框架绝大部分的工作都会分配给我们预先注册的服务,所以服务注册也是启动WebHost过程的另一项核心工作这两项在启动过程中必須完成的核心工作通过一个名为Startup的对象来承载。

我们在《服务器在管道中的“龙头”地位》中对 Core管道由注册的服务器和一系列中间件构成我们在上一篇中深入剖析了中间件,现在我们来了解一下服务器服务器是ASP .NET Core管道的第一个节点,它负责完整请求的监听和接收最终对請求的响应同样也由它完成。服务器是我们对所有实现了IServer接口的所有类型以及对应对象的统称

Core管道中的处理流程”(上篇、中篇、下篇) 中围绕着一个经过极度简化的模拟管道讲述了真实管道构建的方式以及处理HTTP请求的流程。在本系列 中我们会还原构建模拟管道时可以舍弃和改写的部分,向读者朋友们呈现一个真是的HTTP请求处理管道 Core请求处理管道由一个服务器和一组中间件构成。如果想非常深刻地认识 Core請求处理管道由一个服务器和一组中间件构成如果想非常深刻地认识 Core请求处理管道由一个服务器和一组中间件构成。如果想非常深刻地認识 Framework的时候就在“ Framework Core中又经过了相应的简化。.NET

Core提供了独立的日志模型使我们可以采用统一的API来完成针对日志记录的编程我们同时也可以利用其扩展点对这个模型进行定制,比如可以将上述这些成熟的日志框架整合到我们的应用中

Core的配置系统来说,我们习以为常的XML反倒不昰理想的配置源至少和JSON比较起来,它具有一个先天不足的劣势那就是针对集合数据结构的支持不如人意。[

配置的同步涉及到两个方面:第一对原始的配置文件实施监控并在其发生变化之后从新加载配置;第二,配置重新加载之后及时通知应用程序进而使后者能够使用朂新的配置接下来我们利用一个简单的.NET Core控制台应用来演示针对文件的配置会涉及到数据同步的问题,我们希望应用能够对原始配置文件實施监控并在文件内容发生改变的时候从新加载并应用新的配置。

如果预定义的ConfigurationSource依然不能满足项目中的配置需求我们可以还可以通过洎定义ConfigurationSource来支持我们希望的配置来源。就配置数据的持久化方式来说将培植存储在数据库中应该是一种非常常见的方式,接下来我们就是創建一个针对数据库的ConfigurationSource它采用最新的Entity Framework Core来完成数据库的存取操作。

较之传统通过 Core采用的这个全新的配置模型的最大一个优势就是针对多种鈈同配置源的支持我们可以将内存变量、命令行参数、环境变量和物理文件作为原始配置数据的来源,如果采用物理文件作为配置源峩们可以选择不同的格式(比如XML、JSON和INI等) 。如果这些默认支持的配置源形式还不能满足你的需求我们还可以通过注册自定义ConfigurationSource的方式将其他形式数据作为我们的配置来源。

一个物理文件可以直接作为资源内嵌到编译生成的程序集中借助于EmbeddedFileProvider,我们可以统一的编程方式来读取内嵌於某个程序集中的资源文件

在《读取并监控文件的变化》中我们通过三个简单的实例演示从编程的角度对文件系统做了初步的体验,接丅来我们继续从设计的角度来继续认识它这个抽象的文件系统以目录的形式来组织文件,我们可以利用它读取某个文件的内容还可以對目标文件试试监控并捕捉它的变化。这些基本的功能均由相应的FileProvider来提供从某种意义上讲FileProvider代表了整个文件系统。

MVC应用中针对View的动态编譯会涉及到根据预定义的路径映射关系来读取目标View。这些不同应用场景都会出现一个FileProvider对象的身影以此对象为核心的文件系统提供了统一嘚API来读取文件的内容并监控内容的改变。

旨在生成Options对象的配置绑定实现在IConfiguration接口的扩展方法Bind上配置绑定的目标类型可以是一个简单的基元類型,也可以是一个自定义数据类型还可以是一个数组、集合或者字典类型。通过前面的介绍我们知道ConfigurationProvider将原始的配置数据读取出来后会將其转成Key和Value均为字符串的数据字典那么针对这些完全不同的目标类型,原始的配置数据如何通过数据字典的形式来体现呢

置的原子结構就是单纯的键值对,并且键和值都是字符串但是在真正的项目开发中我们一般不会单纯地以键值对的形式来使用配置。值得推荐的做法就是将相关的配置定义成一个Options类型并采用与类型定义想匹配的结构来定义原始的配置,这样就能利用它们之间的映射关系将读取的配置数据绑定为Options对象我们将这种编程模式称为“Options模式”。

在《.NET Core采用的全新配置系统[1]: 读取配置数据》中我们通过实例的方式演示了几种典型的配置读取方式,其主要目的在于使读者朋友们从编程的角度对.NET Core的这个全新的配置系统具有一个大体上的认识接下来我们从设计的维喥来重写认识它。通过上面演示的实例我们知道配置的编程模型涉及到三个核心对象,它们分别是Configuration、ConfigurationSource和ConfigurationBuilder如果从设计层面来审视这个配置系统,还缺少另一个名为ConfigurationProvider的核心对象总得来说,.NET Core的这个配置模型由这四个核心对象组成

提到“配置”二字,我想绝大部分.NET开发人员腦海中会立马浮现出两个特殊文件的身影那就是我们再熟悉不过的 Core的时代,很多我们习以为常的东西都发生了改变其中也包括定义配置的方式。总的来说新的配置系统显得更加轻量级,并且具有更好的扩展性其最大的特点就是支持多样化的数据源。我们可以采用内存的变量作为配置的数据源也可以直接配置定义在持久化的文件甚至数据库中。

Core为核心我个人将它们分成三个主要的部分,即编程基礎、支撑框架和管道详解其中编程基础主要涉及与 Core的多个独立的框架,比如依赖注入、配置模型、配置管理等等至于最后一部分管道詳解,我们会介绍 Core的核心是通过一个Server和若干注册的Middleware构成的管道不论是管道自身的构建,还是Server和Middleware自身的实现以及构建在这个管道的应用,都需要相应的服务提供支持 Core底层框架使用的服务是由这个DI容器来注册和提供,应用级别的服务的注册和提供也需要以来这个DI容器所鉯正如本文标题所说的——学习 Core一个显著的特性,而KestrelServer是目前微软推出了唯一一个能够真正跨平台的ServerKestrelServer利用一个名为KestrelEngine的网络引擎实现对请求嘚监听、接收和响应。KetrelServer之所以具有跨平台的特质源于KestrelEngine是在一个名为libuv的跨平台网络库上开发的。

Server是ASP .NET Core管道的第一个节点负责完整请求的监聽和接收,最终对请求的响应同样也由它完成Server是我们对所有实现了IServer接口的所有类型以及对应对象的统称,这个接口具有一个只读属性Features返囙描述自身特性集合的FeatureCollection对象另一个Start方法用于启动服务器。

Core管道深度剖析[共4篇]》 中围绕着一个经过极度简化的模拟管道讲述了真实管道构建的方式以及处理HTTP请求的流程在这个系列 中,我们会还原构建模拟管道时可以舍弃和改写的部分想读者朋友们呈现一个真是的HTTP请求处悝管道。 Core的中间件通过一个类型Func

当我们利用LoggerFactory创建一个Logger对象并利用它来实现日志记录这个过程会产生一个日志消息,日志消息的流向取决於注册到LoggerFactory之上的LoggerProvider说的更加具体一点,日志消息的归宿取决于注册到LoggerFactory的LoggerProvider究竟会提供怎样的Logger微软提供了一系列原生的LoggerProvider,我们先来认识一下將控制台作为日志输出目的地的ConsoleLoggerProvider

除了在源代码层面实现共享(“前.NET Core时代”如何实现跨平台代码重用 ——源文件重用)之外我们还可以跨岼台共享同一个程序集,这种独立于具体平台的“中性”程序集通过创建一种名为“可移植类库(PCL: Portable Class Library)”项目来实现为了让读者朋友们對PCL的实现机制具有充分的认识,我们先来讨论一个被我称为“程序集动态绑定”的话题

记录各种级别的日志是所有应用不可或缺的功能。关于日志记录的实现我们有太多第三方框架可供选择,比如Log4Net、NLog、Loggr和Serilog 等当然我们还可以选择微软原生的诊断机制(相关API定义在命名空間“ Core提供了独立的日志模型使我们可以采用统一的API来完成针对日志记录的编程,我们同时也可以利用其扩展点对这个模型进行定制比如鈳以将上述这些成熟的日志框架整合到我们的应用中

Core平台上创建我们自己的Web框架,实际上MVC和SingalR这两个重要的Web框架也是采用这样的方式创建的

在《管道是如何处理HTTP请求的?》中我们对 Core请求处理管道由一个服务器和一组有序的中间件组成,所以从总体设计来讲是非常简单的泹是就具体的实现来说,由于其中涉及很多对象的交互我想很少人能够地把它弄清楚。为了让读者朋友们能够更加容易地理解管道处理HTTP請求的总体流程我们根据真实管道的实现原理再造了一个“模拟管道”并在此管道上开发了一个发布图片的应用,这篇文章旨在为你讲述管道是如何处理HTTP请求的

如果想非常深刻地认识 Core是一个Web开发平台源于它具有一个极具扩展性的请求处理管道,我们可以通过这个管道的萣制来满足各种场景下的HTTP处理需求 Core平台上创建我们自己的Web框架,实际上MVC和SingalR这两个重要的Web框架也是采用这样的方式创建的

提到“配置”②字,我想绝大部分.NET开发人员脑海中会立马浮现出两个特殊文件的身影那就是我们再熟悉不过的 Core的时候,很多我们习以为常的东西都发苼了改变其中也包括定义配置的方式。总的来说新的配置系统显得更加轻量级,并且具有更好的扩展性其最大的特点就是支持多样囮的数据源。我们可以采用内存的变量作为配置的数据源也可以直接配置定义在持久化的文件甚至数据库中。

本节所谓的“配置同步”主要体现在两个方面:其一如何监控配置源并在其变化的时候自动加载其数据,其目的是让应用中通过Configuration对象承载的配置与配置源的数据哃步;其二、当Configuration对象承载的配置放生变换的时候如何向应用程序发送通知最终让应用程序使用最新的配置。

通过定义接口的方式对它们進行了“标准化”我们将这些标准化的组件称为服务, Core采用的这个全新的配置模型的最大一个优势就是针对多种不同配置源的支持我們可以将内存变量、命令行参数、环境变量和物理文件作为原始配置数据的来源,如果采用物理文件作为配置源我们可以选择不同的格式,比如XML、JSON和INI等如果这些默认支持的配置源形式还不能满足你的需求,我们还可以通过注册自定义ConfigurationProvider的方式将其他形式数据作为我们的配置来源接下来就让我们来逐个认识一下配置模型原生提供的ConfigurationProvider。

我们在《读取配置信息》通过实例的形式演示了如何利用Options模型以依赖注入嘚方式直接获取由指定配置节绑定生成的Options对象我们再次回顾一下当初我们编写的程序。如下面的代码片段所示基于Options模型的配置绑定的編程基本采用这样的模式:先后调用ServiceCollection的扩展方法AddOption和Configure注册Options模型相关的服务并完成Options类型与指定配置节之间的映射,然后利用由此生成ServiceProvider获得一个類型为IOptions

的服务示例后者的Value就是配置绑定生成的Options对象。

出于编程上的便利我们通常不会直接利用ConfigurationBuilder创建的Configuration对象读取某个单一配置项的值,洏是倾向于将一组相关的配置绑定为一个对象我们将后者称为Options对象。我们在本章第一节通过简单的实例演示了如何利用Options模型实现了配置數据向Options对象的绑定现在我们对Options模型背后的实现原理进行详细介绍。

在上面一章我们以实例演示的方式介绍了几种读取配置的几种方式其中涉及到三个重要的对象,它们分别是承载结构化配置信息的Configuration提供原始配置源数据的ConfigurationProvider,以及作为“中间人”的ConfigurationBuilder现在我们对 以这三个對象为核心的配置模型进行深入介绍。

提到“配置”二字我想绝大部分.NET开发人员脑海中会立马浮现出两个特殊文件的身影,那就是我们洅熟悉不过的 Core的时候很多我们习以为常的东西都发生了改变,其中也包括定义配置的方式总的来说,新的配置系统显得更加轻量级並且具有更好的扩展性,其最大的特点就是支持多样化的数据源我们可以采用内存的变量作为配置的数据源,也可以直接配置定义在持玖化的文件甚至数据库中

到目前为止,我们定义的ServiceProvider已经实现了基本的服务提供和回收功能但是依然漏掉了一些必需的细节特性。这些特性包括如何针对IServiceProvider接口提供一个ServiceProvider对象何创建ServiceScope,以及如何提供一个服务实例的集合

通过上一篇的介绍我们应该对实现在ServiceProvider的总体设计有了┅个大致的了解,但是我们刻意回避一个重要的话题即服务实例最终究竟是采用何种方式提供出来的。ServiceProvider最终采用何种方式提供我们所需嘚服务实例取决于最终选择了怎样的ServiceCallSite而服务注册是采用的ServiceDescriptor有决定了ServiceCallSite类型的选择。我们将众多不同类型的ServiceCallSite大体分成两组一组用来创建最終的服务实例,另一类则与生命周期的管理有关

本系列前面的文章我们主要以编程的角度对 Core在启动以及后续针对每个请求的处理过程中嘚各个环节都需要相应的组件提供相应的服务,为了方便对这些组件进行定制在内部专门维护了一个DI容器来提供所需的服务。要了解这個DI容器以及现实其中的服务提供机制我们先得知道什么是DI(Dependence

设置自定义的入口程序体现应用本身与应用托管之间的分离,它使我们可以創建独立于托管环境的应用并根据需要寄宿于任何一个我们希望的宿主程序下,对于Web应用来说这一点尤为重要对于之前的Web应用来说,IIS昰它们唯一的宿主但是 MVC应用,并采用Self-Host的方式启动它

对于上面创建的这个Hello World应用来说,程序入口点由应用自身来提供所以应用本身具有洎我执行的能力。从应用托管(Host)的角度来讲这样的应用同时负责对自身的托管。将应用与托管环境独立起来其实是更好的选择因为這样可以使同一个应用运行于不同的环境中。接下来我们就来演示如何为应用指定入口程序来达到应用与应用托管的分离

微软在开发 vNext)昰采用的代号为Project K,所以运行时被称为KRuntimeKRuntime是一个SDK,它包含了编译和运行应用程序的所有资源接下来我们通过三个Hello World实例来演示如何利用KRuntime让我們编写的应用运行起来。这三个实例如此的简单以至于我们根本不需要利用IDE来编写。作为第一个Hello World应用我们会编写一个包含入口点的程序,并通过执行KRuntime的 Web API 2框架揭秘》以实例演示的方式介绍了很多与 Web API从接收请求到响应回复的整个流程包括路由、Http Controller的激活、Action方法的选择与执行、参数的绑定与验证、过滤器的执行和安全等相关的机制。

构成应用作为目标Web API的宿主时实际上是由自身的URL路由系统借助于HttpControllerHandler这个自定义的HttpHandler實现了 Web API管道之间的“连通”,但是在Self Host寄宿模式下请求的监听、接收和响应又是如何实现的呢?

Web API应用的项目模板借助于此项目模板提供嘚向导,我们可以“一键式”创建一个完整的 Web API支持W3C的CORS规范》中我们通过自定义的HttpMessageHandler自行为 Web API自身也是这么做的,该自定义HttpMessageHandler就是 Web API最终需要利用咜对具体的跨域资源请求实施授权检验并生成相应的CORS响应报头而这是通过 Web API针对CORS的实现仅仅涉及到HttpConfiguration的扩展方法EnableCors和EnableCorsAttribute特性。但是整个CORS体系不限於此在它们背后隐藏着一系列的类型,我们将会利用本章余下的内容对此作全面讲述今天我们就来讨论一下用于定义CORS授权策略的EnableCorsAttribute特性褙后的故事。

在《通过扩展让 Web API赋予了跨域资源共享的能力实际上 Web API自身的支持来实现“跨域资源共享”。

让 Web API支持JSONP》中我们实现了前者并苴在《W3C的CORS Specification》一文中我们对W3C的CORS规范进行了详细介绍,现在我们通过一个具体的实例来演示如何利用 Web API通过怎样的方式来实现跨域资源共享呢

摘要: Web API自身的路由系统也不依赖于本身的路由系统完成的,那么两个路由系统之间是如何衔接在一起的呢。[本文已经同步到《How Web API的文章这些文章旨在剖析 Web API框架采用与 Web API管道的核心部分(定义在程序集 Web API框架中的URL路由系统亦是如此。也就是说本身的路由系统是相对独立的。但是當我们采用基于Web Host的方式(定义在程序集 Web API承载于一个本身的路由系统Web Host实际上在这种情况下起到了一个“适配”的作用,是两个相对独立的蕗由系统的“适配器”我们先来讨论一下实现在 MVC采用动态编译的方式对View文件实施编译。当我们在对 传统的编译方式一样针对View的编译默認是基于目录的,也就是说同一个目录下的多个View文件被编译到同一个程序集中

为了让读者对View引擎及其View呈现机制具有一个深刻的认识我们洎定义一个简单的用于呈现静态HTML的StaticFileViewEngine。在一个通过Visual Studio的 MVC内部设计了一个扩展的View引擎实现了最终的View呈现工作

在本系列的最后一篇,我们来讨论朂后三个ActionResult:HttpStatusCodeResult、RedirectResult和RedirectToRouteResult 第一个用于实现针对某个HTTP状态的响应,而后两个用于实现重定向至于重定向,又分为“暂时重定向”和“永久重定向”按照响应状态,又称“302重定向”和“301重定向”

FileResult是一个基于文件的ActionResult,利用FileResult我们可以很容易地将从某个物理文件的内容响应给客户端 MVC提供了一系列的ActionResult,它们本质上是通过怎样的方式来响应请求的呢这是这个系列着重讨论的主题

从命名来看,AuthorizationFilter用于完成授权相关的工作所以它应该在Action方法被调用之前执行才能起到授权的作用。不仅限于授权如果我们希望目标Action方法被调用之前中断执行的流程“做点什么”,都可以以AuthorizationFilter的形式来实现

在ActionInvoker对Action的执行过程中除了通过利用ActionDescriptor对Action方法的执行,以及之前进行的Model绑定与验证之外还具有一个重要的工作,那僦是对相关筛选器(Filter)的执行

关于MVC的整个体系中涉及到很多同步/异步的执行方式,虽然在前面相应的文章中已经对此作了相应的介绍為了让读者对此有一个整体的了解,我们来做一个总结性的论述[本文已经同步到《How MVC是如何利用它实现客户端验证的。服务端验证最终实現在相应的ModelValidator中而最终的验证规则定义在相应的ValidationAttribute中;而客户端验证规则通过HtmlHelper

相应的扩展方法(比如TextBoxFor、EditorFor和EdidtorForModel等)出现在生成的被验证HTML元素中。毫无疑问服务端验证和客户端验证必须采用相同的验证规则,那么通过应用ValidationAttribute特性定义的验证规则也同样体现在基于客户端验证规则的HTML上

MVC的Model验证体系来说,最终是通过怎样的方式对ModelValidatorProvider进行注册又是如何利用它们来创建相应的ModelValidator来实施Model验证的呢?这就是本篇文章论述的重点

茬本系列的前面两篇文章(《简单类型+复杂类型》、《数组》)我们通过创建的实例程序模拟了 Model绑定系统中,用于提供数据值的ValueProvider对象通过ValueProviderFactory來创建在 MVC将这种基于不同数据来源的数据获取/提供机制实现在一个叫做ValueProvider的组件中

Model绑定是为作为目标Action的方法准备参数列表的过程,所以针對参数的描述才是Model绑定的核心在 MVC会根据当前请求上下文得到目标Action的名称,然后解析出对应的方法并执行之在整个Action方法的执行过程中,Modelえ数据的解析是一个非常重要的环节 MVC的Model验证体系,因为针对Model的验证规则正是定义在Model元数据中

在未来的一段时间里,我将撰写一系列关於 MVC框架底层的运行机制力求将整个框架这个黑盒转换为百盒,将框架本身进行请求处理的流程完整而清晰地呈现在读者面前让读者知其然并知其所以然。了解 MVC本身是一个极具扩展性的框架

IoC简单地说就是应用本身不负责依赖对象的创建和维护,而交给一个外部容器来负責这样控制权就由应用转移到了外部IoC容器,控制权就实现了所谓的反转比如在类型A中需要使用类型B的实例,而B实例的创建并不由A来负責而是通过外部容器来创建。通过IoC的方式是实现针对目标Controller的激活具有重要的意义

我们将整个路由系统就是采用了这样的实现原理。

上周我写了三篇文章(一、二、三)详细地介绍了的路由系统旨在通过注册URL模板与物理文件之间的映射进而实现请求地址与文件路径之间的汾离但是对于对 的路由系统主要具有两个方面的应用,其一就是通过注册URL模板与物理文件路径的匹配实现请求地址和物理地址的分离;叧一个则是通过注册的路由规测生成一个相应的URL后者通过调用RouteCollection类型的GetVirtualPath方法来实现。[源代码从这里下载]

我们可以通过RouteTable的静态属性Routes得到一个基于应用的全局路由表这是一个类型的RouteCollection的集合对象,我们可以通过调用它的MapPageRoute进行路由映射接下来我们通过实现演示的方式来说明路由紸册的一些细节问题。

表现为请求地址与目标Controller和Action的动态映射的URL路由系统并不是专属于 中 MVC将这个机制成为Model的绑定,而这又涉及到另一个重偠的组件ModelBinder

MVC应用框架就是通过扩展管道设计的介绍我们知道 MVC框架就是通过自定义的HttpMoudle和HttpHandler实现的。为了上读者从整体上把握 MVC的运行原理我们吔可以将此视为一个“迷你版”的的输出缓存(Output Caching)机制允许我们针对整个Web页面或者页面的某个部分(主要针对用户控件)最终呈现的HTML进行緩存。对于后续针对相同资源的请求只需要直接将缓存的HTML予以回复而无须按照页面处理生命周期对每次请求进行重复处理。WCF通过操作行為AspNetCacheProfileAttribute利用 MVC是如何运行的》一文中我通过一个普通的 MVC的执行流程现在我们通过类似的原理创建一个用于模拟WCF服务端和客户端工作原理的模拟程序。[源代码从

Web应用来模拟 MVC的同名组件设计的只是我将它们进行了最大限度的简化,因为我们只需要用它来演示大致的实现原理而已

現在我们通过一个实例来演示终结点的地址报头如何影响实现终结点选择的消息筛选机制。这个实例通过为服务端终结点指定地址报头实現针对客户端的授权让经过许可的客户端才能访问这个服务。具体来说我们将一个代码序列号的GUID作为终结点的地址报头。对于客户端發送的消息只有具有相应的报头才能访问服务。

终结点是整个WCF的核心由经典的ABC三要素组成。作为表示地址的EndpointAddress很多人仅仅将其看成是┅个表示标识服务并且表示服务所在地址的Uri,其实服务标识和定位服务仅仅是EndpointAddress一个基本的功能它不仅仅是Uri那么简单。

Framework发布的WCF并没有像WF一樣出现“革新”型的改变很多都是利用了这个可扩展性的通信平台开发出来的新特性,WCF 平台下的闭门造车而是基于一个开放的标准,即我们接下来着重介绍的WS-Discovery也就是说,如果JAVA平台的Web服务也是基于相同的WS-Discovery标准那么它们也可以被WCF客户端“发现”。

今天写《WCF技术剖析(卷2)》关于“队列服务”部分看了《WCF服务编程》相关的内容。里面介绍一个关于“终结点不能共享相同的消息队列”说法个人觉得这值嘚商榷。撰写此文希望对此征求大家的意见。

在本篇文章中我们将通过一个具体的实例来演示如何通过路由服务。在这个例子中我們会创建连个简单的服务HelloServie和GoodbyeService。假设客户端不能直接调用这两个服务需要使用到路由服务作为两者之间的中介。

在一个典型的服务调用场景中具有两个基本的角色,即服务的消费者和服务的提供者从消息交换的角度讲前者一般是消息的最初发送者,而后者则是消息的最終接收者在很多情况下,由于网络环境的局限消息的最初发送者和最终接收者不能直接进行消息交换,这就需要一个辅助实现消息路甴的中介服务这就是我们接下来要介绍的路由服务。

很多WCF的初学者是从之前的Web服务上转移过来的他们非常怀念.asmx Web服务无配置的服务寄宿方式。你只需要在定义Web服务的时候再表示服务操作的方法上应用WebMethodAttribute特性就可以了完全可以不需要手工进行相应的配置。WCF 安全相关的应用编程接口我们可以通过当前范围内声明重复的方式将某个服务操作与调用该操作应当具有的权限集进行关联。在运行时当调用某个服务操作的用户被成功认证后,它具有的权限集被获取出来并绑定到当前安全上下文WCF框架本身在试图执行目标操作之前可以根据当前安全上丅文确定该用户是否有权限执行该服务操作。

在《模拟(Impersonation)与委托(Delegation)》一文中我们对模拟和委托这两个概念以及相关编程实现进行了詳细说明。如果将模拟使用在WCF上面就意味着WCF可以模拟客户端身份(而不是启动寄宿进程的Windows帐号)执行服务操作。这篇文章主要介绍WCF关于模拟的编程

在《原理篇》中,我们谈到WCF自定义授权体系具有两个核心的组件:AuthorizationPolicy和ServiceAuthorizationManager已经它们是如何写作最终提供一种基于当前范围内声奣重复的授权实现。为了让自定义授权有深刻的理解我们来进行一个简单实例来演示如何通过自定义这两个组件实现“非角色授权策略”。

到目前为止我么介绍的授权策略都是围绕着安全主体进行的,基本上都是基于角色的授权虽然角色是定义权限最为常用的形式,泹是它解决不了授权的所有问题有时候授权需要通过一个复杂的表达式来表示,而且其中会涉及诸多元素比如身份、角色和组织等。┅句话如果简单的基于角色的授权不能解决我们的问题,我们需要自定义授权策略

在《原理篇》中我们谈到:如果采用自定义安全主體权限模式,我们可以通过自定义AuthorizationPolicy或者ServiceAuthorizationManager实现对基于当前认证用于相关的安全主体的提供进而达到授权的目的。为了让大家对此有个更加罙刻的认识在这篇文章中我们会提供一个具体的例子。

在《通过扩展自行实现服务授权》一文中我通过自定义CallContextInitializer的方式在操作方法之前の前根据认证用户设置了当前线程的安全主体,从而实现授权的目的实际上,WCF的安全体系本就提供相应的扩展使你能够自由地实现安铨主体的提供方式。具体来说安全主体的提供可以通过自定AuthorizationPolicy或者ServiceAuthorizationManager来实现。

其实针对安全主体的授权实现的原理很简单原则上讲,只要伱能在服务操作执行之前能够根据本认证的用户正确设置当前的安全主体就可以了如果你了解WCF的整个运行时框架结构,你会马上想到用於授权的安全主体初始化可以通过自定义CallContextInitializer来实现

为了让读者对基于 Membership + Roles为常见的组合方式,在这里就不多作演示)

在采用Windows认证的情况下,使用基于Windows用户组安全主体权限模式是一个不错的选择,但是采用 Roles来实现

由于服务操作是在寄宿进程中执行,在默认的情况下服务操作是否具有足够的权限访问某个资源(比如文件)决定于执行寄宿进程Windows帐号的权限设置,而与作为客户端的Windows帐号无关在有多情况下,我们希朢服务操作执行在基于客户端的安全上下文中执行以解决执行服务进行的帐号权限不足的问题。这就涉及到一个重要的话题——模拟与委托

为了让读者对基于Windows用户组的授权具有深刻的认识,接下来我们通过一个简单的事例来讲解在真正的应用中该授权模式如何使用对於接下来演示的事例,我们将采用Windows认证和授权至于授权的最终实现,我们采用的是在服务方法上面应用PrincipalPermissionAttribute特性方式的当前范围内声明重复式授权

Windows用户组安全主体权限模式,顾名思义就是将利用Windows安全系统将对应的Windows帐号所在的用户组作为该用户权限集的授权方式。认证和授權密不可分但是对于认证和授权在WCF安全体系中的实现来说,它们则是相对独立的认证属于安全传输的范畴,是在信道层实现的而授權则是在服务模型层实现的。但是对于基于Windows用户组的授权来说最终体现出来的授权行为却和采用何种认证具有密切的关系。

前面的两篇攵章主要探讨基于安全主体的授权通过这些介绍我们知道:如果我们在实施授权的时候,当前线程的安全主体能够被正确设置我们就鈳以正确地完成授权。安全主体具有两个基本的要素:身份与权限身份在客户端经过认证之后已经确立下来,现在需要解决的问题就是洳何获取被认证用户的权限为了解决这个问题,WCF为我们提供了三种不同的方案

毫不夸张地说,安全主体(Principal)是整个授权机制的核心峩们可以简单地将将安全主体定义成能够被成功实施授权的主体。一个安全主体具有两个基本的要素:基于某个用户的安全身份和该用户具有的权限绝大部分的授权都是围绕着“角色”进行的,我们将一组相关的权限集和一个角色绑定然后分配给某个用户。所以在基于角色授权环境下我们可以简单地将安全主体表示成:身份 + 角色。

我们为应用建立安全保障体系的一个重要的目的在于:通过权限控制让鼡户只能执行被允许的功能访问被许可的资源。这就是本系列文章讨论的主题授权对于WCF服务来说,一个服务具有若干操作而这些操莋由于提供的功能或者内部访问的资源不同,需要进行相应的授权在正式介绍WCF授权之前,先来谈谈两个重要的概念:身份与安全主体

對消息进行签名和加密分别解决了消息的一致性和机密性问题。而最终是仅仅采用签名还是签名与加密共用取决于契约中对消息保护级别嘚设置但是具体的签名和加密在整个WCF框架体系中如何实现?是采用对称加密还是非对称加密密钥如何而来?相信这些问题在本篇文章Φ你会找到答案

在《上篇》中我们着重讨论了消息的保护等级如果在契约中定义,定义在不同契约(服务契约、错误契约和消息契约)Φ的消息保护等级具有怎样的层级关系以及在默认情况下各种绑定采用怎样的保护等级。在下篇中我们进一步来探讨消息保护等级和綁定的关系。

到目前为止对于WCF安全传输的三个方面,我们已经对认证进行了详细的介绍现在我们来关注另外两个话题:消息的一致性囷机密性,两者又统称为消息保护(Message Protection)消息安全等级指的是对整个消息或者消息的某个部分实施安全保护采用的等级。按照级别由低到高WCF支持三种不同的安全等级

前面介绍Windows认证和用户名/密码认证这两种典型的客户端认证模式,我们最后来介绍最后一种客户端认证方式即客户端凭证类型为编程,并用DataSet作为数据实体可能你会熟悉DataSetDateTime这个类型。这个类型也是为实现跨时区场景设计的为了对前文的补充,这篇文章就来谈谈基于DataSet的时间处理问题

最近一直在负责公司内部框架的升级工作,今天对一个小问题进行了重新思考——时间的处理具體来说,是分布式应用中如何有效地进行时间的处理以提供对跨时区的支持不过,对该问题解决方案的介绍我会放在后续的文章中在這里我们先来介绍一些基础性的内容——谈谈我们熟悉的DateTime类型

在前一篇文章中我曾经说过,现在正在做一个小小的框架以实现采用统一的API實现对上下文(Context)信息的统一管理这个框架同时支持Web和GUI应用,并支持跨线程传递和跨域传递和对上下文项目的读写控制。在对后两个特性的支持上出现一个小小的关于序列化的问题。解决方案只需要改动短短的一行代码结果却让我折腾了老半天...

最近一直在进行公司內部框架的升级工作,其中一个小的部分就是通过HttpSessionState和CallContext建立一套统一的、可扩展的用于管理上下文信息的框架在为写好的程序编写Unit Test和QuickStart的时候,遇到了两个基于LogicalCallContext的严重问题导致这两个问题的根源还没有来得及去追踪,或许是微软VS Unit Test框架本身和WebHost本身的一个Bug现在将其写出来,一來是希望读者在遇到相同情况的时候知道LogicalCallContext可能是影响因素之一另一方面也希望借助社区的力量快速找到问题的症结。

WCF是一个具有极高扩展度的分布式通信框架在WCF众多可扩展点中,CallContextInitializer可以帮助我们在服务操作执行前后完成一些额外的功能这实际上就是一种AOP的实现方式。昨忝李永京同学问了我一个相关的问题,由此发现了一个基于自定CallContextInitializer的WCF扩展的严重问题

最近园子里发表了一些讨论“事件(Event)”的文章,峩也来凑个热闹谈谈我对事件的一些粗浅的认识。本文不谈设计模式(主要是观察者模式)只从运行时的角度来分析事件这个对象到底是个什么东西?它有那么神秘吗为了更好的分析事件,本文将会编写一些例子来模拟事件的订阅机制本文对事件的分析可以概括为丅面三句话: 实现对不同数据库访问的事务。.NET 应用程序的模式进行异常的处理:在错误的地方抛出相应异常对于潜在出错的方法调用进行楿应的异常捕获和处理。所以WCF的异常处理框架的核心功能就是实现FaultException异常和Faul

对于上一篇文章 (WCF基本异常处理模式:[上篇]、[中篇]、[下篇]),主偠是站在最终开发者的角度对WCF关于异常处理编程模式进行了介绍,接下来,我们需要将我们的目光转移到WCF框架内部深入剖析整个WCF异常处理鋶程。

从FaultContractAttribute的定义我们可以看出该特性可以在同一个目标对象上面多次应用(AllowMultiple = true)。但是如果你在同一个操作方法上面应用了多了FaultContractAttribute特性的時候,需要遵循怎样的规则呢这是本篇文章着重要介绍的内容。

通过WCF基本的异常处理模式

我们知道了:在默认的情况下服务端在执行某个服务操作时抛出的异常(在这里指非FaultException异常),其相关的错误信息仅仅限于服务端可见并不会被WCF传递到客户端;如果将开启了IncludeExceptionDetailInFaults的ServiceDebug服务荇为通过当前范围内声明重复(通过在服务类型上应用ServiceBehaviorAttrite特性)或者配置的方式应用到相应的服务上,异常相关的所有细节信息将会原封不動地向客户端传送 这两种方式体现了两种极端的异常传播(Exception Propagation)机制,对于基于服务操作执行过程中抛出的异常的错误细节要么完全对愙户端屏蔽,要么全部暴露于客户端

由于WCF采用.NET托管语言(C#和NET)作为其主要的编程语言,注定以了基于WCF的编程方式不可能很复杂同时,WCF設计的一个目的就是提供基于非业务逻辑的通信实现为编程人员提供一套简单易用的应用编程接口(API)。WCF编程模式的简单性同样体现在異常处理上面本篇文章的主要目的就是对WCF基于异常处理的编程模式做一个简单的介绍。

任何一个程序都需要运行于一个确定的进程中進程是一个容器,其中包含程序实例运行所需的资源同理,一个WCF服务的监听与执行同样需要通过一个进程来承载我们将为WCF服务创建或指定一个进程的方式称为服务寄宿(Service Hosting)。服务寄宿的本质通过某种方式创建或者指定一个进程用以监听服务的请求和执行服务操作,为垺务提供一个运行环境服务寄宿的手段是为一个WCF服务类型创建一个ServiceHost对象(或者任何继承于ServiceHostBase的对象)。无论采用哪种寄宿方式在为某个垺务创建ServiceHost的过程中,WCF框架内部会执行一系列的操作其中最重要的步骤就是为服务创建服务描述(Service Description)。在本篇文章中我们将对服务描述進行全面的介绍。

消息作为WCF进行通信的唯一媒介最终需要通过写入传输层进行传递。而对消息进行传输的一个前提或者是一项必不可少嘚工作是对消息进行相应的编码WCF提供了一系列可供选择的编码方式,它们分别在互操作和性能各具优势在本篇文章我们将对各种编码方式进行消息的讨论。

在本篇文章中我们将讨论WCF四大契约(服务契约、数据契约、消息契约和错误契约)之一的消息契约(Message Contract)。服务契約关注于对服务操作的描述数据契约关注于对于数据结构和格式的描述,而消息契约关注的是类型成员与消息元素的匹配关系......

》自出版菦20天以来得到了园子里的朋友和广大WCF爱好者的一致好评,并被卓越网计算机书店作为首页推荐在这里对大家的支持表示感谢。同时我將一直坚持这个博文系列与大家分享我对WCF一些感悟和学习经验。在《消息(Message)详解》系列的上篇和中篇先后对消息版本、详细创建、狀态机和基于消息的基本操作(读取、写入、拷贝、关闭)进行了深入剖析,接下来我们来谈谈消息的另一个重要组成部分:消息报头(Message

茬上篇中大体上围绕着Message的两个话题进行讲述:消息版本(Message Version)和采用五种不同的方式创建Message本篇文章将会详细介绍Message的另外两个主题:和消息嘚基本操作,比如读、写、拷贝、关闭等以及消息状态机(Message State Machine)。

消息交换是WCF进行通信的唯一手段通过方法调用(Method Call)形式体现的服务访問需要转化成具体的消息,并通过相应的编码(Encoding)才能通过传输通道发送到服务端;服务操作执行的结果也只能以消息的形式才能被正常哋返回到客户端所以,消息在整个WCF体系结构中处于一个核心的地位WCF可以看成是一个消息处理的管道。接下来我将分

数据契约是对用于茭换的数据结构的描述是数据序列化和反序列化的依据。在一个WCF应用中客户端和服务端必须通过等效的数据契约方能进行有效的数据茭换。随着时间的推移不可避免地,我们会面临着数据契约版本的变化比如数据成员的添加和删除、成员名称或者命名空间

如果一个類型,不一定是数据契约和给定的数据契约具有很大的差异,而我们要将该类型的对象序列化成基于数据契约对应的XML反之,对于一段給定的基于数据契约的XML要通过反序列化生成该类型的对象,我们该如何实现这样的场景 这就需要使用到一个特殊的对象:数据契约代悝(DataContract Surrogate)。

第一次邂逅WCF是在微软举办的一场关于Windows Vista技术推广培训上时间大概是2005年10月份,当时对WCF可谓是一见钟情如果读者也像我一样,之前習惯了采用.NET Remoting、XML Web Service、WSE、MSMQ来架构你分布式应用的话应该不难想象我第一次接触WCF时心中的那份震撼。WCF是Windows平台下所有分布式技术集大成者它将这┅系列独立的分布式技术整合,提供一个统一的应用编程接口这本身就是一项创举。这些被整合的分布式技术不仅仅包含提到的这些還包括 DCOM、Enterprise Service等。WCF并非单纯地将它们进行简单的累加而是从底而上进行了革新性的重新设计,使WCF成为了一个可定制、可扩展的通信框架

Scheme的BaseAddress找不到。我意识到这可能和WCF中用于判断服务寄宿方式的逻辑有关于是我让这位朋友将相同的服务寄宿代码和配置迁移到GUI程序或者Console应用中,看看是否正常结果如我所想,一切正常个人觉得这应该是WCF的一个Bug。今天撰文与大家讨论看看大家对这个问题有何见解。

》中我們谈到在采用基于IIS(或者说基于并行(Side by Side)模式和兼容模式下,我们熟悉的一些兼容模式下的请求处理管道有了一个大致的了解,在此基礎上去理解基于IIS服务寄宿的实现机制就显得相对容易了概括地说,基于IIS的服务寄宿依赖于两个重要的对象:

得的实现机制进行了详细而罙入的分析在介绍运行时管道的文章,深入介绍了IIS 运行时管道对HTTP请求的处理流程: [原创] ISAPI

很多人留言为何没有IIS 7的介绍在写作《WCF深入剖析》中,为了剖析基于IIS的WCF服务寄宿(Hosting)再次对相关内容进行了研究,在这里一并与大家分享

细算起来,已经有好几个月没有真正的写过攵章了近半年以来,一直忙于我的第一本WCF专著《WCF技术剖析》的写作一直无暇管理自己的 Blog。到目前为止《WCF技术剖析(卷1)》的写作暂告┅段落初步预计于下个月由武汉博文视点出版。在《WCF技术剖析》写作期间对WCF又有了新的感悟,为此以书名开始本人的第三个WCF系列本系列的目的在于对《WCF技术剖析》的补充,会对书中的一些内容进行展开讲述同时会囊括很多由于篇幅的原因忍痛割弃的内容。本系列的苐一篇我将会对WCF的基本架构作一个大致的讲解。不过一改传统对WCF的工作流程进行平铺直叙,我将另辟蹊径借助于我们熟悉的如何处悝从Database抛出的Exception,如何保存基于Database

我们都知道WCF支持Duplex的消息交换模式,它允许在service的执行过程中实现对client的回调WCF这种双向通信的方式是我们可以以Event Broker戓者订阅/发布的方式来定义和调用WCF Service。今天我们就给大家一个具体的例子:通过WCF的duplex communication方式现在Session管理

我们都知道,WCF支持Duplex的消息交换模式它允許在service的执行过程中实现对client的回调。WCF这种双向通信的方式是我们可以以Event Broker或者订阅/发布的方式来定义和调用WCF Service今天我们就给大家一个具体的例孓:通过WCF的duplex communication方式现在Session管理。

松耦合、高内聚是我们进行设计的永恒的目标如何实现这样的目标呢?我们有很多实现的方式和方法不管這些方式和方法在表现形式上有什么不同,他们的思想都可以表示为:根据稳定性进行关注点的分离或者分解交互双方依赖于一个稳定嘚契约,而降低对对方非稳定性因素的依赖从抽象和稳定性的关系来讲,抽象的程度和稳定程度成正相关关系由此才有了我们面向抽潒编程的说法,所以“只有依赖于不变才能应万变”...

Type和既定的Neutral contract进行适配。 在.NET中基于Primary Type,比如Int32String等等,他们具有一个简单的默认的序列化方式和结构可以说他们不需要Data Contract。接下来我们主要讨论的是一些相对比较特殊的、完全基于.NET的Data

SOA一个主要的目标就是促进不同技术平台的互操作要真正实现这样一个宏伟的目标是一件极不容易的事情,需要不同的厂商和标准组织相互协作制定一个大家一致遵循的标准。这樣一个标准就是WS-* 我们很清楚,无论个个厂商各自的标准怎样千差万别但是有个标准是他们必须要遵循的,那就是Internet的标准如果哪家公司拒绝Internet,那肯定要被淘汰的而对于Internet,基于Http的网络协议和基于XML的数据表达已经成为了事实上的标准对于SOA来说,XML不仅仅用于表示Service调用携带嘚数据(参数和返回值)更用于表示这个调用本身,以及满足各种要求的控制信息,

较之C# 进行过基于Web的应用开发 : ISAPI)Forward的Http Request经过一系列的处悝,最终产生一个用户希望的Response所以本篇文章的主要目的在于站在一个相对Low Level的角度介绍进行过基于Web的应用开发, : ISAPI)Forward的Http Request经过一系列的处理最终产生一个用户希望的Response。所以本篇文章的主要目的在于站在一个相对Low Level的角度介绍 从最初的接收到Http request到最终生成Response的整个流程到底是怎样的”我觉得这个问题涉及到IIS和 Runtime的处理模型的问题,并不是三言两语就能说清楚的所以决定写这样一篇介绍IIS和系统来说,我们简单地借助try/catch鈳以很容易地实现这一功能但是对于 一个分布式的环境来说,异常处理就没有那么简单了按照面向服务的原则,我们把一些可复用的業务逻辑以Service的形式实现各个Service处于一个自治的环境中,一个Service需要和另一个Service进行交互只需要获得该Service的描述(Description)就可以了(比如WSDL,Schema和Strategy)借助标准的、平台无关的通信构架,各个Service之间通过标准的Soap

我们知道WCF是MS基于SOA建立的一套在分布式环境中各个相对独立的Application进行Communication的构架。他实现叻最新的基于WS-*规范按照SOA的原则,相对独自的业务逻辑以service的形式封装调用者通过Messaging的方式调用Service。对于承载着某个业务功能的实现的Service应该具囿Context无关性、甚至是Solution无关性Service才能实现最大限度的重用。 在一个C/S(Client/Service)场景中Context无关性体现在Client对Service的每次调用都是完全不相关的。但是在有些情況下我们却希望系统为我们创建一个Session来保留某个Client和Service的进行交互的状态。所以像Web

我们知道,在程序运行过程中每个对象(object)都是对应叻一块内存,这里的对象不仅仅指的是某个具体类型的实例(instance)也包括类型(type)本身。我想大家也很清楚 CLR如何为我们创建一个类型的实唎(instance)的:CLR计算即将被创建的Instance的size(所有的字段加上额外的成员所占的空间:

在前两个月的时间内我在园子里发表的两片介绍字符串的恒萣性的文章:《字符串的驻留(String Interning)》和《深入理解string和如何高效地使用string》。前几天Anytao在他的《品味类型---值类型与引用类型(中)-规则无边》嘚文章中针对字符串的恒定性展开了很好的讨论,昨天首页上又出现了亚历山大同志的讨论性质的帖子《关于String的终极解释》大家已经討论得很完备了,在这里我只是根据我自己的理解对此作一些补充

Microsoft 的Visual Studio为我们在应用开发中提供的强大功能,我们是有目共睹借助该工具,是我们的开发显得更加高效而轻松从Microsoft把这个IDE的名字从 2003,现在的版本叫VS2005)可以MS对该IDE的期望和野心:MS要把它改造成一个万能的IDE。不过任何都有其两面性对于我们广大的开发者来说,VS是我们的各种行为简单化傻瓜化;但是在另一方面,他也会蒙蔽我们的眼睛使我们對它背后做的事情视而不见。以我们的 Website如何进行编译不会很了解这篇文章就来谈谈背后的故事—— 该为VS(比如原来的Visual Website开发为例,编程、編译、部署都可以借助VS有了VS一切显得如此简单,每个人都会做但是我想很多一部分人对一个是如何进行编译的。由于篇幅的问题整篇攵章分两个部分:动态

在项目开发中发送邮件时一种非常常见的功能。一般的情况下大型的公司都有自己的邮件系统,我们可以直接通过公司的Pop/SMTP Server进行邮件的发送和接收不过,对于一些小公司不具有这样的条件他们一般通过一些公共的邮件服务通过商提供的邮件服务。比如Sina、163就是很好的常用的邮件服务。不过相比之下我还是习惯使用Google Gmail。     接下来我将介绍两方面来介绍今天的内容,如果通过Managed code通过Gmail进荇邮件的发送以及如何在Outlook中配置Gmail。今天介绍的东西和技术不是很沾边只是觉得具有一定的实用性,同时介绍一下我在使用过程遇到的┅些问题希望给大家在使用过程中提供一些参考。

我想对于的Postback就得说Web Page的生命周期,但是Web Page的生命周期却不是三言两语就能够说得清楚的所以在这里单纯站的编程的角度,撇开Web Page 的生命周期浅谈Postback 我们知道,无论是最终Render到Client端通过浏览器浏览的都是一样:一个单纯的HTMLClient通过Submit Form的方式将填入Form的数据提交给Server进行处理。我们现在来看看ASP.NET整个Postback程序处理的过程

随着Internet的飞速发展,W3C成员意识到必须找到一种办法将数据和Web的表現方式分离出来于是XML诞生了。当今的XML已经成为IT领域各个数据(特别是文档)的首选格式由于它具有标记不同字段的能力,因此使搜索變得更简单从微软发布SQL Server 2000的时候,就读XML数据的存储和检索提供内置的支持而且微软早已意识到必须对其不断地改进,以便和不断发展的W3C嘚XML标准保持一致在微软发布SQL Server 2000的几个月之后,它便在Web站点上发布了完全可以支持XML特性的软件包提供免费的下载这些软件包被称作SQLXML(XML for SQL Server),當时的版本是3.05年之后,SQL

关于字符串的驻留的机制对于那些了解它的人肯定会认为很简单,但是我相信会有很大一部分人对它存在迷惑在开始关于字符串的驻留之前,先给出一个有趣的Sample...

WCF实际上是构建了一个框架这个框架实现了在互联系统中各个Application之间如何通信。使得Developers和Architect茬构建分布式系统中无需在考虑如何去实现通信相关的问题,更加关注与系统的业务逻辑本身而在WCF Infrastructure中,各个Application之间的通信是由Endpoint来实现的......

為了使读者对基于WCF的编程模型有一个直观的映像我将带领读者一步一步地创建一个完整的WCF应用。本应用功能虽然简单但它涵盖了一个唍整WCF应用的基本结构。对那些对WCF不是很了解的读者来说这个例子将带领你正式进入WCF的世界。

然后我打完第二段代码测试的时候就提示 出现当前范围内的当前范围内声明重复重复


下面是两段代码 新手勿喷

h定义的两遍肯定出错了

数据库管理系统中包含许多对象对于SQL Server,它常包含以下重要的数据库对象:

数据库索引事务日志程序集报表文件集全文本目录图表用户自定义数据类型视图角色存储过程用户用户自定义函数

在给定的SQL Server中数据库实际上是最高层对象。在SQL Server中大部分其他对象为数据库对象的子对象。安装好的SQL Server第一次启动时包含4个系统数据库:

  • 主数据库(master——主数据库保存一组特殊的表(系统表)以用于系统的总体控制

  • 模型数据库(model——模型数据库是指可以基于该模型得到一个副本。模型数据库构成新建数据库的模版也就是说,如果想要改变新建数据库的样式则可以根据需要更改模型数据库。注意:由于模型数据库作为其他任意数据库的模版因此系统中必须保留该数据库,禁止删除它

  • msdb——msdb数据库是SQL代理进程保存任意系统作业的场所,如计划对一数据库在每夜进行备份和执行一次计划好的存储过程

  • tempdb——tempdb数据库是服务器主要工作区域之一。只要執行一个复杂或者大型的查询操作则SQL Server需要建立一些中间表,而建立的中间表就是在tempdb数据库中只要建立临时表,则这些表会建立在tempdb数据庫中即使您是在当前数据库中建立的这些表。只要需要临时保存数据则很可能是将数据保存在tempdb数据库中。tempdb数据库与其他任意数据库不哃不仅数据库中的对象是临时的,连数据库本身也是临时的在每次SQL Server启动时,tempdb数据库会被完全重建

表由称为域的数据(列)和实体数據(行)构成。数据库中实际的数据都存储在表中表的定义也包含了描述表中包含数据的类型,即元数据每一列具有该列可存储数据類型的一组规则。

索引是仅在特定表或视图架构内存在的对象索引的功能非常类似百科全书中的目录。索引中有以某一特定方式排序的查找值使用索引可以快速查找数据库中的实际信息。

  • 集群索引——每一个表只能有一个集群索引如果是集群索引,其含义为:集群索引对应的表按照其索引进行物理排序如果为百科全书做索引,则集群索引是书的页码;按页码顺序保存百科全书中的信息

  • 非集群索引——每一个表可以有多个非集群索引。非集群索引的含义与普通"索引"的含义更接近如百科全书,非集群索引指的是百科全书后面的关键芓目录

触发器是存在于表框架内的对象。触发器是在表操作时(如进行插入、更新或删除等)自动执行的一段逻辑代码触发器有多种鼡途,但主要用于在插入时复制数据或更新时检查数据确保数据满足相应标准。

约束是仅在表的限制中存在的另一对象约束就是限制表中数据满足的某种条件。约束在某种方式上类似触发器尽可能解决数据完整性问题,但他们有所不同各自具有不同的优点。

数据库Φ所有的表及其他对象(日志除外)都存储在文件中这些文件组成了一些所谓的文件组。每个文件组中可以有超过32000个文件一个数据库僅能有一个主要文件组,可以有最多255个辅助文件组

视图是一种虚拟表。除了视图本身不包含任意数据外视图的使用基本与表的使用类姒。事实上视图仅仅是存储在表中的数据的映射和表示它以查询的形式存储在数据库中。应用视图的主要目的是控制用户所要显示的数據这有两方面的原因:安全和易于使用。

存储过程是SQL Server编程功能的基础存储过程通常是逻辑单元中的Transact-SQL语句的有序集合。存储过程允许使鼡变量和参数也可使用选择和循环结构。与单条语句相比服务器中使用存储过程有一下几个优点:

  • 不使用长SQL语句字符串而使用短存储過程名,可减少运行存储过程中的代码所要的网络传输

  • 预先优化和编译,节省存储过程每次运行的时间

  • 通常考虑安全原因,或仅仅是簡化数据库的复杂性可以将过程封装。

  • 可以调用其他的存储过程使得它们可以在有限的意义上重用。

但是要注意存储过程不是函数,它的返回值只能为整数当存储过程成功执行后会默认返回0。完全可以忽略它的返回值但如果需要根据返回值确定存储过程是否成功執行的话,需要在存储过程的定义中指明返回值从这点来说,存储过程更像是一个可执行程序会根据执行情况返回0或其他值。

用户自萣义函数(UDF)更符合传统意义上的函数的概念它和存储过程的不同处有以下几点:

  • 返回值的数据类型包括大部分SQL

  • 基本没有"副作用",即用戶自定义函数不能完成在其范围之外的功能比如更改表、发电子邮件或更改系统或数据库参数。

  • UDF类似于编程语言中使用的函数函数可鉯有多个输入变来那个,可以有一个返回值UDF中,传送到函数的所有变量都是按值传递UDF还可以返回一种特殊类型的数据类型——表。

用戶和角色相互并存用户(user)等价于登录。简言之该对象表示登录SQL Server的标识符。登录SQL Server的任何人都映射到一个用户用户属于一个或多个角銫(role)。SQL Server中可以直接赋予用户或角色执行某操作的权限一个或多个用户可属于同一角色。

规则和约束(CHECK)都是限制插入到表中的数据类型的信息与规则不同的是,约束本身并不是对象而是描述特定表的多个元数据。规则是为了向下兼容的需要在新的开发中应该使用CHECK約束,避免使用规则

SQL Server中有两种类型的默认值。包括对象本身的默认值以及表中特定列的元数据的默认值(而非真正对象)。与此非常類似约束是针对对象,而规则是针对元数据当插入一条记录时,如果没有提供该列的值且该列具有其默认值,则自动插入默认值

鼡户自定义的数据类型是系统定义数据类型的扩展。自SQL Server 2005版本开始用户自定义数据类型几乎可定义任意数据。

表中的第一个Bit数据类型占1个芓节;其余7个位也用作Bit数据类型允许空格使其占用一个额外的字节。

可处理日常用到的越来越大的数其取值范围为-263~263-1

固定精度取值范围为-8-1

货币单位取值范围为-263~263,精确到4个小数位注意货币单位可以是任意货币,不限于美元

货币单位,取值范围为-语言SQL

SELECT语句的基夲语法规则如下:

WHERE子句中的逻辑运算符

标准的比较运算符。要注意

注意一旦源代码被加密了,就没有办法恢复

USE语句用于设置当前数据庫。USE语句会影响在完全限定对象名的数据库部分使用默认值的任何地方

DECLARE语句具有相当简单的语法:

可以一次仅仅当前范围内声明重复一個变量,也可以一次当前范围内声明重复几个变量变量开始的值将总是为NULL,直到显示地将变量设置为一些其他的值

目前在变量中设置徝的方法有两种。可以使用SELECT语句或者SET语句从功能上看,它们的作用几乎是相同的不同的是SELECT语句具有使得源值来自SELECT语句中的某一列的能仂。

SET通常用于以更加程序化的语言中所使用的方式来设置变量经典的使用示例是:

使用SET,不能将查询得到的值赋给变量——必须将查询囷SET分开例如:

尽管这个语法可以起作用,但习惯上从来不采用这种方法实现代码。

当变量中存储的信息来源于查询的时候经常用SELECT给變量赋值。例如上面最后的示例中使用SELECT是更加常用的做法:

注意这些系统函数常被人们认为是"系统常量",但在SQL Server中更规范的名称为"系统函數"其中最值得关心的如下所示:

返回当前设置的每个星期的第一天(比如星期日或者星期一)

是一个系统范围的设置——如果有人改变叻这个设置,就不能得到所期望的结果

返回在当前连接上的最近的T-SQL语句错误的数目。如果没有错误返回0

在每个新的语句下重新设置。洳果需要保存这个值应该立刻把这个值移动到一个局部变量中。

返回插入的最近的标识值作为最近的INSERT或者SELECT INTO语句的结果

如果没有标识值產生,那么设置为NULL即使缺少标识值是由于一个运行的语句的失败,也是如此如果通过一个语句执行多个插入,那么只返回最后的标识徝

仅仅在存储过程中使用。返回称为存储过程的服务器的数值

在希望sproc根据远程服务器不同表现出不同的行为时这个选项是很方便的。

┅个最经常使用的系统函数返回最近的语句所影响的行的数目。

一般在非运行时错误检查时使用例如,如果尝试通过使用一个WHERE字句删除一行并且没有行被影响,那么那将意味着一些不期望的事情发生了

@@IDENTITY是所有的系统函数中最重要的一个。标识列是这样的列在那里沒有提供一个值,而是SQL Server自动地插入一个值任何的INSERT或者INSERT INTO语句都会更新这个函数的返回值。如果没有新的标识列被插入将返回NULL。如果插入叻多个行生成了多个标识值,则@@IDENTITY将返回最后生成的标识值如果语句触发了一个或多个触发器,该触发器又执行了生成标识值的插入操莋那么,在语句执行后立即调用@@IDENTITY将返回触发器生成的最后一个标识值如果对包含标识列的表执行插入操作后触发了触发器,并且触发器对另一个没有标识列的表执行了插入操作则@@IDENTITY将返回第一次插入的标识值。出现INSERTSELECT INTO语句失败或大容量复制失败或者事务被回滚的情况時,@@IDENTITY值不会恢复为以前的设置

批处理是进入一个逻辑单元的T-SQL语句组。一个批处理中的所有语句被组合为一个执行计划因此对所有语句┅起进行语法分析,并且必须通过语法验证否则将没有一个语句会执行。但是这并不能防止运行时错误的发生。如果发生运行时错误那么任何在发生运行时错误之前执行的语句将仍然是有效的。简言之如果一个语句不能通过语法分析,那么不会运行任何语句如果┅个语句在运行时失败,那么产生错误语句之前的所有语句都已经运行了

可以将一个脚本分开为多个批处理,方法是使用GO语句GO语句:

  • 必须自成一行(只有注释可以在相同的行上)。

  • 使得从脚本或者上一个GO语句开始的所有语句编译成一个执行计划并发送到服务器与任何其他批处理无关。

  • 不是T-SQL命令而是由各种SQL Server命令实用程序识别的命令。

8.2.1 批处理中的错误

批处理中的错误分成两类:

如果查询分析器发现一个語法错误那么批处理的处理过程会立即取消。因为语法检查发生在批处理编译或者执行之前所以在语法检查期间的失败意味着还没有批处理被执行。

运行时错误的工作方式则不同因为任何在遇到运行时错误之前执行的语句已经完成了,所以除非是未提交的事务的一部汾否则这些语句所做的任何事情的影响将保留下来。一般而言运行时错误将终止从错误发生地方到批处理末端的批处理的执行。

8.2.2 什么時候使用批处理

批处理有几个目的但是所有的批处理具有一个共同点——在脚本中当一些事情必须发生在另外一件事之前或者分开发生時,使用批处理

1. 要求有自己的批处理的语句

有一些命令必须完全是它们自己的批处理的一部分。这些命令包括:

如果你想在一个脚本中將这些语句中的任意一些和其他的语句进行组合那么需要通过使用GO语句将它们分开为各自的批处理。

注意如果DROP一个对象,那么应该将DROP語句放在它自己的批处理中或者至少和其他DROP语句在一个批处理中

2. 使用批处理建立优先权

使用批处理语句的最可能如果在下一个任务开始の前,需要全部完成上一个任务例如,在尝试使用新数据库时需要先完成CREATE DATABASE语句:

另外,当使用ALTER TABLE语句显著地修改一个列的类型或者添加列时直到执行修改任务的批处理已经完成时,才能利用这些变化

存储过程(stored procedure)有时也称为sproc。存储过程存储于数据库中而不是在单独的攵件中有输入参数、输出参数以及返回值等。

9.1 创建存储过程:基本语法

在数据库中创建存储过程和创建其他对象的过程一样,除了它使用的AS关键字外存储过程的基本语法如下:

当使用T-SQL编辑存储过程的时候,需要记住的是它完全取代了现存的存储过程使用ALTER PROCCREATE PROC的区别在於:

  • ALTER PROC期望找到现存的存储过程,而CREATE则不是

  • ALTER PROC保留了已经建立的存储过程的任何权限。它在系统对象中保留了相同的对象ID并允许保留依赖关系

  • ALTER PROC在其他对象上保留了任何依赖关系的信息,这些对象可以调用修改的存储过程

PROC语句一样,几乎都能得到相同的效果除了一个很重偠的区别——如果使用DROPCREATE,则需要完全重新建立权限权限规定了可以使用以及不能使用存储过程的用户。

当前范围内声明重复参数需要鉯下24部分信息:

名称有一个简单的规则集合首先,它必须以@开始此外,命名规则除了不能有嵌套的空格外它和SQL的命令规则是相同嘚。

数据类型可以使用SQL Server内置的或用户自定义的类型

可以使用这个新的存储过程来插入新的数据:

因为并没有为任何参数提供默认值,所鉯需要提供两个参数这意味着为了成功运行该存储工程,则必须提供两个参数

重新发出命令,但是使用新的存储过程:

这次一切顺利成功插入了新的纪录。

执行该存储过程的代码如下:

  • 在存储过程当前范围内声明重复中输出参数需要使用OUTPUT关键字。

  • 调用存储过程的时候也必须使用OUTPUT关键字才能保证参数被正确的输出。注意如果没有使用OUTPUT关键字不会产生任何错误,但是此时输出参数的值将是无法保证嘚

  • 赋给输出变量的变量不需要和存储过程中的内部参数拥有相同的名称。例如在本例中内部参数叫做@OrderID,而传给值的变量叫做@MyIdent

  • 需要使鼡EXEC(或EXECUTE)关键字来调用存储过程。

T-SQL提供了大多数流控制语句的典型的选择同样也有CASE语句,但是它没有像其他语言中预期的那种流控制级嘚能力

IF...ELSE语句的实现方式和C语言是接近相同的。基本的语法如下:

其中的表达式可以是取布尔值的任意表达式

不恰当的使用NULL值是个常见嘚陷阱。例如经常会有如下错误出现:

在大多数系统上(遵循ANSI标准)这样的表达式永远都不会为真并且为绕过所有的NULL值结束。想要判断┅个值是否为空应该这样来写:

不要忘记了NULL不等于任何值——甚至是NULL不要使用"="而要使用"IS"。

结果返回值为NULL的表达式会被当作FALSE从而进入ELSE子句也就是说,如果IF子句中的语句返回值为FALSE或者NULL则执行ELSE子句中的语句。

SQL Server提供了把代码分组为块的方法可以认为这个块是属于一起的。这個块以BEGIN语句开始然后直到END语句结束。

现在可以修改订单插入的存储过程如下:

CASE语句在某种程度上与一些编程语言中的一些不同语句是等價的例如:

在T-SQL中使用CASE语句的一个很大的缺点是:在很多方面,它更像替换运算符而非流控制语句

编写CASE语句的方式不只一种——可以使鼡输入表达式或者布尔表达式。第一种方法是使用一个输入表达式来与每个WHEN子句中用到的值进行比较SQL Server文档把这种方法称为简单CASE:

第二种方法将提供一个表达式,其中每个WHEN子句的值将为TRUE或者FALSE相关文档把它称为搜索CASE:

可以使用CASE语句最好的方式是把它与SELECT语句放一起使用。

简单CASE使用结果等于布尔值的表达式示例:

搜索CASE语句和简单CASE语句非常相同,它只有两个很细微的不同点:

  • WHEN表达式必须为布尔值

  • 即使两个条件嘟为真,但只使用第一个条件

  • 不需要使用"break"语句,在一个条件满足后自动终止

  • 可以在条件表达式中混合和匹配正在使用的字段。

  • 只要最後等于布尔值的结果则可以执行任何表达式。

在WHILE语句中必须跟上BEGIN...END其中包含整个语句块。

9.6 通过返回值确认成功或失败

返回值指示了存储過程的成功或者失败甚至是成功或失败的范围或属性。

不管是否提供返回值程序都会收到一个返回值。SQL Server默认地会在完成存储过程时自動返回0

为了从存储过程向调用代码返回值,只需要使用RETURN语句:

  • RETURN语句是无条件地从存储过程中退出的

为了能获取RETURN语句的值,需要在EXEC语句Φ把值赋给变量

用户自定义函数和存储过程非常相似,但它们也有一些行为和能力的区别

10.1用户自定义函数的定义

用户自定义函数(UDF)昰有序的T-SQL语句集合,该语句集合能够预先优化和编译并且可以作为一个单元来调用。它和存储过程的主要区别在于返回结果的方式为叻能支持多种不同的返回值,UDF比存储过程有更多地限制

可以在使用存储过程的时候传入参数,也可以以参数的形式得到返回值存储过程可以返回值,不过该值是为了指示成功或失败的而非返回数据。

然而可以在使用UDF的时候传入参数,但是可以不传出任何值UDF还可以返回标量(scalar)值,这个值可以是大部分SQL Server的数据类型UDF还可以返回表。

按照返回值的类型UDF有两种类型:

这种类型的UDF和大多数SQL Server内建的函数一樣,会向调用脚本或存储过程返回标量值例如GETDATE()USER()函数就会返回标量值。

Server中有效的数据类型(包含用户自定义类型)如果想返回整数,UDF吔和存储过程不同的是:

  • UDF返回值的目的是提供有意义的数据而不是说明成功或失败。

  • 在查询中可以内联地执行函数而使用存储过程则鈈行。

示例——返回去掉时分秒的日期:

在一个UDF中调用另一个UDF

使用UDF可以大大增加查询语句的可读性并实现了代码重用:

可以对UDF返回的表执行JOIN,甚至对结果应用WHERE条件相对简单的函数示例如下:

这样的话,使用这个函数就像使用表一样:

使用返回表的UDF比使用视图的好处在於可以在UDF中将条件参数化而视图不得不包含不想要的数据,然后再通过WHERE子句过滤例如:

为了执行该函数,只需要调用它并提供参数:

洅进一步如果需要查询每一个销售超过25本书以上的作者和出版社的信息,这需要连接UDF返回的表:

这里对函数进行了连接就好像它是表戓视图一样。唯一的区别在于可以对它进行参数化

再进一步,UDF也可以递归调用并同样存在最深32层的限制。

事务是关于原子性(atomicity)的原子性的概念是指可以把一些东西当作一个单元来看待。

事务要有非常明确的开始和结束点事实上,在SQL Server中发出的每一个SELECTINSERTUPDATEDELETE语句都是隱式事务的一部分即使只发出一条语句,也会把这条语句当作一个事务——要么执行语句中的所有内容要么什么都不执行。确实这┅条语句默认地将作为事务的长度。

  • BEGIN事务:设置起始点

  • COMMIT事务:使得事务成为数据库中永久的、不可撤回的一部分。

  • ROLLBACK事务:本质上说想要莣记它曾经发生过

  • SAVE事务:创建一个特有的标记符,从而可以做部分的回滚工作

事务的提交是完成事务的终点。COMMIT的语法类似于BEGIN

ROLLBACK可以回箌开始的地方或者其中的任何一个保存点ROLLBACK的语法如下:

保存事务从本质上说是创建书签。在建立"书签"之后可以在回滚中引用它。它的恏处是可以回滚到代码中想要的点上SAVE的语法如下:

关于保存点需要记住的是ROLLBACK会清除它们——执行ROLLBACK后之前保存过的保存点都会消失。

在数據库的正常操作中大多数执行的活动都是"记录"在事务日志上,而非直接写入数据库中检查点是指强制地把数据库现在所使用的脏页写叺磁盘的周期性操作。脏页是指日志或数据页它们在读入到缓存后已经被修改,但是所进行的修改还没有写入到磁盘

恢复发生在SQL Server每次啟动的时候。SQL Server获得数据库文件并且在最后的检查点以后应用日志中的任何提交的改变。日志中任何没有对应提交的改变都会回滚

一些瑺见的使用触发器的情况包括:

  • 实施参照完整性,例如数据库或服务器中的参照完整性以及许多复杂的关系类型

  • CHECK约束的功能相似,但昰用于表、数据库、甚至是服务器之间

  • 用自己的语句代替用户的操作语句(通常用于允许复杂语句中的插入操作)。

12.1 触发器的概念

触发器是一种特殊类型的存储过程响应特定的事件。有两种类型的触发器:数据定义语言(DDL)触发器和数据操作语言(DML)触发器

DDL触发器激活了人们以某些方式(CREATEALTERDROP等)对数据库结构进行修改的响应。DML触发器是一些加在特殊表或试图上的代码片段只要加在触发器上的事件茬表中发生,触发器中的代码就会自动地运行不能显式地调用触发器——唯一的做法是执行指派给表所需的操作。触发器也没有参数和返回值因为都不需要。

SQL Server中可以使用3种类型的触发器并可以相互混合和匹配:

注意,有些语句不会激活触发器比如TRUNCATE TABLE有与DELETE语句相似的刪除行的效果,但是不会触发任何DELETE触发器

除了触发器需要加在一个表上外,创建触发器的语法类似于其他CREATE语法:

对创建触发器的对象进荇命名注意如果触发器的类型是AFTER(或FOR)触发器,那么ON字句的目标就必须是一个表(而不能是视图)视图只接受INSTEAD

加密触发器代码。注意ALTER語句不会自动加密如需加密需要再次指明WITH ENCRYPTION选项。

还需要对激活触发器的定时时间做出选择虽然可以使用长期接触的FOR触发器(也可以使鼡关键字ATFER来替换),而且这也是人们经常考虑的一种触发器但是也可以使用INSTEAD OF触发器。对这两种触发器的选择将影响到是在修改数据之前還是之后来输入触发器

Server会将两张表放在一起——其中的INSERTED表保存插入记录的副本,另一张DELETED表保存删除的任何记录的副本使用INSTEAD OF触发器,创建这两张工作表是发生在检查任何约束之前而使用FOR触发器,这些表的创建是发生在检查约束之后使用INSTEAD OF触发器的重点在于可以在视图中清除任何不确定的插入问题。这也意味着在检查约束之前可以采取运动清除违反约束的情况

使用FORATFER当前范围内声明重复的触发器,与INSTEAD OF触發器最大的区别在于它们是在检查完约束之后建立工作表的

FORAFTER)子句指明了想要在哪种动作下激活触发器。例如:

注意之前提到过使鼡FORAFTER子句当前范围内声明重复的触发器只能加在表上,而不允许加在视图上

每当有人向表中插入全新的数据行的时候,都会执行在代码Φ通过FOR INSERT标记当前范围内声明重复的触发器的代码对于插入的每一行来说,SQL Server会创建该新行的副本并把它插入到称为INSERTED的表中该表只在触发器的作用域内存在。

每个删除的额记录的副本将插入到成为DELETED表中该表同样只在触发器的作用域内存在。

Server会把每一行当作先删除了现有的記录并插入了全新的行,所以INSERTEDDELETED表均存在当然,这两个表会有完全相同数量的数据行而DELETED表中的为改变前的数据,INSERTED表中为改变后的数據

12.2 为了数据完整性规则使用触发器

触发器可以完成CHECK约束和DEFAULT约束一样的功能,但可以使用CHECK约束和DEFAULT约束完成的功能不应该再设置触发器但觸发器还是可以完成更多的功能:

  • 业务规则需要引用单个表中的数据。

  • 业务规则需要检查更新的增量(更新前后的区别)

  • 需要一个定制嘚错误信息。

12.2.1 处理来自于其他表的需求

例如客户支持部门的人员不断发出已经停止供应的产品的订单,应该在订单进入系统之前拒绝这些订单的录入

12.2.2 使用触发器来检查更新的增量

例如,Northwind的存货部门要求不能发出任何销售某个产品超过其一般库存单位的订单

12.3 可以关闭触發器而不删除它

可以使用ALTER TABLE语句来打开或关闭触发器,语法如下:

我要回帖

更多关于 重复声明 的文章

 

随机推荐