[ASP.NET] HttpApplication的认识与加深理解

mikel阅读(465)

HttpApplication 对象是经由HttpApplicationFactory.GetApplicationInstance(并最终调用 HttpRuntime.CreateNonPublicInstance)创建的HttpApplicationFactory它的主要任务是使用 URL 信息来查找 URL 虚拟目录和汇集的 HttpApplication 对象之间的匹配关系。

这个工厂类的行为概括为有以下几点
1、工厂类维护, HttpApplication 对象池并使用它们来处理应用程序的请求。池的寿命与应用程序的寿命相同。
2、 应用程序的第一个请求到达时,工厂类提取有关应用程序类型的信息(global.asax 类)、设置用于监视更改的文件、创建应用程序状态并触发 Application_OnStart 事件。工厂类从池中获取一个 HttpApplication 实例,并将要处理的请求放入实例中。如果没有可用的对象,则创建一个新的 HttpApplication 对象。要创建 HttpApplication 对象,需要先完成 global.asax 应用程序文件的编译。
3、HttpApplication 开始处理请求,并且只能在完成这个请求后才能处理新的请求。如果收到来自同一资源的新请求,则由池中的其他对象来处理。
4、应用程序对象允许所有注册的 HTTP 模块对请求进行预处理,并找出最适合处理请求的处理程序类型。这通过查找请求的 URL 的扩展和配置文件中的信息来完成。

HttpApplicationFactory.GetApplicationInstance创建HttpApplication实例中有三个关键方法:
HttpApplicationFactory._theApplicationFactory.EnsureInited()  该方法检查HttpApplicationFactory是否被初始化,如果没有,就通过HttpApplicationFactory.Init()进行初始化。Init()中,先获取global.asax文件的完整路径,然后调用CompileApplication()global.asax进行编译。
HttpApplicationFactory._theApplicationFactory.EnsureAppStartCalled(context)  创建特定的HttpApplication实例,触发ApplicationOnStart事件,执行ASP.global_asax中的Application_Start(object sender, EventArgs e)方法。这里创建的HttpApplication实例在处理完事件后,就被回收。
HttpApplicationFactory._theApplicationFactory.GetNormalApplicationInstance(context) 该方法创建HttpApplication实例并进行初始化,调用System.Web.HttpApplication.InitInternal() 方法。创建HttpApplication实例是根据实际的_theApplicationType进行创建。如果Web目录中没有global.asa 文件,也就是说没有动态编译生成ASP.global_asax类型,那就直接实例化  HttpApplication。如果创建了ASP.global_asax类型,那就对ASP.global_asa进行实例化。

创建HttpApplication实例之后就是调用实例的InitInternal方法。
InitInternal方法的主要功能如下:
  1. InitModules():根据Web.Config的设置,创建相应的HttpModules。

  2. HookupEventHandlersForAppplicationAndModules:根据发生的事件,调用HttpApplication实例中相应的事件处理函数。

  3. 创建很多实现IExecutionStep接口的类的实例并添加到当前HttpApplication实例的_execSteps中,等待回调时执行。从 这里我们可以看到HttpApplication是以异步的方式处理请求, 对请求的很多处理工作都放入了_execStep等待回调时执行。
     _execStep中主要的处理工作如下:
    1) 对请求的路径进行安全检查,禁止非法路径访问(ValidatePathExecutionStep)。
    2) 如果设置了UrlMappings, 进行RewritePath(UrlMappingsExecutionStep)。
    3) 执行事件处理函数,比如:BeginRequest、AuthenticateRequest等等。
下 面就是获取处理当前请求的HttpHandler,ASP.NET页面的动态编译也是在这里进行的。至此HttpApplication流程将会转到 HttpHandler流程.也就是说HttpApplication 对象负责查找应该使用哪种处理程序来处理请求。HttpApplication 对象还负责检测对动态创建的、表示资源的程序集(如 .aspx 页面或 .asmx Web 服务)所进行的更改。如果检测到更改,应用程序对象将确保编译并加载所请求的资源的最新来源。HttpApplication调用 ProcessRequest方法来处理用户请求,此方法会调用对应的HttpHandler来处理用户请求,HttpHandler根据用户请求的文件 的扩展名处理请求,并把请求的结果,也就是HTML发送到客户浏览器.

HttpApplication是HttpRuntime所创建的吗? 并不是,HttpRuntime只是向HttpApplicationFactory提出请求,要求返回一个HttpApplication对象。 HttpApplicationFactory在接收到请求后,会先检查是否有已经存在并空闲的对象,如果有就取出一个HttpApplication对 象返回给HttpRuntime,如果没有的话,则要创建一个HttpApplication对象给HttpRunTime。
关于HttpApplication这个类的方法的实现,就不再一一解释,需要了解的,在类里面写上一个HttpApplication单词,然后右键选择“转到定义“,就可以看到里面的元数据了。
从上面看出global类与HttpApplication十分紧密,其事上,global类是继承与System.Web.HttpApplication类。

public class Global : System.Web.HttpApplication
    {
        
protected void Application_Start(object sender, EventArgs e)
        {
        }
        
//省略
    }

假设在global类中定义变量与对象,我们在全局中是否能够取得或设置他的值呢?看下面的例子:

Code

在页面中设置:
WebApp.Global.UserName = "AAAA";
在设置前与设置后输出,你可以看到值的变化,但对变量的设置,会出现什么问题?这些就需要你在开发中去考虑了。
如果使用过MVC框架开发的朋友就很熟其路由规则配置,其实就是设置在Global类的Application_Start事件中,如下面:

 public class MvcApplication : System.Web.HttpApplication
    {
        
public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute(
"{resource}.axd/{*pathInfo}");
            routes.MapRoute(
                
"UpcomingDinners",
                
"Dinners/Page/{page}",
                
new { controller = "Dinners", action = "Index" }
            );
            routes.MapRoute(
                
"Default",                                              // Route name
                "{controller}/{action}/{id}",                           // URL with parameters
                new { controller = "Home", action = "Index", id = "" }  // Parameter defaults
            );
        }
        
protected void Application_Start()
        {
            RegisterRoutes(RouteTable.Routes);
        }
    }

其实我们经常用的Application对象是HttpApplication类的一个属性,至此HttpApplication已经完成,下次转到HttpHandler流程了。
随便谈识文断字,查阅大量的资料,等于收集整理加上自己的见解吧,且当成长过程中的回忆。

[ASP.NET]HttpRuntime的认识与加深理解

mikel阅读(349)

下面最先介绍HttpRuntime的Web.config里的配置

<httpRuntime
   executionTimeout 
= "number" 
   maxRequestLength 
= "number" 
   requestLengthDiskThreshold 
= "number" 
   useFullyQualifiedRedirectUrl 
= "[True|False]" 
   minFreeThreads 
= "number" 
   minLocalRequestFreeThreads 
= "number" 
   appRequestQueueLimit 
= "number"
   enableKernelOutputCache 
= "[True|False]" 
   enableVersionHeader 
= "[True|False]" 
   apartmentThreading 
= "[True|False]"
   requireRootedSaveAsPath 
= "[True|False]"
   enable 
= "[True|False]" 
   sendCacheControlHeader 
= "[True|False]" 
   shutdownTimeout 
= "number"
   delayNotificationTimeout 
= "number"
   waitChangeNotification 
= "number" 
   maxWaitChangeNotification 
= "number" 
   enableHeaderChecking 
= "[True|False]" 
/>

通过上面的配置说明, 下面是在Web.Config里节点的设置

<configuration>
  
<system.web>
  
<httpRuntime maxRequestLength="4000"
    enable 
= "True"
    requestLengthDiskThreshold
="512
    useFullyQualifiedRedirectUrl="True"
    executionTimeout
="45"
    versionHeader
="1.1.4128"/>
  
</system.web>
</configuration>

IIS 所收到的对某 Microsoft ASP.NET 页面的每个请求都被移交给 ASP.NET HTTP 管线。HTTP 管线由一系列托管对象组成,这些对象按顺序处理该请求,并完成从 URL 到普通 HTML 文本的转换。HTTP 管线的入口点是 HttpRuntime 类。ASP.NET 基础结构为辅助进程中所承载的每个 AppDomain 创建此类的一个实例请注意,该辅助进程为当前正在运行的每个 ASP.NET 应用程序维护一个不同的 AppDomain。

要激活 HTTP 管道,可以创建一个 HttpRuntime 类的新实例,然后调用其 ProcessRequest 方法。一个完整的页面请求会包括下面的流程:
首先被WWW服务器截获(inetinfo.exe进程), 该进程首先判断页面后缀, 然后根据IIS中配置决定调用具体的扩展程序。aspx就会调用aspnet_isapi.dll,
然后由aspnet_isapi.dll发送给w3wp.exe(iis 工作者进程,IIS6.0中叫做 w3wq.exe,IIS5.0中叫做 aspnet_wp.exe)。

接下来在w3wp.exe调用.NET类库进行具体处理,顺序如下:ISAPIRuntim, HttpRuntime, HttpApplicationFactory, HttpApplication, HttpModule, HttpHandlerFactory, HttpHandler

ISAPIRuntime:主要作用是调用一些非托管代码生成HttpWorkerRequest对象,HttpWorkerRequest对象包含当前请求的所有信息,然后传递给HttpRuntime
HttpRuntime: 根据HttpWorkerRequest对象生成HttpContext,HttpContext包含request、response等属性, 再调用HttpApplicationFactory来生成IHttpHandler, 调用HttpApplication对象执行请求
HttpApplicationFactory: 生成一个HttpApplication对象
HttpApplication:进行HttpModule的初始化,HttpApplication创建针对此Http请求的 HttpContext对象
HttpModule: 当一个HTTP请求到达HttpModule时,整个ASP.NET Framework系统还并没有对这个HTTP请求做任何处理,也就是说此时对于HTTP请求来讲,HttpModule是一个HTTP请求的“必经之路 ”,所以可以在这个HTTP请求传递到真正的请求处理中心(HttpHandler)之前附加一些需要的信息在这个HTTP请求信息之上,或者针对截获的 这个HTTP请求信息作一些额外的工作,或者在某些情况下干脆终止满足一些条件的HTTP请求,从而可以起到一个Filter过滤器的作用。
HttpHandlerFactory:把用户request 转发到HttpHandlerFactory,再由HttpHandlerFactory实例化HttpHandler对象来相应request
HttpHandle:Http处理程序,处理页面请求

从上面看出HttpRuntime其中有一个ProcessRequest 方法
public static void ProcessRequest(HttpWorkerRequest wr);  //驱动所有 ASP.NET Web 处理执行。伪代码如下:

public static void HttpRuntime.ProcessRequest(HttpWorkerRequest wr)
 {
   
// 检查当前调用者有没有作为ASP.NET宿主(Host)的权限
   InternalSecurityPermissions.AspNetHostingPermissionLevelMedium.Demand(); 
   
if(wr == null)
   {
     
throw new ArgumentNullException("custom");
   }
   RequestQueue queue 
= HttpRuntime._theRuntime._requestQueue;
   
if(queue != null)
   {
     
// 将参数中的Web页面请求放入请求队列中, 并从队列中使用FIFO策略获取一个页面请求
     wr = queue.GetRequestToExecute(wr);
   }
   
if(wr != null)
   {     
     HttpRuntime.CalculateWaitTimeAndUpdatePerfCounter(wr); 
// 更新性能计数器     
      HttpRuntime.ProcessRequestNow(wr); // 实际完成页面请求工作
   }
 }

ProcessRequestNow函数则直接调用缺省HttpRuntime实例的ProcessRequestInternal函数完成实际页面请求工作,伪代码如下:

internal static void HttpRuntime.ProcessRequestNow(HttpWorkerRequest wr)
{
   HttpRuntime._theRuntime.ProcessRequestInternal(wr);
}

ProcessRequestInternal函数逻辑稍微复杂一些,大致可分为四个部分:
检查当前HttpRuntime实例是否第一次被调用,如果是第一次调用则通过FirstRequestInit函数初始化
调用HttpResponse.InitResponseWriter函数初始化页面请求的返回对象HttpWorkerRequest.Response
调用HttpApplicationFactory.GetApplicationInstance函数获取当前 Web 应用程序实例
使用Web应用程序实例完成实际的页面请求工作
伪代码如下:

Code

HttpRuntime.ProcessRequestInternal函数中调用了 HttpApplicationFactory.GetApplicationInstance函数获取当前 Web 应用程序实例。至少HttpRuntime已经完完成,将转进HttpApplicationFactory阶段流程。大家可以看到,围绕 HttpRuntime的函数都有一个HttpWorkerRequest参数,下面简单介绍一下这个参数的作用。在ISAPIRuntime阶段,调用 一些非托管代码生成HttpWorkerRequest对象,该对象包含当前请求的所有信息,然后传递给HttpRuntime,这里生成的 HttpWorkerRequest对象可以直接在我们的页面里调用.
IServiceProvider provider=(IServiceProvider)HttpContext.Current;
HttpWorkerRequest wr=(HttpWorkerRequest)provider.GetService(typeof(HttpWorkerRequest));
然后可以通过wr来调用里面的方法。关于HttpWorkerRequest类里面包含的方法,从其元数据里面可以看到,如下:


public abstract class HttpWorkerRequest
    {
        
// 摘要:
        
//     指定 AcceptHTTP 标头的索引号。
        public const int HeaderAccept = 20;
        
//
        
// 摘要:
        
//     指定 Accept-CharsetHTTP 标头的索引号。
        public const int HeaderAcceptCharset = 21;
        
//
        
// 摘要:
        
//     指定 Accept-EncodingHTTP 标头的索引号。
        public const int HeaderAcceptEncoding = 22;
        
//
        
// 摘要:
        
//     指定 Accept-LanguageHTTP 标头的索引号。
        public const int HeaderAcceptLanguage = 23;
        
//
        
// 摘要:
        
//     指定 Accept-RangesHTTP 标头的索引号。
        public const int HeaderAcceptRanges = 20;
        
//
        
// 摘要:
        
//     指定 AgeHTTP 标头的索引号。
        public const int HeaderAge = 21;
        
//
        
// 摘要:
        
//     指定 AllowHTTP 标头的索引号。
        public const int HeaderAllow = 10;
        
//
        
// 摘要:
        
//     指定 AuthorizationHTTP 标头的索引号。
        public const int HeaderAuthorization = 24;
        
//
        
// 摘要:
        
//     表示 HTTPCache-ControlHTTP 标头的索引。
        public const int HeaderCacheControl = 0;
        
//
        
// 摘要:
        
//     指定 ConnectionHTTP 标头的索引号。
        public const int HeaderConnection = 1;
        
//
        
// 摘要:
        
//     指定 Content-EncodingHTTP 标头的索引号。
        public const int HeaderContentEncoding = 13;
        
//
        
// 摘要:
        
//     指定 Content-LanguageHTTP 标头的索引号。
        public const int HeaderContentLanguage = 14;
        
//
        
// 摘要:
        
//     指定 Content-LengthHTTP 标头的索引号。
        public const int HeaderContentLength = 11;
        
//
        
// 摘要:
        
//     指定 Content-LocationHTTP 标头的索引号。
        public const int HeaderContentLocation = 15;
        
//
        
// 摘要:
        
//     指定 Content-MD5HTTP 标头的索引号。
        public const int HeaderContentMd5 = 16;
        
//
        
// 摘要:
        
//     指定 Content-RangeHTTP 标头的索引号。
        public const int HeaderContentRange = 17;
        
//
        
// 摘要:
        
//     指定 Content-TypeHTTP 标头的索引号。
        public const int HeaderContentType = 12;
        
//
        
// 摘要:
        
//     指定 CookieHTTP 标头的索引号。
        public const int HeaderCookie = 25;
        
//
        
// 摘要:
        
//     指定 DateHTTP 标头的索引号。
        public const int HeaderDate = 2;
        
//
        
// 摘要:
        
//     指定 ETagHTTP 标头的索引号。
        public const int HeaderEtag = 22;
        
//
        
// 摘要:
        
//     指定 ExceptHTTP 标头的索引号。
        public const int HeaderExpect = 26;
        
//
        
// 摘要:
        
//     指定 ExpiresHTTP 标头的索引号。
        public const int HeaderExpires = 18;
        
//
        
// 摘要:
        
//     指定 FromHTTP 标头的索引号。
        public const int HeaderFrom = 27;
        
//
        
// 摘要:
        
//     指定 HostHTTP 标头的索引号。
        public const int HeaderHost = 28;
        
//
        
// 摘要:
        
//     指定 If-MatchHTTP 标头的索引号。
        public const int HeaderIfMatch = 29;
        
//
        
// 摘要:
        
//     指定 If-Modified-SinceHTTP 标头的索引号。
        public const int HeaderIfModifiedSince = 30;
        
//
        
// 摘要:
        
//     指定 If-None-MatchHTTP 标头的索引号。
        public const int HeaderIfNoneMatch = 31;
        
//
        
// 摘要:
        
//     指定 If-RangeHTTP 标头的索引号。
        public const int HeaderIfRange = 32;
        
//
        
// 摘要:
        
//     指定 If-Unmodified-SinceHTTP 标头的索引号。
        public const int HeaderIfUnmodifiedSince = 33;
        
//
        
// 摘要:
        
//     指定 Keep-AliveHTTP 标头的索引号。
        public const int HeaderKeepAlive = 3;
        
//
        
// 摘要:
        
//     指定 Last-ModifiedHTTP 标头的索引号。
        public const int HeaderLastModified = 19;
        
//
        
// 摘要:
        
//     指定 LocationHTTP 标头的索引号。
        public const int HeaderLocation = 23;
        
//
        
// 摘要:
        
//     指定 Max-ForwardsHTTP 标头的索引号。
        public const int HeaderMaxForwards = 34;
        
//
        
// 摘要:
        
//     指定 PragmaHTTP 标头的索引号。
        public const int HeaderPragma = 4;
        
//
        
// 摘要:
        
//     指定 Proxy-AuthenticateHTTP 标头的索引号。
        public const int HeaderProxyAuthenticate = 24;
        
//
        
// 摘要:
        
//     指定 Proxy-AuthorizationHTTP 标头的索引号。
        public const int HeaderProxyAuthorization = 35;
        
//
        
// 摘要:
        
//     指定 RangeHTTP 标头的索引号。
        public const int HeaderRange = 37;
        
//
        
// 摘要:
        
//     指定 RefererHTTP 标头的索引号。
        public const int HeaderReferer = 36;
        
//
        
// 摘要:
        
//     指定 Retry-AfterHTTP 标头的索引号。
        public const int HeaderRetryAfter = 25;
        
//
        
// 摘要:
        
//     指定 ServerHTTP 标头的索引号。
        public const int HeaderServer = 26;
        
//
        
// 摘要:
        
//     指定 Set-CookieHTTP 标头的索引号。
        public const int HeaderSetCookie = 27;
        
//
        
// 摘要:
        
//     指定 TEHTTP 标头的索引号。
        public const int HeaderTe = 38;
        
//
        
// 摘要:
        
//     指定 TrailerHTTP 标头的索引号。
        public const int HeaderTrailer = 5;
        
//
        
// 摘要:
        
//     指定 Transfer-EncodingHTTP 标头的索引号。
        public const int HeaderTransferEncoding = 6;
        
//
        
// 摘要:
        
//     指定 UpgradeHTTP 标头的索引号。
        public const int HeaderUpgrade = 7;
        
//
        
// 摘要:
        
//     指定 User-AgentHTTP 标头的索引号。
        public const int HeaderUserAgent = 39;
        
//
        
// 摘要:
        
//     指定 VaryHTTP 标头的索引号。
        public const int HeaderVary = 28;
        
//
        
// 摘要:
        
//     指定 ViaHTTP 标头的索引号。
        public const int HeaderVia = 8;
        
//
        
// 摘要:
        
//     指定 WarningHTTP 标头的索引号。
        public const int HeaderWarning = 9;
        
//
        
// 摘要:
        
//     指定 WWW-AuthenticateHTTP 标头的索引号。
        public const int HeaderWwwAuthenticate = 29;
        
//
        
// 摘要:
        
//     指定请求的原因。
        public const int ReasonCachePolicy = 2;
        
//
        
// 摘要:
        
//     指定请求的原因。
        public const int ReasonCacheSecurity = 3;
        
//
        
// 摘要:
        
//     指定请求的原因。
        public const int ReasonClientDisconnect = 4;
        
//
        
// 摘要:
        
//     指定请求的原因。默认值为 System.Web.HttpWorkerRequest.ReasonResponseCacheMiss。
        public const int ReasonDefault = 0;
        
//
        
// 摘要:
        
//     指定请求的原因。
        public const int ReasonFileHandleCacheMiss = 1;
        
//
        
// 摘要:
        
//     指定请求的原因。
        public const int ReasonResponseCacheMiss = 0;
        
//
        
// 摘要:
        
//     指定 MaximumHTTP 请求标头的索引号。
        public const int RequestHeaderMaximum = 40;
        
//
        
// 摘要:
        
//     指定 MaximumHTTP 响应标头的索引号。
        public const int ResponseHeaderMaximum = 30;
        
// 摘要:
        
//     初始化 System.Web.HttpWorkerRequest 类的新实例。
        protected HttpWorkerRequest();
        
// 摘要:
        
//     获取 Machine.config 文件的完整物理路径。
        
//
        
// 返回结果:
        
//     Machine.config 文件的物理路径。
        public virtual string MachineConfigPath { get; }
        
//
        
// 摘要:
        
//     获取 ASP.NET 二进制文件的安装目录的物理路径。
        
//
        
// 返回结果:
        
//     ASP.NET 二进制文件的物理目录。
        public virtual string MachineInstallDirectory { get; }
        
//
        
// 摘要:
        
//     获取当前请求的 Windows 跟踪 ID 的对应事件跟踪。
        
//
        
// 返回结果:
        
//     当前 ASP.NET 请求的跟踪 ID。
        public virtual Guid RequestTraceIdentifier { get; }
        
//
        
// 摘要:
        
//     获取根 Web.config 文件的完整物理路径。
        
//
        
// 返回结果:
        
//     根 Web.config 文件的物理路径。
        public virtual string RootWebConfigPath { get; }
        
// 摘要:
        
//     终止与客户端的连接。
        public virtual void CloseConnection();
        
//
        
// 摘要:
        
//     由运行库使用以通知 System.Web.HttpWorkerRequest 当前请求的请求处理已完成。
        public abstract void EndOfRequest();
        
//
        
// 摘要:
        
//     将所有挂起的响应数据发送到客户端。
        
//
        
// 参数:
        
//   finalFlush:
        
//     如果这将是最后一次刷新响应数据,则为 true;否则为 false。
        public abstract void FlushResponse(bool finalFlush);
        
//
        
// 摘要:
        
//     返回当前正在执行的服务器应用程序的虚拟路径。
        
//
        
// 返回结果:
        
//     当前应用程序的虚拟路径。
        public virtual string GetAppPath();
        
//
        
// 摘要:
        
//     返回当前正在执行的服务器应用程序的物理路径。
        
//
        
// 返回结果:
        
//     当前应用程序的物理路径。
        public virtual string GetAppPathTranslated();
        
//
        
// 摘要:
        
//     在派生类中被重写时,返回当前 URL 的应用程序池 ID。
        
//
        
// 返回结果:
        
//     始终返回 null。
        public virtual string GetAppPoolID();
        
//
        
// 摘要:
        
//     获取从客户端读入的字节数。
        
//
        
// 返回结果:
        
//     包含读取的字节数的 Long。
        public virtual long GetBytesRead();
        
//
        
// 摘要:
        
//     在派生类中被重写时,从客户端发出的请求获取证书字段(以 X.509 标准指定)。
        
//
        
// 返回结果:
        
//     包含整个证书内容流的字节数组。
        public virtual byte[] GetClientCertificate();
        
//
        
// 摘要:
        
//     获取证书颁发者(以二进制格式表示)。
        
//
        
// 返回结果:
        
//     包含以二进制格式表示的证书颁发者的字节数组。
        public virtual byte[] GetClientCertificateBinaryIssuer();
        
//
        
// 摘要:
        
//     在派生类中被重写时,返回用于编码客户端证书的 System.Text.Encoding 对象。
        
//
        
// 返回结果:
        
//     表示为整数的证书编码。
        public virtual int GetClientCertificateEncoding();
        
//
        
// 摘要:
        
//     在派生类中被重写时,获取与客户端证书关联的 PublicKey 对象。
        
//
        
// 返回结果:
        
//     一个 PublicKey 对象。
        public virtual byte[] GetClientCertificatePublicKey();
        
//
        
// 摘要:
        
//     在派生类中被重写时,则获取证书开始生效的日期。此日期随区域设置的不同而不同。
        
//
        
// 返回结果:
        
//     表示证书生效时间的 System.DateTime 对象。
        public virtual DateTime GetClientCertificateValidFrom();
        
//
        
// 摘要:
        
//     获取证书到期日期。
        
//
        
// 返回结果:
        
//     表示证书失效日期的 System.DateTime 对象。
        public virtual DateTime GetClientCertificateValidUntil();
        
//
        
// 摘要:
        
//     在派生类中被重写时,返回当前连接的 ID。
        
//
        
// 返回结果:
        
//     始终返回 0。
        public virtual long GetConnectionID();
        
//
        
// 摘要:
        
//     在派生类中被重写时,返回所请求的 URI 的虚拟路径。
        
//
        
// 返回结果:
        
//     请求的 URI 的路径。
        public virtual string GetFilePath();
        
//
        
// 摘要:
        
//     返回请求的 URI 的物理文件路径(并将其从虚拟路径转换成物理路径:例如,从“/proj1/page.aspx”转换成“c:\dir\page.aspx”)
        
//
        
// 返回结果:
        
//     请求的 URI 的已转换的物理文件路径。
        public virtual string GetFilePathTranslated();
        
//
        
// 摘要:
        
//     返回请求标头的指定成员。
        
//
        
// 返回结果:
        
//     请求标头中返回的 HTTP 谓词。
        public abstract string GetHttpVerbName();
        
//
        
// 摘要:
        
//     提供对请求的 HTTP 版本(如“HTTP/1.1”)的访问。
        
//
        
// 返回结果:
        
//     请求标头中返回的 HTTP 版本。
        public abstract string GetHttpVersion();
        
//
        
// 摘要:
        
//     返回与指定的索引相对应的标准 HTTP 请求标头。
        
//
        
// 参数:
        
//   index:
        
//     标头的索引。例如,System.Web.HttpWorkerRequest.HeaderAllow 字段。
        
//
        
// 返回结果:
        
//     HTTP 请求标头。
        public virtual string GetKnownRequestHeader(int index);
        
//
        
// 摘要:
        
//     返回指定的 HTTP 请求标头的索引号。
        
//
        
// 参数:
        
//   header:
        
//     标头的名称。
        
//
        
// 返回结果:
        
//     在 header 参数中指定的 HTTP 请求标头的索引号。
        public static int GetKnownRequestHeaderIndex(string header);
        
//
        
// 摘要:
        
//     返回指定的 HTTP 请求标头的名称。
        
//
        
// 参数:
        
//   index:
        
//     标头的索引号。
        
//
        
// 返回结果:
        
//     在 index 参数中指定的 HTTP 请求标头的名称。
        public static string GetKnownRequestHeaderName(int index);
        
//
        
// 摘要:
        
//     返回指定的 HTTP 响应标头的索引号。
        
//
        
// 参数:
        
//   header:
        
//     HTTP 标头的名称。
        
//
        
// 返回结果:
        
//     在 header 参数中指定的 HTTP 响应标头的索引号。
        public static int GetKnownResponseHeaderIndex(string header);
        
//
        
// 摘要:
        
//     返回指定的 HTTP 响应标头的名称。
        
//
        
// 参数:
        
//   index:
        
//     标头的索引号。
        
//
        
// 返回结果:
        
//     在 index 参数中指定的 HTTP 响应标头的名称。
        public static string GetKnownResponseHeaderName(int index);
        
//
        
// 摘要:
        
//     提供对请求标头的指定成员的访问。
        
//
        
// 返回结果:
        
//     请求标头中返回的服务器 IP 地址。
        public abstract string GetLocalAddress();
        
//
        
// 摘要:
        
//     提供对请求标头的指定成员的访问。
        
//
        
// 返回结果:
        
//     请求标头中返回的服务器端口号。
        public abstract int GetLocalPort();
        
//
        
// 摘要:
        
//     返回具有 URL 扩展的资源的其他路径信息。即对于路径 /virdir/page.html/tail,GetPathInfo 值为 /tail。
        
//
        
// 返回结果:
        
//     资源的附加路径信息。
        public virtual string GetPathInfo();
        
//
        
// 摘要:
        
//     返回 HTTP 请求正文已被读取的部分。
        
//
        
// 返回结果:
        
//     HTTP 请求正文已被读取的部分。
        public virtual byte[] GetPreloadedEntityBody();
        
//
        
// 摘要:
        
//     使用指定的缓冲区数据和字节偏移量获取 HTTP 请求正文当前已被读取的部分。
        
//
        
// 参数:
        
//   buffer:
        
//     要读取的数据。
        
//
        
//   offset:
        
//     开始读取的位置的字节偏移量。
        
//
        
// 返回结果:
        
//     HTTP 请求正文已被读取的部分。
        public virtual int GetPreloadedEntityBody(byte[] buffer, int offset);
        
//
        
// 摘要:
        
//     获取 HTTP 请求正文当前已被读取部分的长度。
        
//
        
// 返回结果:
        
//     一个整数,包含当前已读取的 HTTP 请求正文的长度。
        public virtual int GetPreloadedEntityBodyLength();
        
//
        
// 摘要:
        
//     在派生类中被重写时,返回 HTTP 协议(HTTP 或 HTTPS)。
        
//
        
// 返回结果:
        
//     如果 System.Web.HttpWorkerRequest.IsSecure() 方法为 true,则为 HTTPS;否则,为 HTTP。
        public virtual string GetProtocol();
        
//
        
// 摘要:
        
//     返回请求 URL 中指定的查询字符串。
        
//
        
// 返回结果:
        
//     请求查询字符串。
        public abstract string GetQueryString();
        
//
        
// 摘要:
        
//     在派生类中被重写时,以字节数组的形式返回响应查询字符串。
        
//
        
// 返回结果:
        
//     包含响应的字节数组。
        public virtual byte[] GetQueryStringRawBytes();
        
//
        
// 摘要:
        
//     返回附加了查询字符串的请求标头中包含的 URL 路径。
        
//
        
// 返回结果:
        
//     请求标头的原始 URL 路径。
        public abstract string GetRawUrl();
        
//
        
// 摘要:
        
//     提供对请求标头的指定成员的访问。
        
//
        
// 返回结果:
        
//     客户端的 IP 地址。
        public abstract string GetRemoteAddress();
        
//
        
// 摘要:
        
//     在派生类中被重写时,返回客户端计算机的名称。
        
//
        
// 返回结果:
        
//     客户端计算机的名称。
        public virtual string GetRemoteName();
        
//
        
// 摘要:
        
//     提供对请求标头的指定成员的访问。
        
//
        
// 返回结果:
        
//     客户端的 HTTP 端口号。
        public abstract int GetRemotePort();
        
//
        
// 摘要:
        
//     在派生类中被重写时,返回请求的原因。
        
//
        
// 返回结果:
        
//     原因代码。默认值为 ReasonResponseCacheMiss。
        public virtual int GetRequestReason();
        
//
        
// 摘要:
        
//     在派生类中被重写时,返回本地服务器的名称。
        
//
        
// 返回结果:
        
//     本地服务器的名称。
        public virtual string GetServerName();
        
//
        
// 摘要:
        
//     从与请求关联的服务器变量词典返回单个服务器变量。
        
//
        
// 参数:
        
//   name:
        
//     请求的服务器变量的名称。
        
//
        
// 返回结果:
        
//     请求的服务器变量。
        public virtual string GetServerVariable(string name);
        
//
        
// 摘要:
        
//     返回一个字符串,该字符串描述指定的 HTTP 状态代码的名称。
        
//
        
// 参数:
        
//   code:
        
//     HTTP 状态代码。
        
//
        
// 返回结果:
        
//     状态说明。例如,System.Web.HttpWorkerRequest.GetStatusDescription(System.Int32) (404)
        
//     返回“未找到”。
        public static string GetStatusDescription(int code);
        
//
        
// 摘要:
        
//     获取整个 HTTP 请求正文的长度。
        
//
        
// 返回结果:
        
//     包含整个 HTTP 请求正文的长度的整数。
        public virtual int GetTotalEntityBodyLength();
        
//
        
// 摘要:
        
//     返回非标准的 HTTP 请求标头值。
        
//
        
// 参数:
        
//   name:
        
//     标头名称。
        
//
        
// 返回结果:
        
//     标头值。
        public virtual string GetUnknownRequestHeader(string name);
        
//
        
// 摘要:
        
//     获取所有非标准的 HTTP 标头的名称/值对。
        
//
        
// 返回结果:
        
//     标头的名称/值对的数组。
        [CLSCompliant(false)]
        
public virtual string[][] GetUnknownRequestHeaders();
        
//
        
// 摘要:
        
//     返回请求的 URI 的虚拟路径。
        
//
        
// 返回结果:
        
//     请求的 URI 的路径。
        public abstract string GetUriPath();
        
//
        
// 摘要:
        
//     当在派生类中被重写时,返回当前连接的上下文 ID。
        
//
        
// 返回结果:
        
//     始终返回 0。
        public virtual long GetUrlContextID();
        
//
        
// 摘要:
        
//     在派生类中被重写时,返回客户端的模拟标记。
        
//
        
// 返回结果:
        
//     表示客户端的模拟标记的值。默认值为 0。
        public virtual IntPtr GetUserToken();
        
//
        
// 摘要:
        
//     获取请求虚拟路径的模拟标记。
        
//
        
// 返回结果:
        
//     请求虚拟路径的标记的非托管内存指针。
        public virtual IntPtr GetVirtualPathToken();
        
//
        
// 摘要:
        
//     返回一个值,该值指示请求是否包含正文数据。
        
//
        
// 返回结果:
        
//     如果请求包含正文数据,则为 true;否则,为 false。
        public bool HasEntityBody();
        
//
        
// 摘要:
        
//     返回一个值,该值指示是否已为当前的请求将 HTTP 响应标头发送到客户端。
        
//
        
// 返回结果:
        
//     如果 HTTP 响应标头已发送到客户端,则为 true;否则,为 false。
        public virtual bool HeadersSent();
        
//
        
// 摘要:
        
//     返回一个值,该值指示客户端连接是否仍处于活动状态。
        
//
        
// 返回结果:
        
//     如果客户端连接仍处于活动状态,则为 true;否则,为 false。
        public virtual bool IsClientConnected();
        
//
        
// 摘要:
        
//     返回一个值,该值指示是否所有请求数据都可用,以及是否不需要对客户端进行进一步读取。
        
//
        
// 返回结果:
        
//     如果所有请求数据都可用,则为 true;否则,为 false。
        public virtual bool IsEntireEntityBodyIsPreloaded();
        
//
        
// 摘要:
        
//     返回一个指示连接是否使用 SSL 的值。
        
//
        
// 返回结果:
        
//     如果连接是 SSL 连接,则为 true;否则为 false。默认值为 false。
        public virtual bool IsSecure();
        
//
        
// 摘要:
        
//     返回与指定虚拟路径相对应的物理路径。
        
//
        
// 参数:
        
//   virtualPath:
        
//     虚拟路径。
        
//
        
// 返回结果:
        
//     与 virtualPath 参数中指定的虚拟路径相对应的物理路径。
        public virtual string MapPath(string virtualPath);
        
//
        
// 摘要:
        
//     读取客户端的请求数据(在尚未预加载时)。
        
//
        
// 参数:
        
//   buffer:
        
//     将数据读入的字节数组。
        
//
        
//   size:
        
//     最多读取的字节数。
        
//
        
// 返回结果:
        
//     读取的字节数。
        public virtual int ReadEntityBody(byte[] buffer, int size);
        
//
        
// 摘要:
        
//     使用指定的要从中读取数据的缓冲区、字节偏移量和最大字节数从客户端读取请求数据(当未预先加载时)。
        
//
        
// 参数:
        
//   buffer:
        
//     将数据读入的字节数组。
        
//
        
//   offset:
        
//     开始读取的位置的字节偏移量。
        
//
        
//   size:
        
//     最多读取的字节数。
        
//
        
// 返回结果:
        
//     读取的字节数。
        public virtual int ReadEntityBody(byte[] buffer, int offset, int size);
        
//
        
// 摘要:
        
//     将 Content-Length HTTP 标头添加到小于或等于 2 GB 的消息正文的响应。
        
//
        
// 参数:
        
//   contentLength:
        
//     响应的长度(以字节为单位)。
        public virtual void SendCalculatedContentLength(int contentLength);
        
//
        
// 摘要:
        
//     将 Content-Length HTTP 标头添加到大于 2 GB 的消息正文的响应。
        
//
        
// 参数:
        
//   contentLength:
        
//     响应的长度(以字节为单位)。
        public virtual void SendCalculatedContentLength(long contentLength);
        
//
        
// 摘要:
        
//     将标准 HTTP 标头添加到响应。
        
//
        
// 参数:
        
//   index:
        
//     标头索引。例如 System.Web.HttpWorkerRequest.HeaderContentLength。
        
//
        
//   value:
        
//     标头的值。
        public abstract void SendKnownResponseHeader(int index, string value);
        
//
        
// 摘要:
        
//     将指定文件的内容添加到响应并指定文件中的起始位置和要发送的字节数。
        
//
        
// 参数:
        
//   handle:
        
//     要发送的文件的句柄。
        
//
        
//   offset:
        
//     文件中的起始位置。
        
//
        
//   length:
        
//     要发送的字节数。
        public abstract void SendResponseFromFile(IntPtr handle, long offset, long length);
        
//
        
// 摘要:
        
//     将指定文件的内容添加到响应并指定文件中的起始位置和要发送的字节数。
        
//
        
// 参数:
        
//   filename:
        
//     要发送的文件的名称。
        
//
        
//   offset:
        
//     文件中的起始位置。
        
//
        
//   length:
        
//     要发送的字节数。
        public abstract void SendResponseFromFile(string filename, long offset, long length);
        
//
        
// 摘要:
        
//     将字节数组中指定数目的字节添加到响应。
        
//
        
// 参数:
        
//   data:
        
//     要发送的字节数组。
        
//
        
//   length:
        
//     要发送的字节数(从第一个字节开始)。
        public abstract void SendResponseFromMemory(byte[] data, int length);
        
//
        
// 摘要:
        
//     将内存块中指定数目的字节添加到响应。
        
//
        
// 参数:
        
//   data:
        
//     指向内存块的非托管指针。
        
//
        
//   length:
        
//     要发送的字节数。
        public virtual void SendResponseFromMemory(IntPtr data, int length);
        
//
        
// 摘要:
        
//     指定响应的 HTTP 状态代码和状态说明,例如 SendStatus(200, "Ok")。
        
//
        
// 参数:
        
//   statusCode:
        
//     要发送的状态代码
        
//
        
//   statusDescription:
        
//     要发送的状态说明。
        public abstract void SendStatus(int statusCode, string statusDescription);
        
//
        
// 摘要:
        
//     将非标准 HTTP 标头添加到响应。
        
//
        
// 参数:
        
//   name:
        
//     要发送的标头的名称。
        
//
        
//   value:
        
//     标头的值。
        public abstract void SendUnknownResponseHeader(string name, string value);
        
//
        
// 摘要:
        
//     在发送所有响应数据后注册可选通知。
        
//
        
// 参数:
        
//   callback:
        
//     在发送所有数据(带外)后调用的通知回调。
        
//
        
//   extraData:
        
//     回调的附加参数。
        public virtual void SetEndOfSendNotification(HttpWorkerRequest.EndOfSendNotification callback, object extraData);
        
// 摘要:
        
//     表示用于在完成发送响应后通知调用方的方法。
        
//
        
// 参数:
        
//   wr:
        
//     当前的 System.Web.HttpWorkerRequest。
        
//
        
//   extraData:
        
//     处理请求所需的任何其他数据。
        public delegate void EndOfSendNotification(HttpWorkerRequest wr, object extraData);
    }

 

