ASP.NET一些小问题

1 关于IFramset 分块的问题

今天搞了半天就为了一个框架的设计。

以前还做过了的,过了几个月没有碰忘记了,今天想了半天还没搞好,最后干脆装了DreamWaveMX。— —! 

不过还好复习了一遍还是弄清楚了,记录下来,以免以后又忘记了。

在framset中:

rows = " * "   :   通过行分快,也就是从上到下分快,*代表整个

rows = " 100 , 100, * "    : 意思是从上到下分为3快,第一快和第二快是100px,第3快占其他所有

同样的还有个cols属性,是通过列分快,也就是从左到右分快

cols = " 100,*  "  : 意思是从左到右,第一块100px,第二块占其他所有

 

Q:您在什么情况下会用到虚方法?它与接口和抽象有什么不同?接口与抽象又有什么不同?举例说明什么时候使用接口,什么时候使用抽象类

A

先谈Virtual Method

 

若一个实例方法的声明中含有 virtual 修饰符,则称该方法为虚方法 (virtual method)。若其中没有 virtual 修饰符,则称该方法为非虚方法 (non-virtual method)
在调用一个虚方法时,该调用所涉及的那个实例的运行时类型 (runtime type) 确定了要被调用的究竟是该方法的哪一个实现。在非虚方法调用中,实例的编译时类型 (compile-time type) 是决定性因素。
虚方法可以在派生类中重写 (override)。当某个实例方法声明包括 override 修饰符时,该方法将重写所继承的具有相同签名的虚方法。虚方法声明用于引入新方法,而重写方法声明则用于使现有的继承虚方法专用化(通过提供该方法的新实现)。
抽象 (abstract) 方法是没有实现的虚方法。抽象方法使用 abstract 修饰符进行声明,并且只有在同样被声明为 abstract 的类中才允许出现。抽象方法必须在每个非抽象派生类中重写。

而接口是一个行为的规范或规定; 仅仅承诺了能够调用的方法

 

再来谈谈接口与抽象类

1.抽象类是一个不完全的类,需要进一步专业化.接口只是一个行为的规范或规定;
2.
接口基本上不具备继承的任何具体特点,它仅仅承诺了能够调用的方法;
3.
一个类一次可以实现若干个接口,但是只能扩展一个父类
4.
接口可以用于支持回调,而继承并不具备这个特点.


 


Q

1、重载:多个同名函数同时存在,具有不同的参数个数/类型。
        无法以 返回型别 作为重载函数的区分标准。
        当编译器可以很明确从程序前后状态判断出意义时没问题,如int x=f();但因为我们也可以调用一下函数而不赋给其他变量(只为了获得“副作用”)如f(),这时候编译器便区分不出来该调用哪个。

2、重写:父类与子类之间的多态性。子类中某方法与父类有相同的名称和参数。

 

 


Q

数据在内存中的存储位置,取决于它的数据类型,在C#中,分为值类型和引用类型,值类型的数据存储在内存中的堆栈中,每个变量或程序都有自己的堆栈,不可以共用一个堆栈地址。当数据一个值类型的变量传递到另一个相同类型的变量时,会在堆栈中分配两个不同的地址。

而引用类型的数据存储在内存中的堆中,可以不同的变量或程序共同使用同一个位置的数据。当数据从一个引用类型的变量传递到另一个相同类型的变量时,只是把这个变量的引用地址传递给新的变量,同时引用当前堆中存储的数据。

 

比如:类(class),string 属于引用类型,结构(struct)int属于值类型。

 

 


Q

A

静态变量分为全局静态变量和局部静态变量。

全局静态变量,与全局变量有区别,虽然同为静态存储方式,但是全局静态变量失去了全局的“普遍含义”,它所指的“全局”仅限制在本文件里,而全局变量却是各个文件可见的

静态局部变量,与局部变量是有区别的:

1.         存储方式不同,前者为静态存储方式,后者为动态存储方式;

2.         作用域是一致的,只局限于“模块”或者“代码段”;

静态局部变量的最大特点就是作用类似于全局变量,而作用域类似于局部变量,在出了某个函数或者代码段后,生命周期继续延续,与应用程序共生死,当再次回到这个函数或者代码段时,上次走出时的值仍然保存到了现在,所以一般人用它来做计数器。

 

另外,还有一些需要说明的,静态变量作为类的成员变量时,或者类中有静态成员函数时,这个时候,静态的部分(变量和函数)已经不再属于对象了,而仅仅属于类本身了,类没有实例化,那么静态函数和静态变量同样可以调用;

所以经常在构造函数里对一些静态成员变量递增,来实现对象个数的计数。

例:

class StaticAndInstance
{
 static int classVar;
 int instanceVar;
 static void setClassVar(int i)
 {
  classVar=i;
 }
 static int getClassVar()
 {
  return classVar;
 }
 void setInstanceVar(int i)
 {
  classVar=i;
  instanceVar=i;
 }
 int getInstanceVar()
 {
  return instanceVar;
 }
}
public class StaticAndInstanceTest
{
 public static void main(String args[])
 {
  StaticAndInstance m1=new StaticAndInstance();
  StaticAndInstance m2=new StaticAndInstance();
  m1.setClassVar(1);
  m2.setClassVar(2);
  System.out.println("m1.classVar="+m1.getClassVar()+"m2.classVar="+m2.getClassVar());
  m1.setInstanceVar(11);
  m2.setInstanceVar(22);
  System.out.println("m1.InstanceVar="+m1.getInstanceVar()+"m2.InstanceVar="+m2.getInstanceVar());
 }
}

结果为:m1.classVar=2  m2.classVar=2

m1.InstanceVar=11 m2.InstanceVar=22

 


 
Q

 

 

 


Q

A

通过url传递参数

 


Q

A:

There are generally four of them:

·         Data Store: Where the data resides. This can be a relational database, an XML file, a text file, or some other proprietary storage system.

·         Data Access Layer: The code that takes care of retrieving and manipulating the raw data saved in the data store.

·         Business Logic Layer: The code that takes the data retrieved by the Data Access tier and exposes it to the client in a more abstracted and intuitive way, hiding low-level details such as the data store's schema, and adding all the validation logic that ensures that the input is safe and consistent.

·         Presentation Layer (User Interface): The code that defines what a user should see on the screen, including formatted data and system navigation menus. This layer will be designed to operate inside a web browser in the case of ASP.NET, but some applications might use Windows Forms at this layer.


 

Q

A

好象可以直接传递?

HyperLink1.NavigateUrl = "Forum.aspx?a=" + "中文";

也可以这样解码传递

string urlFmt = "Forum.aspx?a={0}";

HyperLink1.NavigateUrl = string.Format(urlFmt, Server.UrlEncode("测试中文"));

 

Q :Response.Flush()

Flush 方法立即发送缓冲区中的输出。如果未将 Response.Buffer 设置为 TRUE,则该方法将导致运行时错误。

语法

Response.Flush

 

注释

如果在 ASP 页上调用 Flush 方法,则服务器将响应该页上保持活动的请求。

Response.Flush

对于响应缓冲,因为用户在看到东西之前必须等待整个页面生成,所以用户可能够感觉到ASP页面响应比较慢(虽然整体响应时间缩短了);对一个运行时间较长的页面,可以同过Response.Buffer = False 来关掉响应缓冲;但更好的策略是使用
Reponse.Flush方法。这个方法把所有已经由ASP生成的HTML输出到浏览器中。例
如,一个1,000行的大表,在写完100行之后,ASP可以调用Response.Flush来强制把结果写到浏览器中,这样,用户就可以在其余行生成之前先看到100行数据。这个技术能让你两全其美—响应缓冲和渐进式地在浏览器表现数据。