[ASP.NET]ASP.NET底层的初步认识与理解

mikel阅读(431)

最近在国外的网站乱走一通,发现一些比较好的文章,收集整理加于自己的理解,作为笔记形式记录下来,让以后自己有个回忆。

ASP.NET是一个非常强大的构建Web应用的平台,它提供了极大的灵活性和能力以致于可以用它来构建所有类型的Web应用.绝大多数的人只熟悉 高层的框架如WebForms和WebServices-这些都在ASP.NET层次结构在最高层.在这篇文章中我将会讨论ASP.NET的底层机制并解 释请求(request)是怎么从Web服务器传送到ASP.NET运行时然后如何通过ASP.NET管道来处理请求.

ASP.NET是一个请求处理引擎.它接收一个发送过来的请求,把它传给内部的管道直到终点,作为一个开发人员的你可以在这里附加一些代码来处理请 求.这个引擎是和HTTP/Web服务器完全分隔的.事实上,HTTP运行时是一个组件,使你可以摆脱IIS或者任何其他的服务器程序,将你自己的程序寄 宿在内.

运行时提供了一个复杂但同时非常优雅的在管道中路由请求的机制.其中有很多相关的对象,大多数都是可扩展的(通过继承或者事件接口),在几乎所有的 处理流程上都是如此.所以这个框架具有高度可扩展性.通过这个机制,挂接到非常底层的接口(比如缓存,认证和授权)都变得可能了.你甚至可以在预处理或者 处理后过滤内容,也可以简单的将符合特殊标记的请求直接路由你的代码或者另一个URL上.存在着许多不同的方法来完成同一件事,但是所有这些方法都是可以 简单直接地实现的,同时还提供了灵活性,可以得到最好的性能和开发的简单性.