(注意,在上面的1,000行表的例子中,很多浏览器在遇到</table>标记之前可能并不画出整个表。如果想让浏览器逐步显示出数据,可以将一个大表分成多个小表,然后对每个小表调用Response.Flush。新版本的IE会在下载完整个表之前显示表,并且如果指定了表的列宽,显示的速度会更快。)

另外,当产生一个非常大的页面时,响应缓冲可能会消耗掉许多的服务器内存。这个问题也可以通过使用Response.Flush来解决。

Flush的内容至少要有256字节
  很多时候我们写的asp程序会因为做很多操作,所以会花上一分钟甚至几分钟时间。为了使软件使用者能够耐心的等待程序的执行,我们经常会希望有一个进度条来表示程序执行的状态。或者最起码要显示一个类似: “数据载入中”,“正在保存数据” 等的说明性文字。此时我们就会用到Response.flush()。他会将缓冲区中编译完成的数据先发送到客户端。
  但是有很多时候,我们发现即使我们使用了Response.Flush(),但是并没有将前面的信息发到客户端来显示。呈献给我们的依然是白屏。经过反复的测试,我得出一个结论(仅代表个人观点,可随意引用,但后果自负)。就是flush的内容至少要有256字节。也就是只有编译产生了至少256字节的数据,才能在执行Response.Flush()以后将信息发到客户端并显示。

 

 

轻松加密ASP.NET 2.0 Web程序配置信息

一、 简介

  当创建ASP.NET 2.0应用程序时,开发者通常都把敏感的配置信息存储在Web.config文件中。最典型的示例就是数据库连接字符串,但是包括在Web.config文件中的其它敏感信息还包括SMTP服务器连接信息和用户凭证数据,等等。 尽管默认情况下可以配置ASP.NET以拒绝所有对扩展名为.config的文件资源的HTTP请求;但是,如果一个黑客能够存取你的web服务器的文件系统的话,那么,Web.config中的敏感信息仍然能够被窃取。例如,也许你不小心允许匿名FTP存取你的网站,这样以来就允许一个黑客简单地通过FTP协议下载你的Web.config文件。

  幸好,通过允许加密Web.config文件中选择的部分,例如<connectionStrings>节,或你的应用程序使用的一些定制config节,ASP.NET 2.0有助于缓解这个问题。配置部分能够很容易地使用编码或aspnet_regiis.exe(一个命令行程序)预以加密。一旦被加密,Web.config设置即可避开"虎视眈眈"的眼睛。而且,当以编程方式从你的ASP.NET页面中检索加密的配置设置时,ASP.NET会自动地解密它读取的加密部分。简言之,一旦配置信息被加密,你就不需要在你的应用程序中编写任何其它代码或采取任何进一步的行为来使用该加密数据。

  在本文中,我们将讨论如何以编程方式加密和解密该配置设置部分,并且分析一下命令行程序aspnet_regiis.exe的使用。然后,我们将评估ASP.NET 2.0提供的加密选项。另外,还会简短地讨论一下如何加密ASP.NET版本1.x中的配置信息。

  二、 前提

  在我们开始探讨如何加密ASP.NET 2.0配置信息之前,请记住下列几点:

  1. 所有形式的加密都会包含某种秘密,而当加密和解密数据时都要使用这一秘密。对称加密算法在加密和解密一个消息时使用同一把密钥,而非对称加密算法对于加密和解密却使用不同的密钥。无论使用哪种技术,最重要的还是看解密密钥的安全保存程度。

  2. ASP.NET 2.0提供的配置加密技术的设计目的在于,力图阻止能够以某种方式检索你的配置文件的黑客的入侵。其实现思想是,如果在黑客的计算机上有你的Web.config文件;那么,他不能破解该加密的部分。然而,当web服务器上的一个ASP.NET页面从一个加密的配置文件请求信息时,该数据必须被解密才能使用(并且这时不需要你编写任何代码)。因此,如果一个黑客能够把一个能够查询配置文件并显示它的结果的ASP.NET web页面上传到你的系统,那么,他就能够以普通文本方式观看被加密的设置。(详细情况请参考本文提供的示例ASP.NET页面,它展示了加密和解密Web.config文件中各部分的方法;如你所见,一个ASP.NET页面能够存取(并显示)该加密数据的普通文本形式)

  3. 加密和解密配置信息需要付出一定的性能代价。因此,通常是仅加密包含敏感信息的配置部分。比如说,可能不需要加密<compilation>或<authorization>配置部分。

  三、 加密何种信息

  在我们分析如何加密ASP.NET 2.0配置信息前,让我们首先来看一下能够加密什么配置信息。使用.NET框架2.0提供的库,开发人员能够加密在Web.configmachine.config文件中的绝大多数的配置部分。这些配置部分是一些作为<configuration>或<system.web>元素子结点的XML元素。例如,下面的示例Web.config文件中含有三个配置设置,显式地定义为:

connectionStrings>,<compilation>和<authentication>。
?xml version="1.0"?
configuration xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0";
connectionStrings
 <add name="MembershipConnectionString" connectionString="connectionString"/
/connectionStrings
system.web
 <compilation debug="true"/
 <authentication mode="Forms" /
/system.web


  这些节中的每一个都可以有选择地被加密,或者通过编程方式或通过aspnet_regiis.exe(一个命令行工具)实现。当被加密时,加密后的文本直接存储在配置文件中。例如,如果我们要加密上面的<connectionStrings>节,那么结果Web.config文件可能看起来如下所示:(注意:篇幅所限,我们省略了一大块<CipherValue)

?xml version="1.0"?
configuration xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0";
connectionStrings configProtectionProvider="DataProtectionConfigurationProvider"
EncryptedData
 <CipherData
  <CipherValueAQAAANCMnd8BFdERjHoAwE/Cl+sBAAAAed...GicAlQ==/CipherValue
 </CipherData
/EncryptedData
/connectionStrings
system.web
 <compilation debug="true"/
 <authentication mode="Forms" /
/system.web


  另外,存在一些你不能使用这个技术加密的配置部分:

  · processModel
  · runtime
  · mscorlib
  · startup
  · system.runtime.remoting
  · configProtectedData
  · satelliteassemblies
  · cryptographySettings
  · cryptoNameMapping
  · cryptoClasses

  为了加密这些配置部分,你必须加密这些值并把它存储在注册表中。存在一个aspnet_setreg.exe命令行工具可以帮助你实现这一过程;我们将在本文后面讨论这个工具。

  【提示】Web.ConfigMachine.Config之区别:

  Web.config文件指定针对一个特定的web应用程序的配置设置,并且位于应用程序的根目录下;而machine.config文件指定所有的位于该web服务器上的站点的配置设置,并且位于$WINDOWSDIR$/Microsoft.Net/Framework/Version/CONFIG目录下。

四、加密选项

  开发人员可以使用ASP.NET 2.0提供程序模型来保护配置节信息,这允许任何实现都可以被无缝地插入到该API中。.NET框架2.0中提供了两个内置的提供程序用于保护配置节信息:



  · Windows数据保护API(DPAPI)提供程序(DataProtectionConfigurationProvider):这个提供程序使用Windows内置的密码学技术来加解密配置节。默认情况下,这个提供程序使用本机的密钥。你还能够使用用户密钥,但是这要求进行一点定制。

  · RSA保护的配置提供程序(RSAProtectedConfigurationProvider):使用RSA公钥加密来加解密配置节。使用这个提供程序,你需要创建存储用于加解密配置信息的公钥和私钥的密钥容器。你能够在一个多服务器场所下使用RSA,这只要创建可输出的密钥容器即可。