整个ASP.NET引擎是完全建立在托管代码上的,所有的扩展功能也是通过托管代码扩展来提供的.这是对.NET框架具有构建复杂而且高效的框架的 能力的最好的证明.ASP.NET最令人印象深刻的地方是深思熟虑的设计,使得框架非常的容易使用,又能提供挂接到请求处理的几乎所有部分的能力.

ASP.NET在微软的平台上就是通过ISAPI扩展来和IIS进行交互的,这个扩展寄宿着.NET运行时和ASP.NET运行时.ISAPI提供 了核心的接口,ASP.NET使用非托管的ISAPI代码通过这个接口来从Web服务器获取请求,并发送响应回客户端.ISAPI提供的内容可以通过通用 对象(例如HttpRequest和HttpResponse)来获取,这些对象通过一个定义良好并有很好访问性的接口来暴露非托管数据.

当用户发送一个URL请求时,在Web服务器端,IIS5或6,获得这个请求.在最底层,ASP.NET和IIS通过ISAPI扩展进行交互.在 ASP.NET环境中这个请求通常被路由到一个扩展名为.aspx的页面上,但是这个流程是怎么工作的完全依赖于处理特定扩展名的HTTP Handler是怎么实现的.在IIS中.aspx通过’应用程序扩展’(又称为脚本映射)被映射到ASP.NET的ISAPI扩展DLL- aspnet_isapi.dll.每一个请求都需要通过一个被注册到aspnet_isapi.dll的扩展名来触发ASP.NET(来处理这个请 求).

ISAPI是底层的非托管Win32 API.ISAPI定义的接口非常简单并且是为性能做了优化的.它们是非常底层的-处理指针和函数指针表来进行回调-但是它们提供了最底层和面向效率的接 口,使开发者和工具提供商可以用它来挂接到IIS上.因为ISAPI非常底层所以它并不适合来开发应用级的代码,而且ISAPI倾向于主要被用于桥接接 口,向上层工具提供应用服务器类型的功能.

下面来介绍HttpRuntime,HttpContext,HttpApplication

当一个请求到来时,它被路由到ISAPIRuntime.ProcessRequest()方法.这个方法调用HttpRuntime.ProcessRequest方法,它作一些重要的事情

为请求创建一个新的HttpContext实例
获取一个HttpApplication实例
调用HttpApplication.Init()方法来设置管道的事件
Init()方法触发开始ASP.NET管道处理的HttpApplication.ResumeProcessing()方法

首先一个新的HttpContext对象被创建并用来传递ISAPIWorkerRequest,这个上下文在整个请求的生命周期总都是可用的并总可以通过静态属性.
HttpContext.Currect 来访问.正像名字所暗示的那样,HttpContext对象代表了当前活动请求的上下文因为他包含了在请求生命周期中所有典型的你需要访问的重要对 象:Request,Response,Application,Server,Cache.在请求处理的任何时候 HttpContext.Current给你访问所有这些的能力.

HttpContext对象也包含一个非常有用的Items集合,你可以用它来保存针对特定请求的数据.上下文对象在请求周期的开始时被创建,在请 求结束时被释放,所有在Items集合中保存的数据只在这个特定的请求中可用.一个很好的使用的例子是请求日志机制,当你通过想通过在 Global.asax中挂接Application_BeginRequest和Application_EndRequest方法记录请求的开始和结 束时间(象在列表3中显示的那样).HttpContext对你就非常有用了-如果你在请求或页面处理的不同部分需要数据,你自由的使用它.

protected void Application_BeginRequest(Object sender, EventArgs e)
{
 if (App.Configuration.LogWebRequests)
 {
  Context.Items.Add("WebLog_StartTime",DateTime.Now);
 }
}

protected void Application_EndRequest(Object sender, EventArgs e)
{
 if (App.Configuration.LogWebRequests)
 {
  try
  { 
   TimeSpan Span = DateTime.Now.Subtract((DateTime) Context.Items["WebLog_StartTime"] );
   int MiliSecs = Span.TotalMilliseconds;
   WebRequestLog.Log(App.Configuration.ConnectionString, true, MilliSecs);
  } 
 }
}

HttpApplication

每个请求都被路由到一个HttpApplication对象上.HttpApplicationFactory类根据应用程序的负载为你的 ASP.NET应用创建一个HttpApplication对象池并为每个请求分发HttpApplication对象的引用.对象池的大小受 machine.config文件中ProcessModel键中的MaxWorkerThreads设置限制.
HttpApplication 是你的Web程序的外部包装器,而且它被映射到在Global.asax里面定义的类上.它是进入HttpRuntime的第一个入口点.如果你查看 Global.asax(或者对应的代码类)你会发现这个类直接继承自HttpApplication:
HttpApplication的主要职责是作为Http管道的事件控制器,所以它的接口主要包含的是事件.事件挂接是非常广泛的,大概包括以下这些:
BeginRequest
AuthenticateRequest
AuthorizeRequest
ResolveRequestCache
AquireRequestState
PreRequestHandlerExecute
PostRequestHandlerExecute
ReleaseRequestState
UpdateRequestCache
EndRequest

HttpModule和HttpHandler两者都是在HttpApplication.Init()函数调用的一部分中被载入并附加到调用链上

httpApplication它本身对发送给应用程序的数据一无所知-它只是一个通过事件来通讯的消息对象.它触发事件并通过 HttpContext对象来向被调用函数传递消息.实际的当前请求的状态数据由前面提到的HttpContext对象维护.它提供了所有请求专有的数据 并从进入管道开始到结束一直跟随请求

一旦管道被启动,HttpApplication开始象图六那样一个个的触发事件.每个事件处理器被触发,如果事件被挂接,这些处理器将执行它们自 己的任务.这个处理的主要任务是最终调用挂接到此特定请求的HttpHandler.处理器(handler)是ASP.NET请求的核心处理机制,通常 也是所有应用程序级别的代码被执行的地方.记住ASP.NET页面和Web服务框架都是作为HttpHandler实现,这里也是处理请求的的核心之处. 模块(module)趋向于成为一个传递给处理器(handler)的上下文的预处理或后处理器.ASP.NET中典型的默认处理器包括预处理的认证,缓 存以及后处理中各种不同的编码机制.

虽然HttpModule看上去很像ISAPI过滤器,它们都检查每个通过ASP.NET应用的请求,但是它们只检查映射到单个特定的 ASP.NET应用或虚拟目录的请求,也就是只能检查映射到ASP.NET的请求.这样你可以检查所有ASPX页面或者其他任何映射到ASP.NET的扩 展名.

实现一个HTTP模块是非常简单的:你必须实现之包含两个函数(Init()和Dispose())的IHttpModule接口.传进来的事件参 数中包含指向HTTPApplication对象的引用,这给了你访问HttpContext对象的能力.在这些方法上你可以挂接到 HttpApplication事件上.例如,如果你想挂接AuthenticateRequest事件到一个模块上

 

总的来说w3wp.exe调用.NET类库进行具体处理,顺序如下:ISAPIRuntim, HttpRuntime, HttpApplicationFactory, HttpApplication, HttpModule, HttpHandlerFactory, HttpHandler

有时间再对每个对象正进深入理解.