当然,如果需要的话,你还能够创建自己的保护设置提供程序。

  在本文中,我们仅讨论使用DPAPI提供程序使用机器级密钥。到目前为止,这是最简单的方法,因为它不请求创建任何密钥或密钥容器。当然,其消极的一面在于:一个加密的配置文件仅能够用于首先实现加密的web服务器上;而且,使用机器密钥将允许加密的文本能够被web服务器上的任何网站所解密。

  五、以编程方式加密配置部分

  System.Configuration.SectionInformation类对一个配置节的描述进行了抽象。为了加密一个配置节,只需要简单地使用SectionInformation类的ProtectSection(提供程序)方法,传递你想使用的提供程序的名字来执行加密。为了存取你的应用程序的Web.config文件中的一个特定的配置节,你可以使用WebConfigurationManager(System.Web.Configuration命名空间中)来引用你的Web.config文件,然后使用它的GetSection(sectionName)方法返回一个ConfigurationSection实例。最后,你可以经由ConfigurationSection实例的SectionInformation属性得到一个SectionInformation对象。

  下面,我们通过一个简单的代码示例来说明问题:


privatevoid ProtectSection(string sectionName string provider)
{
 Configuration config = WebConfigurationManager.
 OpenWebConfiguration(Request.ApplicationPath);
 ConfigurationSection section = config.GetSection(sectionName);
 if (section != null &&!section.SectionInformation.IsProtected)
 {
  section.SectionInformation.ProtectSection(provider);
  config.Save();
 }
}
private void UnProtectSection(string sectionName) {
 Configuration config =WebConfigurationManager.OpenWebConfiguration(Request.ApplicationPath);
 ConfigurationSection section = config.GetSectio n(sectionName);
 if (section != null && section.SectionInformation.IsProtected)
 {
  section.SectionInformation.UnprotectSection();
  config.Save();
 }




  你可以从一个ASP.NET页面中调用这个ProtectSection(sectionNameprovider)方法,其相应的参数是一个节名(connectionStrings)和一个提供程序(DataProtectionConfigurationProvider),并且它打开Web.config文件,引用该节,调用SectionInformation对象的ProtectSection(provider)方法,最后保存配置变化。

  另一方面,UnProtectSection(provider)方法实现解密一个特定的配置节。在此,仅需要传入要解密的节-我们不需要麻烦提供程序,因为该信息已经存储在伴随encrypted节的标记中(也即是,在上面的示例中的<connectionStrings>节,在被加密以后,它包含了提供程序:<connectionStringsconfigProtectionProvider="DataProtectionConfigurationProvider")

  记住,一旦该数据被加密,当从一个ASP.NET页面读取它时(也即是,从一个SqlDataSource控件或以编程方式经由ConfigurationManager.ConnectionStrings[connStringName].ConnectionString读取该连接字符串信息)ASP.NET会自动地解密该连接字符串并且返回普通文本值。换句话说,在实现加密后,你一点不需要改变你的代码。相当酷,对不对?

  从本文下载的示例ASP.NET 2.0网站中,你会发现有一个示例页面,它展示了该站点的Web.config文件,其中有一个多行TextBox,还提供了相应的Web控件按钮用于加密配置文件的各个部分。这个示例中也使用了上面已经讨论过的ProtectSection()UnProtectSection()方法。

六、 使用命令行工具aspnet_regiis.exe

  你还能够使用aspnet_regiis.exe命令行工具来加密和解密Web.config文件配置部分,你可以在"%WINDOWSDIR%/Microsoft.Net/Framework/version"目录下找到这个工具。为了加密Web.config文件中的一个节,你可以在这个命令行工具中使用DPAPI机器密钥,如下所示:

  加密一个特定网站的Web.config文件的通用形式:


aspnet_regiis.exe -pef section physical_directory -prov provider




  或:


aspnet_regiis.exe -pe section -app virtual_directory -prov provider




  加密一个特定网站的Web.config文件的具体实例:


aspnet_regiis.exe -pef "connectionStrings" "C:/Inetpub/wwwroot/MySite" -prov "DataProtectionConfigurationProvider"




  或:


aspnet_regiis.exe -pe "connectionStrings" -app "/MySite" -prov "DataProtectionConfigurationProvider"




  解密一个特定网站的Web.config文件的通用形式:


aspnet_regiis.exe -pdf section physical_directory




  或:


aspnet_regiis.exe -pd section -app virtual_directory




  解密一个特定网站的Web.config文件的具体实例:


aspnet_regiis.exe -pdf "connectionStrings" "C:/Inetpub/wwwroot/MySite"




  或:

  你还能够指定由aspnet_regiis.exe来执行machine.config文件的加密/解密。

  【提示】 加密ASP.NET版本1.x中的配置设置

  为了保护ASP.NET版本1.x中的配置设置,开发者需要加密并把敏感的设置存储在web服务器的注册表中,并以一种""键方式存储。配置文件中不是存储加密的内容(如ASP.NET 2.0那样),而只是包含一个到存储该加密值的注册表键的引用。例如:


identity impersonate="true"
userName="registry:HKLM/SOFTWARE/MY_SECURE_APP/identity/ASPNET_SETREG
userName"
password="registry:HKLM/SOFTWARE/MY_SECURE_APP/identity/ASPNET_SETREG
password" /




  微软为开发人员提供了aspnet_setreg.exe命令行工具,用于加密敏感的配置信息并且把它移动到一个""注册表入口处。遗憾的是,这个工具仅针对特定的配置设置工作;相比之下,ASP.NET 2.0允许加密任何配置节。

  有关于在一个ASP.NET 1.x应用程序中使用aspnet_setreg.exe的更多信息请参考MSDN中的KB#32990。遗憾的是,这个命令行程序仅能加密配置设置中的预定义的节,并且不允许你加密你自己添加的数据库连接字符串和其它敏感信息。

  七、 结论

  在本文中,我们学习了如何使用ASP.NET 2.0提供的不同的加密选项来保护配置节信息,还讨论了如何使用编程技术和aspnet_regiis.exe来分别加密Web.config中的配置节。保护你的敏感的配置设置有助于确保你的站点更难于被黑客攻击-通过使其更难于发现敏感的配置设置。如今,ASP.NET 2.0已经提供了相对容易的加密和解密技术,开发者毫无理由不使用这种方式来保护你的敏感的配置设置。

 

 

Gridview中的内容导出到Excel

protected void Button1_Click(object sender, System.EventArgs e)

{

 

Response.Clear();

Response.AddHeader("content-disposition", "attachment;filename=FileName.xls");

Response.Charset = "gb2312";

Response.ContentType = "application/vnd.xls";

System.IO.StringWriter stringWrite = new System.IO.StringWriter();

System.Web.UI.HtmlTextWriter htmlWrite = new HtmlTextWriter(stringWrite);

 

GridView1.AllowPaging = false;

iniData();

GridView1.RenderControl(htmlWrite);

 

Response.Write(stringWrite.ToString());

Response.Flush();

Response.End();

GridView1.AllowPaging = true;

iniData();

 

 

}

 

 

public override void VerifyRenderingInServerForm(System.Web.UI.Control control)

{

//Confirms that an HtmlForm control is rendered for

}

 

在上面的代码中,我们首先将gridview绑定到指定的数据源中,然后在button1的按钮(用来做导出到EXCEL的)的事件中,写入相关的代码。这里使用Response.AddHeader("content-disposition","attachment;filename=exporttoexcel.xls");中的filename来指定将要导出的excel的文件名,这里是exporttoexcel.xls。要注意的是,由于gridview的内容可能是分页显示的,因此,这里在每次导出excel时,先将gridviewallowpaging属性设置为false,然后通过页面流的方式导出当前页的gridviewexcel中,最后再重新设置其allowpaging属性。另外要注意的是,要写一个空的VerifyRenderingInServerForm方法(必须写),以确认在运行时为指定的ASP.NET 服务器控件呈现HtmlForm 控件。

 

 

编写高性能Web应用程序的10个入门技巧

使用 ASP.NET 编写 Web 应用程序的简单程度令人不敢相信。正因为如此简单,所以很多开发人员就不会花时间来设计其应用程序的结构,以获得更好的性能了。在本文中,我将讲述 10 个用于编写高性能 Web 应用程序的技巧。但是我并不会将这些建议仅局限于ASP.NET 应用程序,因为这些应用程序只是 Web 应用程序的一部分。本文不作为对 Web应用程序进行性能调整的权威性指南一整本书恐怕都无法轻松讲清楚这个问题。请将本文视作一个很好的起点。

  成为工作狂之前,我原来喜欢攀岩。在进行任何大型攀岩活动之前,我都会首先仔细查看指南中的路线,阅读以前游客提出的建议。但是,无论指南怎么好,您都需要真正的攀岩体验,然后才能尝试一个特别具有挑战性的攀登。与之相似,当您面临修复性能问题或者运行一个高吞吐量站点的问题时,您只能学习如何编写高性能 Web 应用程序。

  我的个人体验来自在 Microsoft ASP.NET 部门作为基础架构程序经理的经验,在此期间我运行和管理 www.ASP.NET,帮助设计社区服务器的结构,社区服务器是几个著名
ASP.NET
应用程序(组合到一个平台的 ASP.NET Forums.Text nGallery)。我确信有些曾经帮助过我的技巧对您肯定也会有所帮助。

  您应该考虑将应用程序分为几个逻辑层。您可能听说过 3 层(或者 n 层)物理体系结构一词。这些通常都是规定好的体系结构方式,将功能在进程和/或硬件之间进行了物理分离。当系统需要扩大时,可以很轻松地添加更多的硬件。但是会出现一个与进程和机器跳跃相关的性能下降,因此应该避免。所以,如果可能的话,请尽量在同一个应用程序中一起运行 ASP.NET 页及其相关组件。

  因为代码分离以及层之间的边界,所以使用 Web 服务或远程处理将会使得性能下降 20% 甚至更多。

  数据层有点与众不同,因为通常情况下,最好具有专用于数据库的硬件。然而进程跳跃到数据库的成本依然很高,因此数据层的性能是您在优化代码时首先要考虑的问题。

  在深入应用程序的性能修复问题之前,请首先确保对应用程序进行剖析,以便找出具体的问题所在。主要性能计数器(如表示执行垃圾回收所需时间百分比的计数器)对于找出应用程序在哪些位置花费了其主要时间也非常有用。然而花费时间的位置通常非常不直观。

  本文讲述了两种类型的性能改善:大型优化(如使用 ASP.NET 缓存),和进行自身重复的小型优化。这些小型优化有时特别有意思。您对代码进行一点小小的更改,就会获得很多很多时间。使用大型优化,您可能会看到整体性能的较大飞跃。而使用小型优化时,对于某个特定请求可能只会节省几毫秒的时间,但是每天所有请求加起来,则可能会产生巨大的改善。

  数据层性能

  谈到应用程序的性能调整,有一个试纸性的测试可用来对工作进行优先级划分:代码是否访问数据库?如果是,频率是怎样的?请注意,这一相同测试也可应用于使用 Web 服务或远程处理的代码,但是本文对这些内容未做讲述。

  如果某个特定的代码路径中必需进行数据库请求,并且您认为要首先优化其他领域(如字符串操作),则请停止,然后执行这个试纸性测试。如果您的性能问题不是非常严重的话,最好花一些时间来优化一下与数据库、返回的数据量、进出数据库的往返频率相关的花费时间。

  了解这些常规信息之后,我们来看一下可能会有助于提高应用程序性能的十个技巧。首先,我要讲述可能会引起最大改观的更改。

  技巧 1 — 返回多个结果集

  仔细查看您的数据库代码,看是否存在多次进入数据库的请求路径。每个这样的往返都会降低应用程序可以提供的每秒请求数量。通过在一个数据库请求中返回多个结果集,可以节省与数据库进行通信所需的总时间长度。同时因为减少了数据库服务器管理请求的工作,还会使得系统伸缩性更强。

  虽然可以使用动态 SQL 返回多个结果集,但是我首选使用存储过程。关于业务逻辑是否应该驻留于存储过程的问题还存在一些争议,但是我认为,如果存储过程中的逻辑可以约束返回数据的话(缩小数据集的大小、缩短网络上所花费时间,不必筛选逻辑层的数据),则应赞成这样做。

  使用 SqlCommand 实例及其 ExecuteReader 方法填充强类型的业务类时,可以通过调用NextResult 将结果集指针向前移动。图 1 显示了使用类型类填充几个 ArrayList 的示例会话。只从数据库返回您需要的数据将进一步减少服务器上的内存分配。

Figure 1 Extracting Multiple Resultsets from a DataReader
// read the first resultset
reader = command.ExecuteReader();

// read the data from that resultset
while (reader.Read()) {
suppliers.Add(PopulateSupplierFromIDataReader( reader ));
}

// read the next resultset
reader.NextResult();

// read the data from that second resultset
while (reader.Read()) {
products.Add(PopulateProductFromIDataReader( reader ));
}

  技巧 2 — 分页的数据访问

  ASP.NET DataGrid 具有一个很好的功能:数据分页支持。在 DataGrid 中启用分页时,一次会显示固定数量的记录。另外,在 DataGrid 的底部还会显示分页 UI,以便在记录之间进行导航。该分页 UI 使您能够在所显示的数据之间向前和向后导航,并且一次显示固定数量的记录。

  还有一个小小的波折。使用 DataGrid 的分页需要所有数据均与网格进行绑定。例如,您的数据层需要返回所有数据,那么 DataGrid 就会基于当前页筛选显示的所有记录。如果通过 DataGrid 进行分页时返回了 100,000 个记录,那么针对每个请求会放弃 99,975 个记录(假设每页大小为 25 个记录)。当记录的数量不断增加时,应用程序的性能就会受到影响,因为针对每个请求必须发送越来越多的数据。

  要编写性能更好的分页代码,一个极佳的方式是使用存储过程。图 2 显示了针对Northwind 数据库中的 Orders 表进行分页的一个示例存储过程。简而言之,您此时要做的只是传递页索引和页大小。然后就会计算合适的结果集,并将其返回。

Figure 2 Paging Through the Orders Table
CREATE PROCEDURE northwind_OrdersPaged
(
@PageIndex int,
@PageSize int
)
AS
BEGIN
DECLARE @PageLowerBound int
DECLARE @PageUpperBound int
DECLARE @RowsToReturn int

-- First set the rowcount
SET @RowsToReturn = @PageSize * (@PageIndex + 1)
SET ROWCOUNT @RowsToReturn

-- Set the page bounds
SET @PageLowerBound = @PageSize * @PageIndex
SET @PageUpperBound = @PageLowerBound + @PageSize + 1

-- Create a temp table to store the select results
CREATE TABLE #PageIndex
(
IndexId int IDENTITY (1, 1) NOT NULL,
OrderID int
)

-- Insert into the temp table
INSERT INTO #PageIndex (OrderID)
SELECT
OrderID
FROM
Orders
ORDER BY
OrderID DESC

-- Return total count
SELECT COUNT(OrderID) FROM Orders

-- Return paged results
SELECT
O.*
FROM
Orders O,
#PageIndex PageIndex
WHERE
O.OrderID = PageIndex.OrderID AND
PageIndex.IndexID
@PageLowerBound AND
PageIndex.IndexID
@PageUpperBound
ORDER BY
PageIndex.IndexID

END

  在社区服务器中,我们编写了一个分页服务器控件,以完成所有的数据分页。您将会看到,我使用的就是技巧 1 中讨论的理念,从一个存储过程返回两个结果集:记录的总数和请求的数据。

  返回记录的总数可能会根据所执行查询的不同而有所变化。例如,WHERE 子句可用来约束返回的数据。为了计算在分页 UI 中显示的总页数,必须了解要返回记录的总数。例如,如果总共有 1,000,000 条记录,并且要使用一个 WHERE 子句将其筛选为 1000 条记录,那么分页逻辑就需要了解记录的总数才能正确呈现分页 UI

  技巧 3 — 连接池

  在 Web 应用程序和 SQL Server之间设置 TCP 连接可能是一个非常消耗资源的操作。Microsoft 的开发人员到目前为止能够使用连接池已经有一段时间了,这使得他们能够重用数据库连接。他们不是针对每个请求都设置一个新的 TCP 连接,而是只在连接池中没有任何连接时才设置新连接。当连接关闭时,它会返回连接池,在其中它会保持与数据库的连接,而不是完全破坏该 TCP 连接。

  当然,您需要小心是否会出现泄漏连接。当您完成使用连接时,请一定要关闭这些连接。再重复一遍:无论任何人对 Microsoft?.NET Framework 中的垃圾回收有什么评论,请一定要在完成使用连接时针对该连接显式调用 Close Dispose。不要相信公共语言运行库(CLR) 会在预先确定的时间为您清除和关闭连接。尽管 CLR 最终会破坏该类,并强制连接关闭,但是当针对对象的垃圾回收真正发生时,并不能保证。

  要以最优化的方式使用连接池,需要遵守一些规则。首先打开连接,执行操作,然后关闭该连接。如果您必须如此的话,可以针对每个请求多次打开和关闭连接(最好应用技巧 1),但是不要一直将连接保持打开状态并使用各种不同的方法对其进行进出传递。第二,使用相同的连接字符串(如果使用集成身份验证的话,还要使用相同的线程标识)。如果不使用相同的连接字符串,例如根据登录的用户自定义连接字符串,那么您将无法得到连接池提供的同一个优化值。如果您使用集成身份验证,同时还要模拟大量用户,连接池的效率也会大大下降。尝试跟踪与连接池相关的任何性能问题时,.NET CLR 数据性能计数器可能非常有用。

  每当应用程序连接资源时,如在另一个进程中运行的数据库,您都应该重点考虑连接该资源所花时间、发送或检索数据所花时间,以及往返的数量,从而进行优化。优化应用程序中任何种类的进程跳跃都是获得更佳性能的首要一点。

  应用层包含了连接数据层、将数据转换为有意义类实例和业务流程的逻辑。例如社区服务器,您要在其中填充Forums Threads集合,应用业务规则(如权限);最重要的是要在其中执行缓存逻辑。

  技巧 4 — ASP.NET 缓存 API

  编写应用程序代码行之前,一个首要完成的操作是设计应用层的结构,以便最大化利用ASP.NET 缓存功能。

  如果您的组件要在 ASP.NET 应用程序中运行,则只需在该应用程序项目中包括一个System.Web.dll 引用。当您需要访问该缓存时,请使用 HttpRuntime.Cache 属性(通过Page.Cache HttpContext.Cache 也可访问这个对象)。

  对于缓存数据,有几个规则。首先,如果数据可能会多次使用时,则这是使用缓存的一个很好的备选情况。第二,如果数据是通用的,而不特定于某个具体的请求或用户时,则也是使用缓存的一个很好的备选情况。如果数据是特定于用户或请求的,但是寿命较长的话,仍然可以对其进行缓存,但是这种情况可能并不经常使用。第三,一个经常被忽略的规则是,有时可能您缓存得太多。通常在一个 x86 计算机上,为了减少内存不足错误出现的机会,您会想使用不高于 800MB 的专用字节运行进程。因此缓存应该有个限度。换句话说,您可能能够重用某个计算结果,但是如果该计算采用 10 个参数的话,您可能要尝试缓存 10 个排列,这样有可能给您带来麻烦。一个要求 ASP.NET 的最常见支持是由于过度缓存引起的内存不足错误,尤其是对于大型数据集。

  缓存有几个极佳的功能,您需要对它们有所了解。首先,缓存会实现最近最少使用的算法,使得 ASP.NET 能够在内存运行效率较低的情况下强制缓存清除 从缓存自动删除未使用过的项目。第二,缓存支持可以强制失效的过期依赖项。这些依赖项包括时间、密钥和文件。时间经常会用到,但是对于 ASP.NET 2.0,引入了一个功能更强的新失效类型:数据库缓存失效。它指的是当数据库中的数据发生变化时自动删除缓存中的项。有关数据库缓存失效的详细信息,请参阅 MSDN?Magazine 2004 7 月的 Dino Esposito Cutting Edge 专栏。要了解缓存的体系结构,请参阅下图。


  技巧 5 — 每请求缓存

  在本文前面部分,我提到了经常遍历代码路径的一些小改善可能会导致较大的整体性能收益。对于这些小改善,其中有一个绝对是我的最爱,我将其称之为"每请求缓存"

  缓存 API 的设计目的是为了将数据缓存较长的一段时间,或者缓存至满足某些条件时,但每请求缓存则意味着只将数据缓存为该请求的持续时间。对于每个请求,要经常访问某个特定的代码路径,但是数据却只需提取、应用、修改或更新一次。这听起来有些理论化,那么我们来举一个具体的示例。

  在社区服务器的论坛应用程序中,页面上使用的每个服务器控件都需要个性化的数据来确定使用什么外观、使用什么样式表,以及其他个性化数据。这些数据中有些可以长期缓存,但是有些数据却只针对每个请求提取一次,然后在执行该请求期间对其重用多次,如要用于控件的外观。

  为了达到每请求缓存,请使用 ASP.NET HttpContext。对于每个请求,都会创建一个HttpContext 实例,在该请求期间从 HttpContext.Current 属性的任何位置都可访问该实例。该 HttpContext 类具有一个特殊的 Items 集合属性;添加到此 Items 集合的对象和数据只在该请求持续期间内进行缓存。正如您可以使用缓存来存储经常访问的数据一样,您也可以使用 HttpContext.Items 来存储只基于每个请求使用的数据。它背后的逻辑非常简单:数据在它不存在的时候添加到 HttpContext.Items 集合,在后来的查找中,只是返回 HttpContext.Items 中的数据。

  技巧 6 — 后台处理

  通往代码的路径应该尽可能快速,是吗?可能有时您会觉得针对每个请求执行的或者每n 个请求执行一次的任务所需资源非常多。发送电子邮件或者分析和验证传入数据就是这样的一些例子。

  剖析 ASP.NET Forums 1.0 并重新构建组成社区服务器的内容时,我们发现添加新张贴的代码路径非常慢。每次添加新张贴时,应用程序首先需要确保没有重复的张贴,然后必须使用"坏词"筛选器分析该张贴,分析张贴的字符图释,对张贴添加标记并进行索引,请求时将张贴添加到合适的队列,验证附件,最终张贴之后,立即向所有订阅者发出电子邮件通知。很清楚,这涉及很多操作。

  经研究发现,大多数时间都花在了索引逻辑和发送电子邮件上。对张贴进行索引是一个非常耗时的操作,人们发现内置的 System.Web.Mail 功能要连接 SMYP 服务器,然后连续发送电子邮件。当某个特定张贴或主题领域的订阅者数量增加时,执行 AddPost 功能所需的时间也越来越长。

  并不需要针对每个请求都进行电子邮件索引。理想情况下,我们想要将此操作进行批处理,一次索引 25 个张贴或者每五分钟发送一次所有电子邮件。我们决定使用以前用于对数据缓存失效进行原型设计的代码,这个失效是用于最终进入 Visual Studio? 2005 的内容的。

  System.Threading 命名空间中的 Timer 类非常有用,但是在 .NET Framework 中不是很有名,至少对于 Web 开发人员来说是这样。创建之后,这个 Timer 类将以一个可配置的间隔针对 ThreadPool 中的某个线程调用指定的回调。这就表示,您可以对代码进行设置,使其能够在没有对 ASP.NET 应用程序进行传入请求的情况下得以执行,这是后台处理的理想情况。您还可以在此后台进程中执行如索引或发送电子邮件之类的操作。

  但是,这一技术有几个问题。如果应用程序域卸载,该计时器实例将停止触发其事件。另外,因为 CLR 对于每个进程的线程数量具有一个硬性标准,所以可能会出现这样的情形:服务器负载很重,其中计时器可能没有可在其基础上得以完成的线程,在某种程度上可能会造成延迟。ASP.NET 通过在进程中保留一定数量的可用线程,并且仅使用总线程的一部分用于请求处理,试图将上述情况发生的机会降到最低。但是,如果您具有很多异步操作时,这可能就是一个问题了。

  这里没有足够的空间来放置该代码,但是您可以下载一个可以看懂的示例,网址是www.rob-howard.net。请了解一下 Blackbelt TechEd 2004 演示中的幻灯片和演示。

  技巧 7 — 页输出缓存和代理服务器

  ASP.NET 是您的表示层(或者说应该是您的表示层);它由页、用户控件、服务器控件(HttpHandlers HttpModules)以及它们生成的内容组成。如果您具有一个 ASP.NET 页,它会生成输出(HTMLXML、图像或任何其他数据),并且您针对每个请求运行此代码时,它都会生成相同的输出,那么您就拥有一个可用于页输出缓存的绝佳备选内容。

  将此行内容添加页的最上端 %@ Page OutputCache VaryByParams="none" Duration="60" %

  就可以高效地为此页生成一次输出,然后对它进行多次重用,时间最长为 60 秒,此时该页将重新执行,输出也将再一次添加到 ASP.NET 缓存。通过使用一些低级程序化 API 也可以完成此行为。对于输出缓存有几个可配置的设置,如刚刚讲到的 VaryByParams 属性。VaryByParams 刚好被请求到,但还允许您指定 HTTP GET HTTP POST 参数来更改缓存项。例如,只需设置 VaryByParam="Report" 即可对 default.aspx?Report=1 default.aspx?Report=2 进行输出缓存。通过指定一个以分号分隔的列表,还可以指定其他参数。

  很多人都不知道何时使用输出缓存,ASP.NET 页还会生成一些位于缓存服务器下游的HTTP 标头,如 Microsoft Internet Security and Acceleration Server Akamai 使用的标头。设置了 HTTP 缓存标头之后,可以在这些网络资源上对文档进行缓存,客户端请求也可在不必返回原始服务器的情况下得以满足。

  因此,使用页输出缓存不会使得您的应用程序效率更高,但是它可能会减少服务器上的负载,因为下游缓存技术会缓存文档。当然,这可能只是匿名内容;一旦它成为下游之后,您就再也不会看到这些请求,并且再也无法执行身份验证以阻止对它的访问了。

  技巧 8 — 运行 IIS 6.0(只要用于内核缓存)

  如果您未运行 IIS 6.0 (Windows Server? 2003),那么您就错过了 Microsoft Web 服务器中的一些很好的性能增强。在技巧 7 中,我讨论了输出缓存。在 IIS 5.0 中,请求是通过 IIS 然后进入 ASP.NET 的。涉及到缓存时,ASP.NET 中的 HttpModule 会接收该请求,并返回缓存中的内容。

  如果您正在使用 IIS 6.0,就会发现一个很好的小功能,称为内核缓存,它不需要对ASP.NET 进行任何代码更改。当请求由 ASP.NET 进行输出缓存时,IIS 内核缓存会接收缓存数据的一个副本。当请求来自网络驱动程序时,内核级别的驱动程序(无上下文切换到用户模式)就会接收该请求,如果经过了缓存,则会将缓存的数据刷新到响应,然后完成执行。这就表示,当您将内核模式缓存与 IIS ASP.NET 输出缓存一起使用时,就会看到令人不敢相信的性能结果。在 ASP.NET Visual Studio 2005 开发过程中,我一度是负责 ASP.NET 性能的程序经理。开发人员完成具体工作,但是我要看到每天进行的所有报告。内核模式缓存结果总是最有意思的。最常见的特征是网络充满了请求/响应,而 IIS 运行时的 CPU 使用率只有大约 5%。这太令人震惊了!当然使用 IIS 6.0 还有一些其他原因,但是内核模式缓存是其中最明显的一个。

  技巧 9 — 使用 Gzip 压缩

  虽然使用 gzip 并不一定是服务器性能技巧(因为您可能会看到 CPU 使用率的提高),但是使用 gzip 压缩可以减少服务器发送的字节数量。这就使人们觉得页速度加快了,并且还减少了带宽的用量。根据所发送数据、可以压缩的程度以及客户端浏览器是否支持(IIS只会向支持 gzip 压缩的客户端发送经过 gzip 压缩的内容,如 Internet Explorer 6.0 Firefox),您的服务器每秒可以服务于更多的请求。实际上,几乎每当您减少所返回数据的数量时,都会增加每秒请求数。

  Gzip 压缩已经内置到 IIS 6.0 中,并且其性能比 IIS 5.0 中使用的 gzip 压缩要好的多,这是好消息。但不幸的是,当尝试在 IIS 6.0 中打开 gzip 压缩时,您可能无法在IIS 的属性对话中找到该设置。IIS 小组在该服务器中置入了卓越的 gzip 功能,但是忘了包括一个用于启用该功能的管理 UI。要启用 gzip 压缩,您必须深入到 IIS 6.0 XML 配置设置内部(这样不会引起心脏虚弱)。顺便提一句,这归功于 OrcsWeb Scott Forsyth,他帮助我提出了在 OrcsWeb 上宿主的 www.asp.net 服务器的这个问题。

  本文就不讲述步骤了,请阅读 Brad Wilson 的文章,网址是 IIS6 Compression。还有一篇有关为 ASPX 启用压缩的知识库文章,网址是 Enable ASPX Compression in IIS。但是您应该注意,由于一些实施细节,IIS 6.0 中不能同时存在动态压缩和内核缓存。

  技巧 10 — 服务器控件视图状态

  视图状态是一个有趣的名称,用于表示在所生成页的隐藏输出字段中存储一些状态数据的ASP.NET。当该页张贴回服务器时,服务器可以分析、验证、并将此视图状态数据应用回该页的控件树。视图状态是一个非常强大的功能,因为它允许状态与客户端一起保持,并且它不需要 cookie 或服务器内存即可保存此状态。很多 ASP.NET 服务器控件都使用视图状态来保持在与页元素进行交互期间创建的设置,例如保存对数据进行分页时显示的当前页。

  然而使用视图状态也有一些缺点。首先,服务或请求页时,它都会增加页的总负载。对张贴回服务器的视图状态数据进行序列化或取消序列化时,也会发生额外的开销。最后,视图状态会增加服务器上的内存分配。

  几个服务器控件有着过度使用视图状态的趋势,即使在并不需要的情况下也要使用它,其中最著名的是 DataGridViewState 属性的默认行为是启用,但是如果您不需要,则可以在控件或页级别关闭。在控件内,只需将 EnableViewState 属性设置为 false,或者在页中使用下列设置即可对其进行全局设置:

%@ Page EnableViewState="false" %

  如果您不回发页,或者总是针对每个请求重新生成页上的控件,则应该在页级别禁用视图状态。

  我为您讲述了一些我认为在编写高性能 ASP.NET 应用程序时有所帮助的技巧。正如我在本文前面部分提到的那样,这是一个初步指南,并不是 ASP.NET 性能的最后结果。(有关改善 ASP.NET 应用程序性能的信息,请参阅 Improving ASP.NET Performance。)只有通过自己的亲身体验才能找出解决具体性能问题的最好方法。但是,在您的旅程中,这些技巧应该会为您提供一些好的指南。在软件开发中,几乎没有绝对的东西;每个应用程序都是唯一的。

 

 

 

 

 

 

 

 

 

 

 

 

ASP.NET 2.0 Web 事件

    ASP.NET 2.0 包含了内置的事件,包括心跳、应用程序生存期事件(启动/停止/编译)和错误陷阱事件(未处理异常)。不过,,您可以很容易地在这些基类之上进行构建,以从应用程序重创建并引发您自己的事件。举例来说,您可能创建一个自定义的事件来记录何时第一百个用户单击某一特定的链接。

    ASP.NET 2.0
健康监视系统真正强大的功能是,通过 web.config machine.config 文件它是完全可配置的。使用正常的 XML,您能定义事件、定义提供程序(事件接收器),以及将特定的事件发送到特定的提供程序。

创建事件
   
事件在结构上与异常是类似的。也就是说,除了作为一个消息容器,event 类本身几乎没有功能。在健康监视方面,所有事件都从 Sytstem.Web.Management.WebBaseEvent 继承。不过,您也可以从用于专用目的(如收集 HTTP 请求数据或处理异常)的高级基类派生。

列表 4. 自定义事件

using System;
using System.Web.Management;

public class CustomEvent : WebBaseEvent
{
  public const int EventCode = WebEventCodes.WebExtendedBase + 10;
  public MyEvent(string message, object eventSource)
                : base(message, eventSource, EventCode)
  { }
}

   
创建自定义事件的最重要部分是提供一个唯一的 EventCode。所有内置事件代码都在 WebEventCodes 枚举中。自定义事件应该有从 WebEventCodes.WebExtendedBase + 1 开始的数字。除此之外创建自定义事件中的唯一常见任务是正确地初始化事件。

使用事件
虽然内置事件自动激发,但您还是可以将代码添加到应用程序以在任何时候启动自定义事件。

列表 5. 引发一个事件

<script runat="server">
   void Page_Load(Object sender, EventArgs e)
   { // Raise a custom event MyEvent myEvent =
          new MyEvent("loading webevent sample page", this); myEvent.Raise(); }
</script>

   
当您从 ASP.NET 页面引发一个事件时,您只是创建该事件的一个新实例然后执行 Raise() 方法。Raise() 方法将事件实例自动传递到健康监视引擎。然后此引擎将该事件映射到配置文件和提供程序,并将该事件移交给正确的提供程序。提供程序最终将事件传递到正确的接收器。

配置健康监视
   
健康监视是在 machine.Config Web.Config 文件中的新的 区域配置的。您可以配置 部分来设置一个周期性报告应用程序状态的 Web 检测信号。您也可以配置应用程序来生成事件,并将该事件通过各种提供程序传递。

配置事件
     
必须在事件映射区域标识每个事件。事件通过唯一名称和完整类型被标识。事件名称在规则区域是作为链接来使用的。

列表 6. 事件映射区域

<!-- Event mappings define the events that are monitored -->
<eventMappings>
     <add name="SampleWebRequests"
          type="Samples.ASPNet.SampleWebRequestEvent, SampleWebRequestEvent,
                Version=0.0.0.0, Culture=neutral, PublicKeyToken=f0c63b9a560d5e5a"/>
</eventMappings>

ASP.NET 2.0
附带几个内置事件,配置为下列名称:

所有事件。所有事件名称捕获任何 WebBaseEvent。这个事件类别是对被健康监视系统捕获的每个事件的一个广泛的 catch-all
 
检测信号。检测信号事件使用 WebHeartBeatEvent 提供关于 Web 应用程序状态的定期通知。
 
应用程序生存期事件。应用程序生存期事件包括启动和停止应用程序,以及重新编译应用程序的部分或全部。这些事件是基于 WebApplicationLifetimeEvent 的,并且在 lifetime 事件发生时,报告日期、时间和当前的状态。
 
所有错误。所有错误类别收集系统检测到的任何异常或错误。这些事件是基于 WebBaseErrorEvent 的。

基础结构错误。使用 WebErrorEvent 捕获与 ASP.NET 运行库或 IIS 相关的错误。这些事件是所有错误类别的子类,并且主要与系统管理员而不是应用程序开发者相关。

请求处理错误。在请求期间发生的任何错误或异常都会触发一个 WebRequestErrorEvent。这个事件记录了进入的请求以及与处理该请求相关联的错误。请求处理错误也是所有错误类别的一个子集。
 
所有审核。可使用健康监视系统通过 WebAuditEvent 提供审核尝试。这个事件自动记录 Web 应用程序中活动用户的操作。如果您正在使用模拟,审核事件将帮助您对谁在使用您的应用程序,以及他们是如何在使用保持跟踪。
 
失败审核。WebFailureAuditEvent 是一个特殊的审核事件类型,当一个用户试图使用无效的用户名或密码登录您的 Web 站点时,它就会被触发。当一个对于指定资源用户是无法验证的时候,此事件也会发生。
 
成功审核。WebSuccessAuditEvent 是失败事件的对应,只要用户已验证或执行一些其他需要审核记录的操作就会发生。
 



自定义事件是易于创建的,尽管您必须向您的应用程序添加代码来引发事件。

配置提供程序
   
每个提供程序都必须在配置文件中注册。注册一个提供程序需要应用程序的唯一名称和类型。这个类型包含了实际 provider 类的完整强名称 (strong name)。提供程序的名称被用作规则区域中的一个链接。

列表 7. 提供程序

<healthMonitoring Enabled="true" heartBeatInterval="0">
 <!-- Providers link health events to various targets such as WMI or SMTP email -->
   <providers>
      <add name="WmiEventProvider" type="System.Web.Management.WebWmiEventProvider,
          System.Web,Version=1.2.3400.0,Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"/>
  </providers>

    ASP.NET 2.0
附带有 WMIWindows 事件监视器、SMTP 电子邮件和 SQL Server 数据库的提供程序。通过扩展正确的基类,可以创建自定义提供程序来连接到其他事件接收器。

将事件映射到提供程序

   
配置健康监视的最后一步是,用规则将事件连接到提供程序。规则提供了在好的事件名称、事件类、提供程序和事件配置文件或者类别之间的一个链接。您也可以使用规则来定义启动特定的事件的一个最短时间间隔。

列表 8. 规则

<!-- Rules link events to providers and profiles, and define intervals for event checking -->
<rules>
    <add name="Custom Database Events" eventName="CustomDBEvents"
         provider="WmiEventProvider" profile="Database" minInterval="00:01:00" />
    <add name="Standard Web Requests" eventName="All Events"
         provider="SqlEventProvider" profile="Default" minInterval="00:01:00" /> </rules>
</healthMonitoring>
 

规则执行由不同属性配置的若干不同任务:

• name
。规则名称是友好名称,它会在事件被发送到接收器时出现。
 
• eventName
eventName 映射到一个在 区域中配置的事件。
 
• provider
。提供程序是一个到在 区域中配置的提供程序的链接。任何匹配这个规则的事件将通过这个提供程序传递给由该提供程序支持的目标。举例来说,System.Web.Management.SqlWebEventProvider 会自动将事件写入到 SQL Server 数据库。
 
• profile
。不同的提供程序将配置文件属性作为显示事件的过滤器来使用。举例来说,电子邮件提供程序可能立即为任何带有紧急配置文件的事件发送一个消息,但是也可能只发送带有该日例行配置文件事件的汇编的日常电子邮件。
 
• minInterval
。一些事件,如检测信号,必须在最短的时间间隔内激发。您可以使用这个属性来设置最小事件时间间隔。
 

   
通过这个配置区域,您可以设置各种健康相关事件,并将事件映射到各种提供程序。举例来说,您可能设置一个检测信号,每 10 分钟发送一个 WMI 事件。同样,您可以为任何未捕获的异常设置一个电子邮件警报。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

[asp.net]扩展Forms验证

1.使用Forms验证存储用户自定义信息

Forms
验证在内部的机制为把用户数据加密后保存在一个基于cookie的票据FormsAuthenticationTicket中,因为是经过特殊加密的,所以应该来说是比较安全的。而.net除了用这个票据存放自己的信息外,还留了一个地给用户自由支配,这就是现在要说的UserData

UserData
可以用来存储string类型的信息,并且也享受Forms验证提供的加密保护,当我们需要这些信息时,也可以通过简单的get方法得到,兼顾了安全性和易用性,用来保存一些必须的敏感信息还是很有用的。

下面来看怎么使用UserData,然后会给出一个实际使用的例子。

//
创建一个新的票据,将客户ip记入ticketuserdata
FormsAuthenticationTicket ticket=new FormsAuthenticationTicket(
1,userName.Text,DateTime.Now,DateTime.Now.AddMinutes(30),
false,Request.UserHostAddress);
//
将票据加密
string authTicket=FormsAuthentication.Encrypt(ticket);
//
将加密后的票据保存为cookie
HttpCookie coo=new HttpCookie(FormsAuthentication.FormsCookieName,authTicket);
//
使用加入了userdata的新cookie
Response.Cookies.Add(coo);

下面是FormsAuthenticationTicket构造函数的重载之一的方法签名
public FormsAuthenticationTicket(
int version,
string name,
DateTime issueDate,
DateTime expiration,
bool isPersistent,
string userData
);

参数
version
版本号。
name
与身份验证票关联的用户名。
issueDate
Cookie
的发出时间。
expiration
Cookie
的到期日期。
isPersistent
如果 Cookie 是持久的,为 true;否则为 false
userData
将存储在 Cookie 中的用户定义数据

使用userdata也很简单,FormsIdentityTicket属性就提供了对当前票据的访问,获得票据后就可以用UserData属性访问保存的信息,当然是经过解密的。
((System.Web.Security.FormsIdentity)this.Context.User.Identity).Ticket.UserData


下面是一个具体的应用。

由于Forms验证是通过cookie来进行的,它需要传递一个票据来进行工作。虽然票据是加密的,里面的内容不可见,但这并不能阻止别人用一个假冒的身份使用票据(就像我们可以拿别人的钥匙去开别人的锁),比较常见的就是不同ip的用户在不安全通道截获了这个票据,然后使用它进行一些安全范围外的活动。

解决这个问题的办法之一就是使用SSL来传递信息。

但是如果不能使用SSL呢?我们可以判断ip和票据是否匹配,如果发出请求的ip是初次产生票据的ip,则没有问题,否则就销毁这个票据。

为此,我们需要在一开始处理登录时将用户的ip保存起来,这样就可以在以后的请求中随时验证后继请求的ip是否和初始ip相同。保存这个敏感ip的最佳场所当然是UserData啦,而验证的时机则是在AuthenticateRequest事件发生时,即Global.aspx.cs中定义的处理此事件的Application_AuthenticateRequest方法中。

上面的示例实际上已经是把用户ip保存到了UserData中,下面是验证的过程。

if(this.Request.IsAuthenticated)
{
if(((System.Web.Security.FormsIdentity)this.Context.User.Identity).Ticket.UserData !=this.Request.UserHostAddress)
{
System.Security.Principal.GenericIdentity gi=new System.Security.Principal.GenericIdentity("","");
string[] rolesi={};
System.Security.Principal.GenericPrincipal gpi=new System.Security.Principal.GenericPrincipal(gi,rolesi);
this.Context.User=gpi;
}
}

通过给GenericPrincipal空的GenericIdentityroles使票据失效,这样将强迫用户重新登录。为了测试这个方法,可以先把条件改为相等,看效果如何 :)

这个方法也有不足之处,具体为:

1.
使用同一代理的用户将拥有同一个ip,这样就不能防范此类假冒攻击了

2.
如果用户使用动态ip,则可能造成正常用户被我们强行销毁票据。不过总的来说,这个办法还是比较可行的。

 

 

正则表达式

Email : /^/w+([-+.]/w+)*@/w+([-.]/w+)*/./w+([-.]/w+)*$/,
Phone : /^((/(/d{3}/))|(/d{3}/-))?(/(0/d{2,3}/)|0/d{2,3}-)?[1-9]/d{6,7}(/-/d{1,4})?$/,
Mobile : /^((/(/d{3}/))|(/d{3}/-))?13/d{9}$/,
Url : /^http:////[A-Za-z0-9]+/.[A-Za-z0-9]+[//=/?%/-&_~`@[/]/':+!]*([^<>/"/"])*$/,
IdCard : /^/d{15}(/d{2}[A-Za-z0-9])?$/,
Currency : /^/d+(/./d+)?$/,
Number : /^/d+$/,
Zip : /^[1-9]/d{5}$/,
QQ : /^[1-9]/d{4,8}$/,
Integer : /^[-/+]?/d+$/,
Double : /^[-/+]?/d+(/./d+)?$/,
English : /^[A-Za-z]+$/,
Chinese : /^[/u0391-/uFFE5]+$/,
Username : /^[a-z]/w{3,}$/i,
UnSafe : /^(([A-Z]*|[a-z]*|/d*|[-_/[email protected]#/$%/^&/*/./(/)/[/]/{/}<>/?/////'/"]*)|.{0,5})$|/s/

日期:^/d{4}/-/d{1,2}/-/d{1,2}

 

:请列举一些您用到过的设计模式以及在什么情况下使用该模式?

 
Q
:您对编程的兴趣如何?工作中遇到不懂的问题是怎样去解决的?您一般怎样去提高自己的编程水平?

 
Q
:通过超链接怎样传递中文参数?
Have you doing any software development in recent years,do you familiar with the multi-tier software design (also called n-tier design)?
:如果在一个B/S结构的系统中需要传递变量值,但是又不能使用SessionCookieApplication,您有几种方法进行处理?
DataReaderDataset有什么区别?
:怎样理解静态变量?
:值类型与引用类型有什么区别?
A
Override与重载有什么区别?
A
如果预计要创建组件的多个版本,则创建抽象类。抽象类提供简单易行的方法来控制组件版本。通过更新基类,所有继承类都随更改自动更新。另一方面,接口一旦创建就不能更改。如果需要接口的新版本,必须创建一个全新的接口。
如果创建的功能将在大范围的全异对象间使用,则使用接口。
抽象类应主要用于关系密切的对象,而接口最适合为不相关的类提供通用功能。
如果要设计小而简练的功能块,则使用接口。
如果要设计大的功能单元,则使用抽象类。
如果要在组件的所有实现间提供通用的已实现功能,则使用抽象类。抽象类允许部分实现类,而接口不包含任何成员的实现。;