[C#]C# 4.0 新对象ExpandoObject

mikel阅读(433)

      今天无意中看了4.0的一些新特性,其中看到SystemDynamic 命名空间下的ExpandoObject 类很感兴趣,看了篇英文文章给大伙分享下。

先来看下该类的成员:

  http://msdn.microsoft.com/en-us/library/system.dynamic.expandoobject_members(VS.100).aspx

ExpandoObject instances can add and remove members at run time.什么意思呢?这意味着此类的实例能够在运行时动态的增加和删除成员。

其中有个新概念:dynamic language runtime (DLR)(动态语言运行时),我才疏学浅,还希望各位专家们多去研究下。

说说ExpandoObject这个动态特性的意义吧。

我们用XML来做下对比:

首先我们创建一个XML对象,

 


XElement contactXML =
    
new XElement("Contact",
        
new XElement("Name""Patrick Hines"),
        
new XElement("Phone""206-555-0144"),
        
new XElement("Address",
            
new XElement("Street1""123 Main St"),
            
new XElement("City""Mercer Island"),
            
new XElement("State""WA"),
            
new XElement("Postal""68042")
        )
    );

再来看看Dynamic对象,

 


dynamic contact = new ExpandoObject();
contact.Name 
= "Patrick Hines";
contact.Phone 
= "206-555-0144";
contact.Address 
= new ExpandoObject();
contact.Address.Street 
= "123 Main St";
contact.Address.City 
= "Mercer Island";
contact.Address.State 
= "WA";
contact.Address.Postal 
= "68402";

 

首先,我们看下dynamic对象的声明:dynamic contact = new ExpandoObject();

 

我没有写成 ExpandoObject contact = new ExpandoObject(), 因为我用静态的ExpandoObject 类型来声明则此对象没有在运行时增加成员的特性,所以我使用新的关键字dynamic.

其次,大家能注意到,我创建一个子节点只需要创建一个ExpandoObject实例作为contact对象的成员。

这样你可以很简单的看清父子节点之间的关系,更重要的是你可以很简单的访问每一个元素。

用linq to XML:

Console.WriteLine((string)contactXML.Element("Address").Element("State"));

用 ExpandoObject对象:

Console.WriteLine(contact.Address.State);
可是,当你有很多个contact对象时该怎么办呢?
呵呵,看代码:
//用XML 方式: XElement contactsXML =     new XElement("Contacts",         new XElement("Contact",             new XElement("Name""Patrick Hines"),             new XElement("Phone""206-555-0144")         ),         new XElement("Contact",             new XElement("Name""Ellen Adams"),             new XElement("Phone""206-555-0155")         )     ); //用dynamic对象: dynamic contacts = new List<dynamic>(); contacts.Add(new ExpandoObject()); contacts[0].Name = "Patrick Hines"; contacts[0].Phone = "206-555-0144"; contacts.Add(new ExpandoObject()); contacts[1].Name = "Ellen Adams"; contacts[1].Phone = "206-555-0155";

再来看看用Linq to Object怎么来操作dynamic吧,

 

var phones = from c in (contacts as List<dynamic>)
             
where c.Name == "Patrick Hines"
             select c.Phone;

 

大家看了这个新特性有什么感受呢?想不想立刻感受下C# 4.0?不管怎么样我是很期待啦。。希望.net越来越强大~~你可是我的饭碗啊(PS:坚决不会转向java)

[C#]C# 委托的同步调用和异步调用

mikel阅读(521)

转载:http://www.cnblogs.com/yinhu435/archive/2009/10/19/1585958.html

委托的Invoke方法用来进行同步调用。同步调用也可以叫阻塞调用,它将阻塞当前线程,然后执行调用,调用完毕后再继续向下进行。

同步调用的例子:

using System;
using System.Threading;
public delegate int AddHandler(int a, int b);
public class Foo {
static void Main() {
Console.WriteLine("**********SyncInvokeTest**************");
AddHandler handler = new AddHandler(Add);
int result = handler.Invoke(1,2);
Console.WriteLine("Do other work... ... ...");
Console.WriteLine(result);
Console.ReadLine();
}
static int Add(int a, int b) {
Console.WriteLine("Computing "+a+" + "+b+" ...");
Thread.Sleep(3000);
Console.WriteLine("Computing Complete.");
return a+b;
}
}

运行结果:

**********SyncInvokeTest**************

Computing 1 + 2 …

Computing Complete.

Do other work… … …

3

同步调用会阻塞线程,如果是要调用一项繁重的工作(如大量IO操作),可能会让程序停顿很长时间,造成糟糕
的用户体验,这时候异步调用就很有必要了。
异步调用不阻塞线程,而是把调用塞到线程池中,程序主线程或UI线程可以继续执行。
委托的异步调用通过BeginInvoke和EndInvoke来实现。
异步调用:

using System;
using System.Threading;
public delegate int AddHandler(int a, int b);
public class Foo {
static void Main() {
Console.WriteLine("**********AsyncInvokeTest**************");
AddHandler handler = new AddHandler(Add);
IAsyncResult result = handler.BeginInvoke(1,2,null,null);
Console.WriteLine("Do other work... ... ...");
Console.WriteLine(handler.EndInvoke(result));
Console.ReadLine();
}
static int Add(int a, int b) {
Console.WriteLine("Computing "+a+" + "+b+" ...");
Thread.Sleep(3000);
Console.WriteLine("Computing Complete.");
return a+b;
}
}

运行结果: **********AsyncInvokeTest**************
Do other work… … …
Computing 1 + 2 …
Computing Complete.
3  

可以看到,主线程并没有等待,而是直接向下运行了。
但是问题依然存在,当主线程运行到EndInvoke时,如果这时调用没有结束(这种情况很可能出现),这时为了等待调用结果,线程依旧会被阻塞。
解决的办法是用回调函数,当调用结束时会自动调用回调函数
回调异步:

public class Foo {
static void Main() {
Console.WriteLine("**********AsyncInvokeTest**************");
AddHandler handler = new AddHandler(Add);
IAsyncResult result = handler.BeginInvoke(1,2,new AsyncCallback(AddComplete),"AsycState:OK");
Console.WriteLine("Do other work... ... ...");
Console.ReadLine();
}
static int Add(int a, int b) {
Console.WriteLine("Computing "+a+" + "+b+" ...");
Thread.Sleep(3000);
Console.WriteLine("Computing Complete.");
return a+b;
}
static void AddComplete(IAsyncResult result) {
AddHandler handler = (AddHandler)((AsyncResult)result).AsyncDelegate;
Console.WriteLine(handler.EndInvoke(result));
Console.WriteLine(result.AsyncState);
}
}

[MVC]ASP.Net MVC 源码解析系列

mikel阅读(645)

转载自Q.yuChen的blog
他写的这系列的文章比较清晰明了,值得收藏,对学习ASP.NET MVC Framework原理很有帮助,特收藏之
ASP.NET MVC

ASP.NET MVC Preview 2 – 流程分析 (1)
ASP.NET MVC Preview 2 – 流程分析 (2)
ASP.NET MVC Preview 2 – 流程分析 (3)
ASP.NET MVC Preview 2 – RedirectToAction
ASP.NET MVC Preview 2 – ClientNoCacheFilterAttribute
ASP.NET MVC Preview 2 – AuthenticateFilterAttribute
ASP.NET MVC Preview 2 – ActionCacheFilterAttribute
ASP.NET MVC Preview 2 – NVelocityViewEngine
ASP.NET MVC Preview 2 – Context
ASP.NET MVC Preview 2 – ControllerActionFilter
ASP.NET MVC Preview 3 – 1. MvcHandler
ASP.NET MVC Preview 3 – 2. Controller
ASP.NET MVC Preview 3 – 3. View
ASP.NET MVC Preview 3 – 4. Route
ASP.NET MVC Preview 4 – 1. ActionInvoker
ASP.NET MVC Preview 4 – 2. Filter
ASP.NET MVC 1.0 – 1. 准备工作
ASP.NET MVC 1.0 – 2. 流程分析 (System.Web.Routing)
ASP.NET MVC 1.0 – 3. 流程分析 (MvcHandler & Controller)
ASP.NET MVC 1.0 – 4. 流程分析 (ControllerActionInvoker)
ASP.NET MVC 1.0 – 5. 流程分析 (ControllerActionInvoker 续)
ASP.NET MVC 1.0 – 6. 流程分析 (ViewResult)
ASP.NET MVC 1.0 – 7. Route Namespace
ASP.NET MVC 1.0 – 8. TempData
ASP.NET MVC 1.0 – 9. ModelBinder
ASP.NET MVC 1.0 – 10. Controller
ASP.NET MVC 1.0 – 11. ViewData
ASP.NET MVC 1.0 – 12. NVelocityViewEngine
ASP.NET MVC 1.0 – 13. OutputCacheAttribute
ASP.NET MVC 1.0 – 14. CompressAttribute
ASP.NET MVC 1.0 – 15. StaticCacheAttribute
ASP.NET MVC 1.0 – 16. NoClientCacheAttribute
ASP.NET MVC 1.0 – 17. Anti Attack
ASP.NET MVC 1.0 – 18. ControllerContext

[MYSQL]理解MySQL——架构与概念

mikel阅读(539)

转载:http://www.cnblogs.com/hustcat/archive/2009/10/18/1585626.html

写在前面:最早接触的MySQL是在三年前,那时候MySQL还是4.x版本,很多功能都不支持,比如,存储过程,视图,触发器,更别说分布式事务 等复杂特性了。但从5.0(2005年10月)开始,MySQL渐渐步入企业级数据库的行列了;复制、集群、分区、分布式事务,这些企业级的特性,使得现 在的MySQL,完全可以应用于企业级应用环境(很多互联网公司都用其作为数据库服务器,尽管节约成本是一个因素,但是没有强大功能作后盾,则是不可想象 的)。虽然,MySQL还有很多不足,比如,复制、分区的支持都十分有限、查询优化仍需要改进,但是MySQL已经是一个足够好的DBMS了,更何况它是 opensource的。这段时间没有事,出于好奇,略微的研究了一下MySQL,积累了一些资料,欲总结出来。这些资料打算分为两部分,上部主要讨论 MySQL的优化,其中主要参考了《MySQL Manual》和《High Performance MySQL》,如果有时间,以后在下部分析一下MySQL的源码。如果你是MySQL高手,希望你不吝赐教;如果你是新手,希望对你有用。

 

第一章、MySQL架构与概念
1、MySQL的逻辑架构

 最上面不是MySQL特有的,所有基于网络的C/S的网络应用程序都应该包括连接处理、认证、安全管理等。
中间层是MySQL的核心,包括查询解析、分析、优化和缓存等。同时它还提供跨存储引擎的功能,包括存储过程、触发器和视图等。
最下面是存储引擎,它负责存取数据。服务器通过storage engine API可以和各种存储引擎进行交互。
1.1、查询优化和执行(Optimization and Execution)

MySQL 将用户的查询语句进行解析,并创建一个内部的数据结构——分析树,然后进行各种优化,例如重写查询、选择读取表的顺序,以及使用哪个索引等。查询优化器不 关心一个表所使用的存储引擎,但是存储引擎会影响服务器如何优化查询。优化器通过存储引擎获取一些参数、某个操作的执行代价、以及统计信息等。在解析查询 之前,服务器会先访问查询缓存(query cache)——它存储Select语句以及相应的查询结果集。如果某个查询结果已经位于缓存中,服务器就不会再对查询进行解析、优化、以及执行。它仅仅 将缓存中的结果返回给用户即可,这将大大提高系统的性能。

 1.2、并发控制
MySQL提供两个级别的并发控制:服务器级(the server level)和存储引擎级(the storage engine level)。加锁是实现并发控制的基本方法,MySQL中锁的粒度:
(1)    表级锁:MySQL独立于存储引擎提供表锁,例如,对于Alter TABLE语句,服务器提供表锁(table-level lock)。
(2)    行级锁:InnoDB和Falcon存储引擎提供行级锁,此外,BDB支持页级锁。InnoDB的并发控制机制,下节详细讨论。
另外,值得一提的是,MySQL的一些存储引擎(如InnoDB、BDB)除了使用封锁机制外,还同时结合MVCC机制,即多版本两阶段封锁协议(Multiversion two-phrase locking protocal),来实现事务的并发控制,从而使得只读事务不用等待锁,提高了事务的并发性。
注:并发控制是DBMS的核心技术之一(实际上,对于OS也一样),它对系统性能有着至关重要的影响,以后再详细讨论。

1.3、事务处理
MySQL中,InnoDB和BDB都支持事务处理。这里主要讨论InnoDB的事务处理(关于BDB的事务处理,也十分复杂,以前曾较为详细看过其源码,以后有机会再讨论)。
1.3.1、事务的ACID特性
事务是由一组SQL语句组成的逻辑处理单元,事务具有以下4个属性,通常简称为事务的ACID属性(Jim Gray在《事务处理:概念与技术》中对事务进行了详尽的讨论)。
(1)原子性(Atomicity):事务是一个原子操作单元,其对数据的修改,要么全都执行,要么全都不执行。
(2)一致性(Consistent):在事务开始和完成时,数据都必须保持一致状态。这意味着所有相关的数据规则都必须应用于事务的修改,以保持数据的完整性;事务结束时,所有的内部数据结构(如B树索引或双向链表)也都必须是正确的。
(3)隔离性(Isolation):数据库系统提供一定的隔离机制,保证事务在不受外部并发操作影响的“独立”环境执行。这意味着事务处理过程中的中间状态对外部是不可见的,反之亦然。
(4)持久性(Durable):事务完成之后,它对于数据的修改是永久性的,即使出现系统故障也能够保持。
1.3.2、事务处理带来的相关问题
由于事务的并发执行,带来以下一些著名的问题:
(1)更新丢失(Lost Update):当两个或多个事务选择同一行,然后基于最初选定的值更新该行时,由于每个事务都不知道其他事务的存在,就会发生丢失更新问题--最后的更新覆盖了由其他事务所做的更新。
(2) 脏读(Dirty Reads):一个事务正在对一条记录做修改,在这个事务完成并提交前,这条记录的数据就处于不一致状态;这时,另一个事务也来读取同一条记录,如果不加 控制,第二个事务读取了这些“脏”数据,并据此做进一步的处理,就会产生未提交的数据依赖关系。这种现象被形象地叫做"脏读"。
(3)不可重复读(Non-Repeatable Reads):一个事务在读取某些数据后的某个时间,再次读取以前读过的数据,却发现其读出的数据已经发生了改变、或某些记录已经被删除了!这种现象就叫做“不可重复读”。
(4)幻读(Phantom Reads):一个事务按相同的查询条件重新读取以前检索过的数据,却发现其他事务插入了满足其查询条件的新数据,这种现象就称为“幻读”。
1.3.3、事务的隔离性
SQL2标准定义了四个隔离级别。定义语句如下:
SET TRANSACTION ISOLATION LEVEL
[READ UNCOMMITTED |
READ COMMITTED  |
REPEATABLE READ  |
SERIALIZABLE ]
这 与Jim Gray所提出的隔离级别有点差异。其中READ UNCOMMITTED即Jim的10(浏览);READ COMMITTED即20,游标稳定性;REPEATABLE READ为2.99990隔离(没有幻像保护);SERIALIZABLE隔离级别为30,完全隔离。SQL2标准默认为完全隔离(30)。各个级别存在 问题如下:

隔离级

脏读

不可重复读

幻象读

读未提交

(Read uncommitted)

可能

可能

可能

读提交

(Read committed)

不可能

可能

可能

可重复读

(Repeatable read)

不可能

不可能

可能

可串行化

(Serializable)

不可能

不可能

不可能

各 个具体数据库并不一定完全实现了上述4个隔离级别,例如,Oracle只提供READ COMMITTED和Serializable两个标准隔离级别,另外还提供自己定义的Read only隔离级别;SQL Server除支持上述ISO/ANSI SQL92定义的4个隔离级别外,还支持一个叫做“快照”的隔离级别,但严格来说它是一个用MVCC实现的Serializable隔离级别。MySQL 支持全部4个隔离级别,其默认级别为Repeatable read,但在具体实现时,有一些特点,比如在一些隔离级别下是采用MVCC一致性读。国产数据库DM也支持所有级别,其默认级别为READ COMMITTED。
 

1.3.4、InnoDB的锁模型
InnoDB的行级锁有两种类型:
(1)共享锁(shared lock,S):允许一个事务去读一行,阻止其他事务获得相同数据集的排他锁。
(2)排它锁(exclusive lock,X):允许获得排它锁的事务更新数据,阻止其他事务取得相同数据集的共享读锁和排他写锁。
此外,InnoDB支持多粒度加锁(multiple granularity locking),从而允许对记录和表同时加锁。为此,InnoDB引入意向锁(intention locks),意向锁是针对表的:
(1)意向共享锁(IS):事务打算给数据行加行共享锁,事务在给一个数据行加共享锁前必须先取得该表的IS锁。
(2)意向排他锁(IX):事务打算给数据行加行排他锁,事务在给一个数据行加排他锁前必须先取得该表的IX锁。
例如,Select … LOCK IN SHARE MODE加IS锁,Select … FOR Update加IX锁,意向锁的规则如下:
(1)事务在对表T中的记录获取S锁前,先要获取表T的IS锁或者更强的锁;
(2)事务在获取表T中记录的X锁前,先要获取表T的IX锁。
InnoDB的锁相容性矩阵:

 如 果一个事务请求的锁模式与当前的锁兼容,InnoDB就将请求的锁授予该事务;反之,如果两者不兼容,该事务就要等待锁释放。意向锁只会阻塞其它事务对表 的请求,例如,LOCK TABLES …WRITE,意向锁的主要目的是表明该事务将要或者正在对表中的记录加锁。使用封锁机制来进行并发控制,一个比较重要的问题就是死锁。
来看一个死锁的例子:

1-1

Session 1

Session 2

mysql> Create TABLE t (i INT) ENGINE = InnoDB;

Query OK, 0 rows affected (0.22 sec)

 

mysql> Insert INTO t (i) VALUES(1);

Query OK, 1 row affected (0.08 sec)

 

mysql> START TRANSACTION;

Query OK, 0 rows affected (0.00 sec)

 

mysql> Select * FROM t Where i = 1 LOCK IN SHARE MODE;

+——+

| i    |

+——+

|    1 |

+——+

1 row in set (0.01 sec)

 

mysql>

 

 

mysql> START TRANSACTION;

Query OK, 0 rows affected (0.00 sec)

 

mysql> Delete FROM t Where i = 1;

等待

mysql> Delete FROM t Where i = 1;

等待

 

 

ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction

Query OK, 1 row affected (0.00 sec)

 

 

 1.3.5、一致性非阻塞读
一致性读是MySQL的重要特点之一,InnoDB通过MVCC机制表示数据库某一时刻的查询快照,查询可以看该时刻之前提交的事务所做的改变,但是不能看到该时刻之后或者未提交事务所做的改变。但是,查询可以看到同一事务中之前语句所做的改变,例如:

1-2

Session 1

Session 2

mysql> select * from t;

Empty set (0.00 sec)

 

mysql> Insert INTO t (i) VALUES(1);

Query OK, 1 row affected (0.00 sec)

 

mysql> select * from t;

+——+

| i    |

+——+

|    1 |

+——+

1 row in set (0.00 sec)

 

mysql> set autocommit = 0;

Query OK, 0 rows affected (0.01 sec)

 

mysql> update t set i=3;

Query OK, 1 row affected (0.00 sec)

Rows matched: 1 Changed: 1 Warnings: 0

mysql> select * from t;

+——+

| i    |

+——+

|    3 |

+——+

1 row in set (0.00 sec)

 

 

mysql> set autocommit = 0;

Query OK, 0 rows affected (0.00 sec)

 

mysql> select * from t;

+——+

| i    |

+——+

|    1 |

+——+

1 row in set (0.00 sec)

mysql> commit;

Query OK, 0 rows affected (0.06 sec)

 

 

mysql> select * from t;

+——+

| i    |

+——+

|    1 |

+——+

1 row in set (0.00 sec)

 

mysql> commit;

Query OK, 0 rows affected (0.00 sec)

 

mysql> select * from t;

+——+

| i    |

+——+

|    3 |

+——+

1 row in set (0.00 sec)

 

 如果事务的隔离级别为REPEATABLE READ(默认),同一个事务中的所有一致性读都是读的事务的第一次读操作创建的快照。你可以提交当前事务,然后在新的查询中即可看到最新的快照,如上所示。
如果事务的隔离级别为READ COMMITTED,一致性读只是对事务内部的读操作和它自己的快照而言的,结果如下:

1-3

Session 1

Session 2

mysql> SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;

Query OK, 0 rows affected (0.01 sec)

 

mysql> set autocommit = 0;

Query OK, 0 rows affected (0.00 sec)

 

mysql> select * from t;

+——+

| i    |

+——+

|    3 |

+——+

1 row in set (0.00 sec)

 

 

mysql> SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;

Query OK, 0 rows affected (0.01 sec)

 

mysql> set autocommit = 0;

Query OK, 0 rows affected (0.00 sec)

 

mysql> select * from t;

+——+

| i    |

+——+

|    3 |

+——+

1 row in set (0.00 sec)

mysql> update t set i=5;

Query OK, 1 row affected (0.00 sec)

Rows matched: 1 Changed: 1 Warnings: 0

 

 

mysql> select * from t;

+——+

| i    |

+——+

|    3 |

+——+

1 row in set (0.00 sec)

mysql> commit;

Query OK, 0 rows affected (0.06 sec)

 

 

mysql> select * from t;

+——+

| i    |

+——+

|    5 |

+——+

1 row in set (0.00 sec)

 

 注意,session 2发生了不可重复读。
当InnoDB在READ COMMITTED 和REPEATABLE READ隔离级别下处理Select语句时,一致性读是默认的模式。一致性读不会对表加任何锁,所以,其它连接可以同时改变表。
假设事务处于REPEATABLE READ级别,当你正在进行一致性读时,InnoDB根据查询看到的数据给你一个时间点。如果其它的事务在该时间点之后删除一行,且提交事务,你不会看到行已经被删除,插入和更新操作一样。但是,InnoDB与其它DBMS的不同是,在REPEATABLE READ隔离级别下并不会造成幻像。
一致性读不与Drop TABLE 或者 Alter TABLE一起工作。
在 nodb_locks_unsafe_for_binlog变量被设置或者事务的隔离级别不是SERIALIZABLE的情况下,InnoDB对于没有指 定FOR Update 或 LOCK IN SHARE MODE的Insert INTO … Select, Update … (Select), 和Create TABLE … Select语句使用一致性读,在这种情况下,查询语句不会对表中的元组加锁。否则,InnoDB将使用锁。

1.3.6、Select … FOR Update和Select … LOCK IN SHARE MODE的加锁读(locking read)
在一些场合,一致性读并不是很方便,此时,可以用加锁读。InnoDB支持两种加锁读:
(1)    Select … LOCK IN SHARE MODE:对读取的元组加S锁。
(2)    Select … FOR Update:在扫描索引记录的过程中,会阻塞其它连接的Select …LOCK IN SHARE MODE和一定事务隔离级别下的读操作。
InnoDB 使用两阶段封锁协议,事务直到提交或回滚时才会释放所有的锁,这都是系统自动执行的。此外,MySQL支持LOCK TABLES和UNLOCK TABLES,但这些都是在服务器层实现的,而不是在存储引擎。它们有用处,但是不能取代存储引擎完成事务处理,如果你需要事务功能,请使用事务型存储引 擎。
来考虑locking read的应用,假设你要在表child插入一个新的元组,并保证child中的记录在表parent有一条父记录。如果你用一致性读来读parent 表,确实可以将要插入的child row的parent row,但是可以安全的插入吗?不,因为在你读parent表时,其它连接可能已经删除该记录。(一致性读是针对事务内而言的,对于数据库的状态,它应该 叫做“不一致性读”)
此时,就可以使用Select LOCK IN SHARE MODE,它会对读取的元组加S锁,从而防止其它连接删除或更新元组。另外,如果你想在查询的同时,进行更新操作,可以使用Select … FOR Update,它读取最新的数据,然后对读到的元组加X锁。此时,使用Select … LOCK IN SHARE MODE不是一个好主意,因为此时如果有两个事务进行这样的操作,就会造成死锁。
注:Select … FOR Update仅在自动提交关闭(即手动提交)时才会对元组加锁,而在自动提交时,符合条件的元组不会被加锁。

1.3.7、记录锁(record lok)、间隙锁(gap lock)和后码锁(next-key lock)
InnoDB有以下几种行级锁:
(1) 记录锁:对索引记录(index records)加锁,InnoDB行级锁是通过给索引的索引项加锁来实现的,而不是对记录实例本身加锁。如果表没有定义索引,InnoDB创建一个隐藏 的聚簇索引,然后用它来实现记录加锁(关于索引与加锁之间的关系的详细介绍请看下一章)。
(2)间隙锁:对索引记录之间的区间,或者第一个索引记录之前的区间和最后一个索引之后的区间加锁。
(3)后码锁:对索引记录加记录锁,且对索引记录之前的区间加锁。
默认情况下,InnoDB的事务工作在REPEATABLE READ的隔离级别,而且系统变量innodb_locks_unsafe_for_binlog为关闭状态。此时,InnoDB使用next-key锁进行查找和索引扫描,从而达到防止“幻像”的目的。
Next- key锁是记录锁和间隙的结合体。当InnoDB查找或扫描表的索引时,对它遇到的索引记录加S锁或者X锁,所以,行级锁(row-level lock)实际上就是索引记录锁(index-record lock);此外,它还对索引记录之前的区间加锁。也就是说,next-key锁是索引记录锁,外加索引记录之前的区间的间隙锁。如果一个连接对索引中的 记录R持有S或X锁,其它的连接不能按照索引的顺序在R之前的区间插入一个索引记录。
假设索引包含以下值:10, 11,13和20,则索引的next-key锁会覆盖以下区间(“(”表示不包含,“[”表示包含):
(negative infinity, 10]
(10, 11]
(11, 13]
(13, 20]
(20, positive infinity)
对于最后一个区间,next-key锁将锁住索引最大值以上的区间,上界虚记录(“supremum” pseudo-record)的值比索引中的任何值都大,其实,上界不是一个真实的索引记录,所以,next-lock将对索引的最大值之后的区间加锁。
间隙锁对查询唯一索引中的唯一值是没有必要的,例如,id列有唯一索引,则下面的查询仅对id=100的元组加索引记录锁(index-record lock),而不管其它连接是否在之前的区间插入元组。
Select * FROM child Where id = 100;
如果id没有索引,或者非唯一索引,则语句会锁住之前的空间。

例1-4
Session 1
Session 2
mysql> create unique index i_index on t(i);
Query OK, 0 rows affected (0.19 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> select * from t;
+------+
| i    |
+------+
|    4 |
|   10 |
+------+
2 rows in set (0.00 sec)
 
 
 
mysql> set autocommit=0;
Query OK, 0 rows affected (0.00 sec)
mysql> select i from t where i =10 lock in share mode;
+------+
| i    |
+------+
|   10 |
+------+
1 row in set (0.00 sec)
mysql> insert into t(i) values(9);
Query OK, 1 row affected (0.03 sec)
 
 
mysql> select * from t;
+------+
| i    |
+------+
|    4 |
|    9 |
|   10 |
+------+
3 rows in set (0.00 sec)

 上例中,产生了幻像问题。如果将唯一查询变成范围查询,结果如下(接上例的索引):

1-5

Session 1

Session 2

mysql> select * from t;

+——+

| i    |

+——+

|    4 |

|    9 |

|   10 |

+——+

3 rows in set (0.00 sec)

 

 

mysql> set autocommit=0;

Query OK, 0 rows affected (0.00 sec)

mysql> select i from t where i>4 lock in share mode;

+——+

| i    |

+——+

|    9 |

|   10 |

+——+

2 rows in set (0.00 sec)

mysql> insert into t(i) values(1);

ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

mysql> insert into t(i) values(8);

ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

 

 可以看到,session 2 的next-key使得在i=4之前的区间和之后的插入都被阻塞。
另外,如果删除索引i_index,则结果如下:

1-6

Session 1

Session 2

mysql> drop index i_index on t;

Query OK, 3 rows affected (0.25 sec)

Records: 3 Duplicates: 0 Warnings: 0

mysql> select * from t;

+——+

| i    |

+——+

|    4 |

|   10 |

|    9 |

+——+

3 rows in set (0.00 sec)

 

 

mysql> set autocommit=0;

Query OK, 0 rows affected (0.00 sec)

 

mysql> select i from t lock in share mode;

+——+

| i    |

+——+

|    4 |

|   10 |

|    9 |

+——+

3 rows in set (0.00 sec)

mysql> insert into t(i) values(8);

等待。。。

 

 

 另 外,针对插入(Insert)操作,只要多个事务不会在同一索引区间的同一个位置插入记录,它们就不用互相等待,这种情况可以称为插入意向间隙锁 (insertion intention gap lock)。例如,索引记录的值为4和7,两个独立的事务分别插入5和6,仅管它们都持有4—7之间的间隙锁,但是它们不会相互阻塞。这可以提高事务的并 发性。

1-7

Session 1

Session 2

mysql> select * from t;

+——+

| i    |

+——+

|    4 |

|   10 |

|    9 |

|    8 |

+——+

4 rows in set (0.00 sec)

 

mysql> create unique index i_index on t(i);

Query OK, 4 rows affected (0.34 sec)

Records: 4 Duplicates: 0 Warnings: 0

 

mysql> set autocommit=0;

Query OK, 0 rows affected (0.00 sec)

 

 

mysql> set autocommit=0;

Query OK, 0 rows affected (0.00 sec)

mysql> insert into t(i) values(5);

Query OK, 1 row affected (0.00 sec)

 

 

mysql> insert into t(i) values(5);

ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

mysql> insert into t(i) values(6);

Query OK, 1 row affected (0.00 sec)

 间隙锁是可以显示关闭的,如果你将事务的隔离级别设为READ COMMITTED,或者打开innodb_locks_unsafe_for_binlog系统变量,间隙锁就会关闭。在这种情况下,查找或扫描索引仅会进行外键约束检查和重复键值检查。
此 外,READ COMMITTED隔离级别和关闭nodb_locks_unsafe_for_binlog还有另外一个负作用:MySQL会释放掉不匹配Where条 件的记录锁。例如,对于Update语句,InnoDB只能进行“半一致性(semi_consistent)读”,所以,它会返回最新提交事务所做改 变,从而产生不可重复读和幻像问题。
1.3.8、使用next-key lock防止幻像问题
例1-4展示了一个幻像问题。使用next-key锁的select语句可以解决幻像问题,但例1-4的之所以会产生总是在于唯一索引,使得select语句没有使用gap lock,而只使用了index-record lock。

1.4、存储引擎
插 件式存储引擎是MySQL最重要特性之一,也是最不同于其它DBMS的地方。MySQL支持很多存储引擎,以适用于不同的应用需求,常用的包括 MyISAM、InnoDB、BDB、MEMORY、MERGE、NDB Cluster等。其中,BDB和NDB Cluster提供事务支持。
MySQL 默认的存储引擎为MyISAM,当然,创建表的时候可以指定其它的存储引擎,你可以在同一个数据库中对不同的表使用不同的存储引擎(这是非常强大而独特的 特性)。可以通过SHOW TABLE STATUS命令查询表所使用的存储引擎,例如,查看mysql数据库的user表:

mysql> SHOW TABLE STATUS LIKE 'user' \G

*************************** 1. row ***************************

           Name: user

         Engine: MyISAM

        Version: 10

     Row_format: Dynamic

           Rows: 4

 Avg_row_length: 61

    Data_length: 244

Max_data_length: 281474976710655

   Index_length: 2048

      Data_free: 0

 Auto_increment: NULL

    Create_time: 2009-06-16 21:50:34

    Update_time: 2009-09-30 14:59:08

     Check_time: NULL

      Collation: utf8_bin

       Checksum: NULL

 Create_options:

        Comment: Users and global privileges

1 row in set (0.00 sec)

 Name:表的名称;
Engine:表使用的存储引擎;
Row_format:记录的格式。MyISAM支持三种不同的存储格式:静态(固定长度)表(默认格式)、动态表及压缩表。静态表的字段都是固定长度的,例如CHAR和INTEGER;动态表的字段可以是变长的,例如,VARCHAR或者BLOB。
Rows:表中记录的数量。
Avg_row_length:记录的平均长度(字节数);
Data_length:表中数据的全部字节数;
Max_data_length:表中数据最大的字节数;
Index_length:索引消耗的磁盘空间;
Data_free:对于MyISAM表,表示已经分配但还没有使用的空间;该空间包含以前删除的记录留下的空间,可以被Insert操作重用。
Auto_increment:下一个自增的值。
Check_time:上次使用CHECK TABLE或myisamchk检查表的时间。

1.4.1、MyISAM
1.4.1.1、存储
MySQL的默认存储引擎,性能与功能的折中,包括全文索引(full-text index)、数据压缩,支持空间(GIS)数据,但是,不支持事务和行级锁。一般来说,MyISAM更适用于大量查询操作。如果你有大量的插入、删除操作,你应该选择InnoDB。
每个表包含3个文件:
(1).frm:表定义文件,对于其它存储引擎也一样。
(2).MYD文件:数据文件。
(3).MYI文件:索引文件。
可以在创建表时通过DATA DIRECTORY和INDEX DIRECTORY为数据文件和索引文件指定路径,它们可以位于不同目录。另外,MyISAM的存储格式是跨平台的,你可以将数据文件和索引文件从Intel平台拷贝到PPC或者SPARC平台。
5.0 中,MyISAM的变长记录表默认处理256TB数据,使用6字节的指针来指向数据记录;而之前的版本使用默认的4字节指针,所以只能处理4GB数据。所 有的版本都可以将指针增加到8字节指针,如果你想改变MyISAM表的指针的大小,可以通过设置MAX_ROWS和AVG_ROW_LENGTH来实现:
Create TABLE mytable (
   a    INTEGER  NOT NULL PRIMARY KEY,
   b    CHAR(18) NOT NULL
) MAX_ROWS = 1000000000 AVG_ROW_LENGTH = 32;
上面的例子中,MySQL将至少可以存储32GB的数据。可以查看一下表的信息:

mysql> SHOW TABLE STATUS LIKE 'mytable' \G

*************************** 1. row ***************************

           Name: mytable

         Engine: MyISAM

     Row_format: Fixed

           Rows: 0

 Avg_row_length: 0

    Data_length: 0

Max_data_length: 98784247807

   Index_length: 1024

      Data_free: 0

 Auto_increment: NULL

    Create_time: 2002-02-24 17:36:57

    Update_time: 2002-02-24 17:36:57

     Check_time: NULL

 Create_options: max_rows=1000000000 avg_row_length=32

        Comment:

1 row in set (0.05 sec)

可以看到,Create_options列出了创建时的选项,而且该表的最大的数据量为91GB。你可以用Alter TABLE来改变指针的大小,但是那会导致表和索引的重建,这会花费很长的时间。
1.4.1.2、MyISAM的特性
(1)锁与并发性:MyISAM只有表级锁,不支持行级锁。所以不适合于大量的写操作,但是它支持并发插入(concurrent inserts),这是一个非常重要且有用的特性。
(2)自动修复:MySQL支持自动检查和修复MyISAM表。
(3)手动修复:你可以使用CHECK TABLE检查表的状态,并用REPAIR TABLE修复表。
(4)索引:你可以为BLOB和TEXT的前500个字符创建索引。而且,MyISAM还支持全文索引,但仅限于CHAR、VARCHAR、和TEXT列。
(5) 延迟键写(Delayed key writes):如果创建MyISAM表时指定DELAY_KEY_WRITE,MySQL在查询结束时,不会将改变的索引数据写入磁盘,而将修改保存在 key buffer中。只有要改变缓存或者关闭表时,才会把索引数据刷入磁盘。
1.4.2、InnoDB
InnoDB是一个高性能的事务存储引擎,此外,BDB也支持事务处理(关于BDB,以前曾较为详细的阅读过其源码,以后有时间再讨论),它有以下一些特点:
1.4.2.1、表空间
InnoDB存储表和索引有两种方式:
(1)共享表空间存储:这种方式下,表的定义位于.frm文件中,数据和索引保存在innodb_data_home_dir和innodb_data_file_path指定的表空间中。
(2)多表空间存储:表的定义仍位于.frm文件,但是,每个InnoDB表和它的索引在它自己的文件(.idb)中,每个表有它自己的表空间。
对那些想把特定表格移到分离物理磁盘的用户,或者那些希望快速恢复单个表的备份而无须打断其余InnoDB表的使用的用户,使用多表空间会是有益的。你可以往my.cnf的[mysqld]节添加下面行来允许多表空间:
[mysqld]
innodb_file_per_table
重 启服务器之后,InnoDB存储每个新创建的表到表格所属于的数据库目录下它自己的文件tbl_name.ibd里。这类似于MyISAM存储引擎所做 的,但MyISAM 把表分成数据文件tbl_name.MYD和索引文件tbl_name.MYI。对于InnoDB,数据和所以被一起存到.ibd文件。 tbl_name.frm文件照旧依然被创建。
如果你从my.cnf文件删除innodb_file_per_table行,并重启服务器,InnoDB在共享的表空间文件里再次创建表。
innodb_file_per_table只影响表的创建。如果你用这个选项启动服务器,新表被用.ibd文件来创建,但是你仍旧能访问在共享表空间里的表。如果你删掉这个选项,新表在共享表空间内创建,但你仍旧可以访问任何用多表空间创建的表。
InnoDB总是需要共享表空间,.ibd文件对InnoDB不足以去运行,共享表空间包含熟悉的ibdata文件,InnoDB把内部数据词典和undo日志放在这个文件中。
1.4.2.2、外键约束
MySQL中,支持外键的存储引擎只有InnoDB,在创建外键时,要求被参照表必须有对应的索引,参照表在创建外键时也会自动创建对应的索引。
1.4.2.3、MVCC与后码锁(next-key locking)
InnoDB 将MVCC机制与next-key lock结合起来,实现事务的各个隔离级别,这是非常用意思的。在nodb_locks_unsafe_for_binlog变量被设置或者事务的隔离级 别不是SERIALIZABLE的情况下,InnoDB对于没有指定FOR Update 或 LOCK IN SHARE MODE的Insert INTO … Select, Update … (Select), 和Create TABLE … Select语句使用一致性读(参照前面),在这种情况下,查询语句不会对表中的元组加锁。否则,InnoDB将使用锁。

 

主要参考:

 《MySQL Manual》

 《High Performance MySQL》

[工具]Visual Studio Team System 2008正式版下载

mikel阅读(504)

Microsoft Visual Studio Team System 2008 Team Suite正式版已于2007/11/19在MSDN Subscriptions开放下载。
微 软官方网站仅提供Visual Studio Team System 2008 Beta 2 Team Suite(90-day Trial) 和Visual Studio 2008 Beta 2 Team Foundation Server (Virtual PC) 下载。
Visual Studio Team System 2008正式版下载地址 – Visual Studio Team System 2008 Team Suite download
Microsoft Visual Studio Team System 2008 Team Suite.iso

引用
ed2k://|file|Microsoft.Visual.Studio.Team.System.2008.Team.Suite-ZWTiSO.iso|4112060416|7730FC9FFA4E1A1AB2A070C61BFD634B|h=YDLIF7QGESBVMJNATFOZA47X4YLBLY6H|/

Visual Studio Team System 2008 Team Suite正式版下载(eMule下载)
Microsoft Visual Studio Team System 2008 Team Suite正式版下载(BT下载)
微软Visual Studio Team System 2008正式版激活方法
如果下载过MSDN上的Visual Studio Team System 2008 Beta 2 Team Suite 90天trial版,
只要把Setupsetup.sdb文件中的[Product Key],
由T2CRQGDKBVW7KJR8C6CKXMW3D修改为PYHYPWXB3BB2CCMV9DX9VDY8T,
再次安装就成Visual Studio Team System 2008 Team Suite正式版了。
Visual Studio Team System 2008正式版下载地址-Visual Studio Team System 2008 Team Suite download
Visual Studio 2008 Professional正式版下载地址
VS2008正式版下载-vs2008各版本下载地址
MSN9.0下载-Windows Live Messenger 9.0 Beta下载(免邀请)
Visual Studio 2008 Team Foundation Server的90天免费试用版本下载(官方试用版本)
Visual Studio 2008 Beta 1 Download
Visual Studio 2008 和 .NET 3.5 的新功能和改进
Visual Studio 2008下载(Visual Studio 2008 Download)
SQL Server 2008 CTP Download
ASP.NET 3.5 Extensions, Silverlight 2.0, IIS7
Windows Server 2008 Download-Windows Server 2008下载
迁移到Windows Server 2008和IIS 7.0

Visual Studio Team System 2008正式版下载

原文来自:http://www.ad0.cn/netfetch/read.php/1152.htm
转载请保留本文链接:http://www.ad0.cn/netfetch/read.php/1152.htm

[管理]我是如何带领团队开发工作流项目的(转载)

mikel阅读(482)

转载:http://www.cnblogs.com/foundation/archive/2009/10/17/1584875.html#_Toc243502607

最近有不少朋友写信问我一些关于团队开发的问题,由于这段时间有些忙,没有回复.今天写一篇这方面的文章向大家介绍一下我是如何带领团队开发工作流项目的

关于团队建设,项目管理的文章网上已经有很多了,在这里我就不谈这些理论了,直接给大家展示一个我在 项目开发方,后台服务开发方式,前台UI开发方式,后台服务与前台UI对接方式,代码文档,页面的开发文档,源码管理,单元测试,以及单元测试文档,实现 思路设计文档,数据库文档,数据库设计规范,编码规范,操做数据的方法命名规则 方面的一些片断,这是一个为期6个月的工作流平台开发项目,是今年3月份启动的,现在已完成,比计划时间多出25天.核心开发人员(不包括美工,需求,黑 盒测试)共有12人(编号从114到125)

补充一点:在UI草图设计上,这次想用绘图板,但最后还是使用铅笔绘制+扫描的方式制做的.

 

目录

项目开发方式说明图    1

后台服务开发方式说明图    1

前台UI开发方式说明图    2

后台服务与前台UI对接方式说明图    3

代码文档(片断节选)    4

页面的开发文档(片断节选)    5

源码管理    6

单元测试,以及单元测试文档(片断节选)    7

实现思路设计文档(片断节选)    9

数据库文档    11

数据库设计规范(片断节选)    13

编码规范(片断节选)    13

操做数据的方法命名规则    13

 

 

项目开发方式说明图

 

后台服务开发方式说明图

 

 

前台UI开发方式说明图

 

 

后台服务与前台UI对接方式说明图

 

 

代码文档(片断节选)

 

方法签名

public int? addBaseEnumeration(string powerID, List<baseEnumeration> list)

返回值

  • [0]:方法未完成
  • [null]:操作成功
  • [401]:参数[powerID]没通过[security.checkPowerID方法]验证
  • [-1]:参数[list][null]
  • [-2]:参数[list]中的[baseEnumeration.rowID][Guid.Empty]
  • [-3]:参数[list]中的[baseEnumeration.entity][null][string.Empty]
  • [-4]:参数[list]中的[baseEnumeration.field][null][string.Empty]
  • [-5]:参数[list]中的[baseEnumeration.entityType][null][string.Empty]
  • [-6]:参数[list]中的[baseEnumeration.title][null][string.Empty]
  • [-7]:参数[list]中的[baseEnumeration.value][null][string.Empty]
  • [-8]:参数[list]中存在[rowID]重复的记录
  • [-9]:参数[list]中存在[Entity][field][value]重复的记录
  • [3] :参数[list]中的[baseEnumeration.rowID]在数据库中已存在
  • [4] :联合唯一索引[Entity][field][value]在数据库中已存在
  • [5] :参数[entityType]的传入值不是枚举表[entitytype]的基础枚举数据
  • [6] :插入时数据库异常
  • [7] :不能插入[entity]["insertBaseEnumeration"],[field]["entitytype"]的数据

参数

  • [powerID]: 权限参数
  • [list]: 多条baseEnumeration表记录,的泛型集合

约束

<1> 调用[security.checkPowerID方法]判断[powerID],如果[security.checkPowerID方法]返回[false],返回[401]

<2> 如果参数[list][null],返回[-1]

<3> 如果参数[list]中的[baseEnumeration.rowID][Guid.Empty],返回[-2]

<4> 如果参数[list]中的[baseEnumeration.entity][null][string.Empty], 返回[-3]

<5> 如果参数[list]中的[baseEnumeration.field][null][string.Empty], 返回[-4]

<6> 如果参数[list]中的[baseEnumeration.entityType][null][string.Empty], 返回[-5]

<7> 如果参数[list]中的[baseEnumeration.title][null][string.Empty], 返回[-6]

<8> 如果参数[list]中的[baseEnumeration.value][null][string.Empty], 返回[-7]

<9> 如果参数[list]中存在[rowID]重复的记录,返回[-8]

<10>如果参数[list]中存在[entity][field][value]重复的记录,返回[-9]

<11>如果参数[list]中的[baseEnumeration.rowID]在数据库中已存在, 返回[3]

<12>如果联合唯一索引[entity][field][value]在数据库中已存在, 返回[4]

<13>如果参数[entity]["baseEnumeration"],[field][entitytype]的数据,返回[7]

<14>如果参数[entityType]的传入值不是枚举表[entitytype]的基础枚举数据, 返回[5]

<15>如果插入时数据库异常, 返回[6]

说明

[Entity][field][value] 联合唯一索引

<14>获取[Entitytype]基础枚举数据时,使用[23 selectBaseEnumerationTypeName]得到枚举类型名称

单元测试

(组合测试),(自动判断返回状态),(自动判断返回结果)

WFServiceTestProject. manageServiceTest. addBaseEnumerationTest()

 

//[26] 批量添加枚举

public int? addBaseEnumeration(string powerID, List<baseEnumeration> list)

{

//<1>

if (!security.checkPowerID(powerID))

{

return 401;

}

//<2>

if (list == null)

{

return -1;

}

using (wxwinterDBDataContext db = new wxwinterDBDataContext())

{

//<3>

if (list.Count(p => p.rowID == Guid.Empty) > 0)

{

return -2;

}

//<4>

if (list.Count(p => string.IsNullOrEmpty(p.entity)) > 0)

{

return -3;

}

//<5>

if (list.Count(p => string.IsNullOrEmpty(p.field)) > 0)

{

return -4;

}

//<6>

if (list.Count(p => string.IsNullOrEmpty(p.entityType)) > 0)

{

return -5;

}

 

………………………….

 

 

页面的开发文档(片断节选)

模块编号

Wxwinter.Index.Power.manageDutyControl

模块需要调用的其它UI模块列表

Wxwinter.Index.Power.insertDutyControl

Wxwinter.Index.Power.changeDutyControl

模块的调用入口UI

Wxwinter.Index.Power.navigationOrganizationControl

UI类型

[ V ] 中控件 700 * 500

工具栏按钮的调用路径

[ V ] 无工具栏

模块调用方式

[ V ] 模式化弹出框

action说明

不需要action

 

 

源码管理

 

 

 

 

单元测试,以及单元测试文档(片断节选)

文档

×

方法签名

public int? transactComplete(string powerID , Guid instanceID , Guid stateID , string transactResult , status status)

×

返回值

  • [0]:方法未完成
  • [null]:操作成功
  • [401]:参数[powerID]没通过[security.checkPowerID方法]验证
  • [-1]:[instanceID]为[Guid.empty]
  • [-2]:[stateID]为[Guid.empty]
  • [1]:[wfStateTransactTask]表中没有指定完成的办理任务
  • [2]:所指定的办理任务处在非等待状态
  • [3]:数据库提交失败

×

参数

  • [powerID]: 权限参数
  • [instanceID]:实例编号
  • [stateID]:状态编号
  • [transactResult]:办理结果
  • [status]:身份

×

约束

<1>调用[security.checkPowerID方法]判断[powerID],如果[security.checkPowerID方法]返回[false],返回[401]

<2>调用[checkInstanceState()]方法对[instanceID][stateID]指定的实例状态进行验证,返回值不为[null],返回[checkInstanceState()]的返回值

<3>调用[checkStatus()]方法对[status]进行验证,返回值不为[null],返回[checkStatus()]的返回值

<4>得到[wfStateTransactTask]表中

[

wfStateTransactTask.instanceID = instanceID

&& wfStateTransactTask.stateID = stateID

&& wfStateTransactTask.departmentNo = status.departmentNo

&& wfStateTransactTask.dutyNo = status.dutyNo

&& wfStateTransactTask.personNo = status.personNo

]

的记录并赋给变量[taskInfo],如果不存在,返回[1]

<5>如果[taskInfo.runState != runState.wait],返回[2]

<6>修改

[

taskInfo.runState=runState.end

taskInfo.completeTime=System.DateTime.Now

taskInfo.transactResult=transactResult

]

用[taskInfo]修改[wfStateTransactTask]表中记录

<7>向[wfStepList]中插入数据

[

flowID = taskInfo.flowID

flowName = taskInfo.flowName

nodeID = taskInfo.nodeID

nodeName = taskInfo.nodeName

departmentNo = status.departmentNo

departmentName = status.departmentName

dutyNo = status.dutyNo

dutyName = status.dutyName

personNo = status.personNo

personName = status.personName

instanceID = taskInfo.instanceID

stateID = taskInfo.stateID

processID = taskInfo.processID

processName = taskInfo.processName

stepAction = stepAction.办理

stepTime = System.DateTime.Now

taskID = stepAction.办理

]

<8>如果数据库提交失败,返回[3],成功,返回[null]

×

说明

调用[checkInstanceState()]方法对实例状态进行验证

×

单元测试

 

 

单元测试选项

范围

判断

影响

(无)

 

(不需要)

 

(全路径)

 

(正常路径)

 

(简单调用)

 

(组合测试)

 

(已在外部调试通过)

(自动判断返回状态)

 

(自动判断返回结果)

 

(人工判断返回结果,结果控制台输出)

 

(人工判断返回结果,结果存入磁盘)

 

(自动判断操作结果)

 

(人工判断操作结果,结果存入数据库)

 

(人工判断操作结果,结果存入数据库)

 

(不出异常即可)

 

(说明…)

(操作数据库,完成测试后已复原)

 

(操作数据库,已做state标记)

 

(操作数据库,需要手工复原)

 

(操作临时数据库)

 

 

实现思路设计文档(片断节选)

例1:工作流结构的解析

 

 

 

例2:删除部门职能人员的约束

 

 

例3:查询用户的模块权限

public List<viewRelationModel> searchModelPowerOfPerson(string powerID,string personNo)

 

searchModelPowerOfPerson("","user1")

step1

使用 searchStatusList("", "user1") 得到[得到身份列表]

 

departmentNo 

dutyNo 

personNo 

user1 

user1 

user1 

step2

用得到的身份与[powerRelationModel]对比,并返回如下算法的集合

 

departmentNo 

dutyNo 

modelNo 

action 

scope 

all 

m1 

see 

 

B 

all 

m2 

see 

 

all 

all 

m3 

see 

 

m4 

see 

 

 

 

 

数据库文档

wfFlow 流程表

表说明:存储流程模板的属性信息,该表内容是将xoml存入时,解析xoml后一次性生成的,不能修改

flowID

流程编号

f1

来自iFlow

flowName

流程名称

f2

flowType

流程类型

f3

flowDescription

流程说明

f4

businessType

业务类型

f5

startWindow

启动窗体

f6

dataFormList

表单列表

f7

startDataForm

启动时填写的表单

f8

inputFormItems

传入表单

f9

inputFormItemsExpandData

传入表单扩展数据

f10

commandOption

命令选项

f11

commandOptionExpandData

命令选项扩展数据

f12

ownedType

流程归属类型

f13

timelimitUnit

时限单位

l1

来自iTimelimit

timelimit

时限

l2

overtimeOperate

超时操作

l3

calendar 

日历

l4

residualTimelimit

剩余时限

l5

createDepartmentNo

创建部门编号

创建该流程模板的人员所在部门的编号

createDepartmentName

创建部门名称

 

createDutyNo

创建职能编号

创建该流程模板的人员所担任的职能的编号

createDutyName

创建职能名称

 

createPersonNo

创建人员编号

创建该流程模板的人员的编号

createPersonName

创建人员名称

 

createTime

创建时间

 

isCheckout

是否签出

如果为真,签出人可以对流程模板进行修改,非签出人不能再将该模板签出进行修改,如果为假,就可以将该流程签出进行修改

checkoutPersonNo

签出人员编号

 

checkoutPersonName

签出人员名称

 

checkoutTime

签出时间

 

checkinTime

签入时间

 

isFreeze

是否冻结

如果为真,流程模板处于冻结状态,不能被启动

如果为假,流程模板可以正常启动

 

wfFlow 流程表 表结构

编号

字段

中文对照

外创

模创

外改

数据类型

主键

非空

唯一

外键约束/字段值选项

 

rowID

行编号

     

uniqueidentifier 

 
 

state

状态

     

int

       

f1

flowID

流程编号

 

 

nvarchar(50) 

 

 

f2

flowName

流程名称

 

 

nvarchar(50) 

 

   

f3

flowType

流程类型

 

 

nvarchar(50) 

 

 

option.flowType

f4

flowDescription

流程说明

 

 

nvarchar(MAX)

 

   

f5

businessType

业务类型

 

 

nvarchar(50) 

 

 

wfBusinessType.businessType

f6

startWindow

启动窗体

 

 

nvarchar(255)

 

   

f7

dataFormList

表单列表

 

 

nvarchar(MAX)

 

   

f8

startDataForm

启动时填写的表单

 

 

nvarchar(MAX)

 

   

f9

inputFormItems

传入表单

 

 

nvarchar(MAX)

 

   

f10

inputFormItemsExpandData

传入表单扩展数据

 

 

nvarchar(MAX)

 

   

f11

commandOption

命令选项

 

 

nvarchar(255)

 

   

f12

commandOptionExpandData

命令选项扩展数据

 

 

nvarchar(MAX)

 

   

f13

ownedType

流程归属类型

 

 

nvarchar(50) 

 

 

option.ownedType

l1

timelimitUnit

时限单位

 

 

nvarchar(50) 

 

 

option.timelimitUnit

l2

timelimit

时限

 

 

int

 

   

l3

overtimeOperate

超时操作

 

 

nvarchar(50)

 

   

l4

calendar 

日历

 

 

nvarchar(50) 

 

   

I5

residualTimelimit

剩余时限

 

 

int

 

   
 

createDepartmentNo

创建部门编号

   

nvarchar(50) 

 

   
 

createDepartmentName

创建部门名称

   

nvarchar(50) 

 

   
 

createDutyNo

创建职能编号

   

nvarchar(50) 

 

   
 

createDutyName

创建职能名称

   

nvarchar(50) 

 

   
 

createPersonNo

创建人员编号

   

nvarchar(50) 

 

   
 

createPersonName

创建人员名称

   

nvarchar(50) 

 

   
 

createTime

创建时间

   

datetime 

 

   
 

isCheckout

是否签出

 

int

 

   
 

checkoutPersonNo

签出人员编号

 

nvarchar(50) 

       
 

checkoutPersonName

签出人员名称

 

nvarchar(50) 

       
 

checkoutTime

签出时间

 

datetime 

       
 

checkinTime

签入时间

 

datetime 

       
 

isFreeze 

是否冻结

 

int

 

   

 

数据库设计规范(片断节选)

SQL Server 类型

C# 类型

说明

nvarchar(50)

string

  • 字符类的[唯一索引]
  • 标识类信息,如用户名,口令,性别
  • 标签类信息
  • 标记类信息
  • 状态标识 :如:[成功],[失败],[运行中]

对应的UI为单行行文本框,标签,提示文字

nvarchar(255)

string

  • 小量的描述信息,两行以上
  • 磁盘文件的路径信息

对应的UI为垂直滚动条的多行文本框,或多段的标签

nvarchar(MAX)

string

  • 文章
  • 二进制串行化信息
  • XML数据
  • 大字符数据

对应的UI为双滚动条的多行文本框

 

编码规范(片断节选)

操做数据的方法命名规则

前缀

含义

传入

返回

get

得到单条记录

rowID

单条记录对象

唯一键

search

查询多条记录

唯一键

List<记录对象>

非唯一键

多个参数, [and/or]关系组合

select

查询多条记录

唯一键,[字段名]

List<string>

字段名所对应的[表.字段.ToString()]

非唯一键,[字段名]

多个参数, [and/or]关系组合,[字段名]

change

修改单条

记录对像

执行状态

stringResult.state 执行状态

stringResult.value 出问题的字段

set

修改单条

唯一键,[字段名],[值]

执行状态

rowID,[字段名],[值]

update

修改多条

(事物)

List<记录对象>

执行状态

modify

修改多条

(无事物)

List<记录对象>

无法完成修改的List<记录对象>

insert

添加单条

记录对象

执行状态

add

批量添加

(事物)

List<记录对象>

执行状态

stringResult.state 执行状态

stringResult.value 出问题的字段

append

批量添加

(无事物)

List<记录对象>

无法完成添加的List<记录对象>

remove

删除单条

rowID

执行状态

唯一键

delete

删除多条

(事物)

List<记录对象>

执行状态

clear

删除多条

(事物)

多个参数, [and/or]关系组合

执行状态

wipe

删除多条

(无事物)

List<记录对象>

无法完成删除的List<记录对象>

execute

执行SQL

SQL 字串

 

存储过程

执行状态

stringResult.state 执行状态

stringResult.value 问题

stringResult.state 执行状态

stringResult.value 返回对象的XML

binding

添加单条(关系表)

记录对象

执行状态

unbinding

删除单条(关系表)

rowID

执行状态

[ASP.NET] SWFUpload+Javascript仿163邮件上传文件

mikel阅读(459)

转载:http://www.cnblogs.com/land/archive/2009/10/16/swfupload.html
  时下上传文件一般有三种方式:
    1、使用隐藏的FRAME来模拟AJAX的方式上传文件
    2、使用ACTIVEX上传文件
    3、使用Flash控件+JavaScript上传文件(严格的来说FLASH也算是ACTIVEX)
    一般使用B/S结构上传大文件的应用一般在局域网中使用,在INTERNET中应用的效果还不是很理想。使用INTERNET上传文件一般也就是10-20M左右。
    SWFUpload是老外开发的一个FLASH上传控件,其主页地址是http://www.swfupload.org/,目前最新版本是V2.2.0.1,下载地址是http://code.google.com/p/swfupload/downloads/list,中文文档在这:http://www.v-sky.com/doc/swfupload/v2.1.0/Documentation.html
     其具体使用请大家到官方网站下载吧,DEMO地址:http://demo.swfupload.org/
     页面效果如下图:


    目前上传单个文件的进度条的位置有点偏上,CSS我没调整好,希望各位在调整好进度条显示后把CSS给我发一份!
源码下载

修正

针对网友“Aleax”提出的问题[上传第一个文件后再上传一个文件,上传总进度统计错了],修正如下:
(1)、在demoupload.aspx页面的声明加"queue_complete_handler:queueComplete"
(2)、在handler.js中的queueComplete如下:
function queueComplete(numFilesUploaded)
{
    fg_fileSizes = 0;
    fg_uploads = 0;  
}