六月 30 2009
四月 12 2009
三月 20 2009

ASP.NET MVC 教程推荐

kick it on 伯乐族 www.ibole.cn

十月 07 2007

Tip/Trick: Building a ToJSON() Extension Method using .NET 3.5

Earlier this year I blogged about a new language extensibility feature of C# and VB called "Extension Methods". 

Extension methods allow developers to add new methods to the public contract of an existing CLR type, without having to sub-class it or recompile the original type.  In doing so they enable a variety of useful scenarios (including LINQ).  They also provide a really convenient way to add a dash of "syntactic sugar" into your code.

Over the last few months I've been making a list of cool extension methods that I plan to sit down and implement when I get some free time (not sure when that is... but at least I can still have fun coming up with the ideas!)  Two of the scenarios I added to my extension method list were easy methods to automate generating JSON (JavaScript Object Notation) or XML serialization strings for any .NET object. 

Simple Scenario: The ToJSON() extension method

Assume I had a Person object defined like below (note: I'm using the new automatic properties feature to implement it):

I'd like to then be able to initialize a collection of Person objects and programmatically retrieve a JSON string representation of them by just calling a ToJSON() extension method on it like below:

This would work just like the built-in ToString() method on the Object class in .NET today - except that it would generate a JSON-format representation of the collection that I could use for AJAX scenarios on the client:

Note: Clicking on the hour-glass in the debugger above allows us to bring up the Text Visualizer in VS to see a clean version of the JSON serialization:

This string format could then be used within JavaScript on the client to instantiate an appropriate JavaScript object that represents my collection (note: ASP.NET AJAX has a built-in JavaScript library to support this).

Implementing the ToJSON Extension Method

Implementing a basic ToJSON() extension method is pretty simple.  All I needed to-do was use the JavaScriptSerializer class in the System.Web.Script.Serialization namespace, and define two extension methods like below. One of the methods serializes an object graph any levels deep, the other is an overloaded version that allows you to optionally constrain how deep it recurses (for example: ToJSON(2) would serialize only 2 levels deep in the object graph).

Note that the ToJSON() extension methods above are defined for type "Object" - which means they can be used with all objects in .NET (not just collections).  This means that in addition to calling .ToJSON() on collections like I did above, I could also have called ToJSON() on individual Person objects, as well as any other .NET datatype.

To use the extension method, all I need to-do is add a using statement at the top of my program that references the namespace it was defined within:

VS 2008 then takes care of providing intellisense and compile time support for it to all objects:

Note: In addition to the JavaScriptSerializer class, .NET 3.5 also now includes a new System.Runtime.Serialization.DataContractJsonSerializer class that you can use for JSON serialization/deserialization.

Summary

Hopefully the above sample provides a simple example of how you can easily encapsulate useful functionality into extension methods.  Overtime I expect that we'll start to see some nice utility libraries come out that provide helpful extension methods like above. 

I'd be curious to see suggestions for other common scenarios you think should be packaged up into re-usable extension methods (feel free to use the comments of this post to suggest them).  We can then figure out how to get a good CodePlex project created that bundles up some of them together into one library to easily use.

Hope this helps,

Scott

P.S. Please check out my Tips/Tricks and Tutorials page to find other useful ASP.NET and .NET posts I've done.

,
十月 06 2007

WebSite和WebApplication有何区别

WebApplication编程模型的优点:
    网站编译速度快,使用了增量编译模式,仅仅只有文件被修改后,这部分才会被增量编译进去。
    生成的程序集
     WebSite:生成随机的程序集名,需要通过插件WebDeployment才可以生成单一程序集WebApplication:可以指定网站项目生成单一程序集,因为是独立的程序集,所以和其他项目一样可以指定应用程序集的名字、版本、输出位置等信息

可以将网站拆分成多个项目以方便管理
可以从项目中和源代码管理中排除一个文件
支持VSTS的Team Build方便每日构建
更强大的代码检查功能,并且检查策略受源代码控制
可以对编译前后进行自己规定的处理
对App_GlobalResources 的Resource强类支持
直接升级使用VS2003构建的大型系统

WebSite编程模型的优点:
动态编译该页面,马上可以看到效果,不用编译整个站点(主要优势)
同上,可以使错误的部分和使用的部分不相干扰可以每个页面生成一个程序集
可以把一个目录当做一个Web应用来处理,直接复制文件就可以发布,不需要项目文件
可以把页面也编译到程序集中
两种编程模型的互相转换:
VS2005 SP1内置了转换程序,可以非常方便的从WebSite转换到WebApplication
只需要复制文件,右键执行“转换为Web应用程序”即可。未查到有专门的反向转换工具,但比较后发现如果转换也非常简单。*.designer.cs
*.aspx
*.ascx
*.master
删除所有*.designer.cs
将*.aspx、*.ascx、*.master页面文件中的 Codebehind="FileList.aspx.cs" 批量替换成 CodeFile="FileList.aspx.cs"
总之,大网站比较适合用WebApplication项目,小网站比较适合用WebSite项目
www.ibole.cn tags:
八月 14 2007

.NET Cache

摘要: 介绍缓存的基本概念和常用的缓存技术,给出了各种技术的实现机制的简单介绍和适用范围说明,以及设计缓存方案应该考虑的问题(共17页)

1         概念
1.1   缓存能解决的问题
· 性能——将相应数据存储起来以避免数据的重复创建、处理和传输,可有效提高性能。比如将不改变的数据缓存起来,例如国家列表等,这样能明显提高web程序的反应速度;

· 稳定性——同一个应用中,对同一数据、逻辑功能和用户界面的多次请求时经常发生的。当用户基数很大时,如果每次请求都进行处理,消耗的资源是很大的浪费,也同时造成系统的不稳定。例如,web应用中,对一些静态页面的呈现内容进行缓存能有效的节省资源,提高稳定性。而缓存数据也能降低对数据库的访问次数,降低数据库的负担和提高数据库的服务能力;

· 可用性——有时,提供数据信息的服务可能会意外停止,如果使用了缓存技术,可以在一定时间内仍正常提供对最终用户的支持,提高了系统的可用性。

1.2   理解状态
在深入介绍缓存技术之前,需要对状态有一个认识,因为缓存可以说是状态管理的框架。理解状态的含义和它的一些特性——比如生存期和生存范围——对决定是否缓存和选择合适的缓存技术有很大帮助。状态是指一些数据,在应用系统某个时间点上,数据的状态和条件。这些数据可能是永久性的存储在数据库中,可能是只在内存里停留一会,也可能是按照某个逻辑存活(比如多长时间后释放),它的应用范围可能是所有用户可访问,可能是单个用户有权限;

1.2.1  状态的生存期
生存期是指数据保持有效性的时间区间,也就是从创建到移除的时间间隔。通常的生存期有以下几种:

·永久状态Permanent State——应用程序使用的永久数据;

·进程状态Process State——只在进程周期内有效;

·会话状态Session State——和特定的用户会话有关;

·消息状态Message State——处理某个消息的时间内有效;

1.2.2  状态的范围
状态的范围指对该状态有访问权限的物理或逻辑范围。

·物理范围指可以被访问到的状态数据存放的物理位置,通常包括:

1、  组织Organization——在一个组织内的所有应用程序可以访问状态数据;

2、  场Farm——在应用场范围内的任何机器上都可以访问;

3、  机器Machine——单个机器范围内可以访问;

4、  进程Process——进程内的访问许可;

5、  应用域AppDomain——应用程序域内的访问许可。

·逻辑范围指可访问状态数据的逻辑范围,常见的有:

1、  应用程序Application;

2、  业务流程Business Process;

3、  角色Role;

4、  用户User;

1.2.3  状态数据的陈旧
缓存的状态数据只是主数据(Master State Data)的快照,由于数据源可能被修改,所以状态数据就有会陈旧的特性。合理利用此特性和将数据陈旧的负面影响最小化是缓存状态数据的一个重要任务。你可以以一下方式定义数据的陈旧依据:

·主数据更改的可能性——随着时间的推进,主数据更改的可能是否大大增加?安照这一点来决定缓存状态数据的陈旧;

·更改的相关性——主数据更新时,缓存的状态数据不相应更新是不是造成影响系统的使用?比如,更改系统的外观风格并不会对业务造成很大影响。

1.2.4  状态数据陈旧的容忍度
缓存状态数据的陈旧对业务流程的影响称为容忍度,应用系统的可以为不能容忍(No Tolerance)和一定程度的容忍(some Tolerance),前者必须和主数据同步更新,后者允许一定时间或一定范围的陈旧,判断标准就是对业务流程的影响度。 

1.2.5  理解状态数据的转换过程
状态的另一个属性是在不同阶段的表现形式。在数据库中存储的是原始格式的数据,业务流程中的是处理过的数据,给最终用户呈现的则是另外的形式。如下表所示:

表现形式
 描述
 举例
 
原始数据
 数据的原始形式
 如数据库中的数据
 
处理过的数据
 业务流程中对原始数据加工后的数据
 业务过程中的数据形式
 
呈现形式
 可呈现给最终用户的形式
 HTML或可理解的文字说明
 

当决定缓存数据时,应该考虑缓存哪个阶段(哪种形式)的状态数据。以下方针有助于你做决定:

· 当业务逻辑可以容忍缓存数据的陈旧时就缓存原始数据;原始数据可以缓存在数据库访问组件和服务代理中;

·缓存处理过的数据以减少处理时间和资源;处理过的数据可以缓存在业务逻辑组件和服务接口中。

·当需要呈现的数据量很大并且控件的呈现时间很长时,缓存呈现数据(比如包含大数据量的Treeview控件)。这种数据应该被缓存在UI控件中。

1.3   为什么要缓存数据
在应用程序中缓存数据有以下好处:

·减少交互的通讯量——缓存数据能有效减少在进程和机器间的传输量;

·降低系统中的处理量——减少处理次数;

·降低需要做的磁盘访问次数——比如缓存在内存中的数据。

1.4   数据应该被缓存在哪里
缓存数据只是一份主数据的拷贝,它可能在内存中或以不同的表现形式保存在硬盘上,也就是说,离说句的使用者越近越好。所以,除了考虑要缓存哪些数据以外,数据缓存在哪里也是一个主要的考量点。这个问题分为以下两个范围:

1、  存储类型Storage Type——数据可用的物理存储位置;

2、  层间的架构元素(Layered architecture elements)——数据可用的逻辑存储位置。

1.4.1  存储类型
缓存有很多实现方法,所有这些可以被分为两类,基于内存的缓存和基于磁盘的缓存:

1、  内存驻留缓存——包含在内存中临时存储数据的所有实现方法,通常在以下情况下使用:

a)       应用程序频繁使用同样的数据;

b)       应用程序需要经常获取数据;

通过将数据保留在内存中,你可以有效降低昂贵的磁盘访问操作,也可以通过将数据保留在使用者进程中来最大程度的减少跨进程的数据传输。

2、  磁盘驻留缓存——这种技术包含所有使用磁盘作为存储介质的缓存技术,如文件和数据库。在以下情况下基于磁盘的缓存是很有效的:

a)       处理大数据量时;

b)       应用服务提供的数据可能并不是总能使用(比如离线的情况);

c)       缓存的数据必须能在进程回收和机器重启的情况下保持有效;

通过缓存处理过的数据,你可以有效降低数据处理的负担,同时可减少数据交互的代价。

1.4.2  架构间元素
应用程序中的每个逻辑层的组件都会处理数据,下图标识了一些通用组件:

当使用这些组件进行工作时,你需要考虑哪些数据可以被缓存起来,还有以哪种方式进行缓存会对程序的整体性能和可用性有帮助,以上的这些元素都可以缓存相应的数据。当然,要考虑的远不止这些。

1.5   实施缓存时的考虑
当设计一个缓存方案时,不但要考虑缓存哪些数据、数据缓存到哪里,还有其它的因素需要考虑。

1.5.1  格式和访问模式
当决定是否缓存一个对象时,关于数据的格式和访问机制,你需要考虑三个主要问题:

1、  线程安全——当缓存的内容可以被多个线程访问时,使用某种锁定机制来保证数据不会被两个线程同时操作;

2、  序列化——将一个对象缓存时,需要将它序列化以便保存,所以包缓存的对象必须支持序列化;

3、  规格化缓存数据——缓存数据时,相对于要使用的数据格式而言,要保证数据的格式是优化过的。

1.5.2  内容加载
在使用缓存数据前,必须将数据加载到缓存中,有两种机制来加载数据:

·提前加载Proactive Load——使用这种方式时,你提前将所有的状态数据加载到缓存中,可能在应用程序或线程启动时进行,然后在应用程序或线程的生存期内一直缓存;

·动态加载Reactive Load——或称反应式加载,当使用这种方法时,在应用程序请求数据时取到数据,并且将它缓存起来以备后续使用。

1.5.3  过期策略
另外一个关键因素是如何保持缓存数据和主数据(文件或数据库或其他的应用程序资源)的一致性,你可以定义过期策略来决定缓存中的内容,如已经缓存的时间或者收到其他资源的通知。

1.5.4  安全性
当缓存数据时,需要非常清楚缓存中数据的潜在安全威胁。缓存中的数据可能会被别的进程访问或修改,而此进程对主数据是没有权限的。原因是当数据存储在原始位置时,有相应的安全机制来保护它,当数据被带出传统的安全边界时,需要有同等的安全机制。

1.5.5  管理
当你缓存数据时,应用系统需要的维护工作加大了。在发布应用程序时,需要配置相应的属性,比如缓存的大小限制和清除策略。同时要使用某种机制来监控缓存的效率(比如事件日志和性能计数器)

1.6   小结
第一节内容简单介绍了缓存技术中的概念、缓存数据的原因和方案、优势、实施缓存方案时的考虑等基本内容。现在你对缓存中涉及的内容有了一个大致了解,下面着重介绍可用的缓存技术。

2         缓存技术
本节将介绍以下技术:

使用Asp.Net缓存;

使用Remoting Singleton缓存;

使用内存映射文件;

使用SQL Server缓存;

使用静态变量缓存;

使用Asp.net 会话状态(Session State);

使用Asp.net客户端缓存和状态;

使用Internet Explorer缓存。

2.1             Asp.net缓存 
将常用的数据保存在内存中对asp的开发人员来说并不陌生,Session对象和Application对象提供键值对来缓存数据,Session对象保存和单个用户有关的数据,Application对象可保留和应用程序有关的数据,每个用户都可以访问。

在Asp.net中,提供了专门用于缓存数据的Cache对象,它的应用范围是应用程序域。生存期是和应用程序紧密相关的,每当应用程序启动的时候就重新创建Cache对象。它域Application对象的主要区别就是提供了专门用于缓存管理的特性,比如依赖和过期策略。

你可以使用Cache对象和它的属性来实现高级的缓存功能,同时可以利用Asp.net Cache来对客户端输出的响应内容进行缓存。关于Asp.net中的缓存技术,有以下内容要介绍:

2.1.1  编程缓存Programmatic Caching
Cache对象定义在System.Web.Caching命名空间,可以使用HttpContext类的Cache属性或Page对象的Cache属性来得到Cache的引用,Cache对象除了存储键值对以外,还可以存储.net框架的对象。下面介绍相应的依赖和过期策略。

2.1.1.1 依赖和过期策略
当向缓存中加数据时,可以指定它的依赖关系来实现在某些情况下强制移除它。可用的方案包括以下几种:

·文件依赖(File Dependency)——当硬盘上的某个(某些)文件更改时,强制移除缓存数据;如:

CacheDependency cDependency = new

CacheDependency(Server.MapPath("authors.xml"));

Cache.Insert("CachedItem", item, cDependency);

·键值依赖(Key Dependency)——指定缓存中的某个数据项更改时移除。如:

// Create a cache entry.

Cache["key1"] = "Value 1";

// Make key2 dependent on key1.

String[] dependencyKey = new String[1];

dependencyKey[0] = "key1";

CacheDependency dependency = new CacheDependency(null, dependencyKey);

Cache.Insert("key2", "Value 2", dependency);

·基于时间的过期策略——按照预先定义的时间策略来使数据失效,可以是绝对时间(如某个日期的18:00)也可以是相对现在的相对时间。如:

/// Absolute expiration

Cache.Insert("CachedItem", item, null, DateTime.Now.AddSeconds(5),Cache.NoSlidingExpiration);

/// Sliding expiration

Cache.Insert("CachedItem", item, null, Cache.NoAbsoluteExpiration,

TimeSpan.FromSeconds(5));

使用太短和太长的过期时间都不行,不是造成用不上的缓存数据,就是缓存了陈旧的数据并加重了缓存负担,所以可以使用高并发的测试来决定过期时间的最佳值。

·另外有个问题就是如何实现对数据库的依赖,这就要求实现自己的通知机制,当数据库数据改变时能够通知你的缓存数据改变。可参考http://www.gotdotnet.com/team/rhoward的示例。

由于数据会过期,所以当使用缓存中的数据时,必须检查数据的有效性。如以下代码:

string data = (string)Cache["MyItem"];

if (data == null)

{

data = GetData();

Cache.Insert("MyItem", data);

}

DoSomeThingWithData(data);

依赖和过期策略指定了缓存中数据的移除方式,有时候你可能需要在移除发生时做一些工作,这能靠写代码来实现这一点,这就是我们要讲到的。

2.1.1.2 使用缓存回调(Cache Callback)
你可以定义回调,这样当移除自动发生时, 你可以不移除它或者使用新的数据来替换它。如:

CacheItemRemovedCallback onRemove = new CacheItemRemovedCallback(this.RemovedCallback);

Cache.Insert("CachedItem",

item,

null,

Cache.NoAbsoluteExpiration,

Cache.NoSlidingExpiration,

CacheItemPriority.Default,

onRemove);

// Implement the function to handle the expiration of the cache.

public void RemovedCallback(string key, object value, CacheItemRemovedReason r)

{

// Test whether the item is expired, and reinsert it into the cache.

if (r == CacheItemRemovedReason.Expired)

{

// Reinsert it into the cache again.

CacheItemRemovedCallback onRemove = null;

onRemove = new CacheItemRemovedCallback(this.RemovedCallback);

Cache.Insert(key,

value,

null,

Cache.NoAbsoluteExpiration,

Cache.NoSlidingExpiration,

CacheItemPriority.Default,

onRemove);

}

}

2.1.1.3 对缓存项使用优先级
当运行应用程序的服务器内存不足时,会自动清除缓存中的数据,称为“清除scavenging”。此时,Cache对象根据缓存项的优先级来决定先移除哪些缓存数据,你可以在代码中指定缓存项的优先级。参看MSDN中“CacheItemPriority 枚举”,如:

Cache.Insert("DSN", connectionString, null, d, t, CacheItemPriority.High, onRemove);

2.1.1.4 刷新数据(清除缓存)
没有直接的方法来刷新Asp.net的输出缓存,但是有替代方法(设置所有数据失效),比如:

Response.Cache.SetExpires(DateTime.Now)

这可以清除缓存,但页面上并不立刻体现出来,直到最初的缓存期结束,比如:

<%@ OutputCache Duration="10" VaryByParam="none" %>指令指定的缓存只会在10秒后才清除。通常并不需要清除所有缓存项,你只要重新加载数据更新缓存就够了。

2.1.2  输出缓存(Output Cache)
你可以使用两种方式的输出缓存来缓存需要传输和显示到客户端浏览器上的数据——页面输出缓存(Page Output Cache)和页面片断缓存(Page Fragment Cache)。当整个页面相对变化较少时,可以缓存整个页面;如果只是页面的一部分经常变化,可以使用片断缓存。

2.1.2.1 页面输出缓存
Page Output Caching将对页面请求的响应放入缓存中,后续对此页面的请求将直接从缓存中得到信息而不是重建此页面。可以通过添加Page指令(高级别,声明实现)来实现,也可以使用HTTPCachePolicy类来实现(低级别,程序实现)。本指南不打算介绍技术细节,只给出如何更好使用的指南和最佳实践。有四方面的内容:

1、 决定缓存的内容

页面输出缓存可缓存各种信息,缓存这些信息意味着你不需要经常处理同样的数据和结果,包括:

·经常被请求但不不改变的静态页面;

·更新频率和时间已知的页面(如显示股票价格的页面);

·根据HTTP参数,有几个可能输出的页面(如根据城市的代号显示该城市天气情况的页面);

·从Web Service返回的结果;如:

[WebMethod(CacheDuration=60)]

public string HelloWorld()

{

return "Hello World";

}

2、 缓存动态页面

基于输入参数、语言和浏览器类型改变的动态网页经常用到。你可以使用OutputCache的以下属性来实现对动态页面的缓存:

VaryByParam——基于输入参数不同缓存同一页面的多个版本;

VaryByHeader——基于Page Header的内容不同缓存页面的多个版本;

VaryByCustom——通过声明属性和重载GetVaryByCustomString方法来定制缓存处理页面的多个版本;

VaryByControl——基于控件中asp对象属性的不同来缓存控件。

对多个版本页面的缓存会降低可用内存,所以要仔细衡量缓存策略。s

3、 控制缓存的位置

你可以使用@OutputCache指令的OutputCacheLocation属性的枚举值来指定缓存的位置,如:

<%@ outputcache duration="10" varybyparam="none" Location="Server" %>

4、 配置页面输出缓存

有两种方式控制,你可以使用Page指令,也可以使用Cache API编程实现。参考以下两段代码:

//代码1,使用指令

<%@ OutputCache Duration="20" Location="Server" VaryByParam="state" VaryByCustom="minorversion" VaryByHeader="Accept-Language"%>

//代码2,编程实现

private void Page_Load(object sender, System.EventArgs e)

{

// Enable page output caching.

Response.Cache.SetCacheability(HttpCacheability.Server);

// Set the Duration parameter to 20 seconds.

Response.Cache.SetExpires(System.DateTime.Now.AddSeconds(20));

// Set the Header parameter.

Response.Cache.VaryByHeaders["Accept-Language"] = true;

// Set the cached parameter to 'state'.

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

// Set the custom parameter to 'minorversion'.

Response.Cache.SetVaryByCustom("minorversion");



}

2.1.2.2 页面片断缓存
有时候缓存整个页面并不灵活,同时内存的发但也比较大,这时候应考虑片断缓存。页面片断缓存适合以下类型的数据:

·创建开销很大的页面片断(控件);

·包含静态数据的页面片断;

·可被多个用户使用的页面片断;

·多个页面共享的页面片断(如公用菜单条)

以下是缓存部分页面的例子:

// Partial caching for 120 seconds

[System.Web.UI.PartialCaching(120)]

public class WebUserControl : System.Web.UI.UserControl

{

// Your Web control code

}

2.1.3  在非Web项目中使用Asp.net缓存
Asp.net Cache位于System.Web命名空间,但由于它是一个通用的方案,所以仍然可以在引用此命名空间的任何非Web项目中使用它。

System.Web.Caching.Cache 类是对象的缓存,它可以通过System.Web.HttpRuntime.Cache 的静态属性或System.Web.UI.Page 和System.Web.HttpContext.Cache来访问。因此在请求上下文之外也可以存在,在每个应用程序域中只有一个实例,所以HttpRuntime.Cache对象可以在Aspnet_wp.exe之外的每个应用程序域中存在。以下代码演示了在普通应用里访问Cache对象:

HttpRuntime httpRT = new HttpRuntime();

Cache cache = HttpRuntime.Cache;

 

2.2             使用Remoting Singleton缓存
.Net Remoting提供了跨应用程序域、跨进程、跨计算机的程序运行框架。服务器激活的对象有两种激活模式,其中Singleton 类型任何时候都不会同时具有多个实例。如果存在实例,所有客户端请求都由该实例提供服务。如果不存在实例,服务器将创建一个实例,而所有后继的客户端请求都将由该实例来提供服务。由于 Singleton 类型具有关联的默认生存期,即使任何时候都不会有一个以上的可用实例,客户端也不会总接收到对可远程处理的类的同一实例的引用。所以将数据缓存起来可以在多个客户端之间共享状态信息。

为了使用.Net Remoting实现缓存方案,要保证远程对象的租约不过期,并且远程对象没有被垃圾回收器销毁(对象租约是指在系统删除该对象前它在内存中的生存期)。当实现缓存时,重载MarshalByRefObject的InitializeLifetimeService方法并且返回null,这样就能保证租约永远不过期并且相关的对象生存期是无限的。以下代码是一个示例:

public class DatasetStore : MarshalByRefObject

{

// A hash table-based data store

private Hashtable htStore = new Hashtable();

//Returns a null lifetime manager so that GC won't collect the object

public override object InitializeLifetimeService() { return null; }

// Your custom cache interface

}

注意:由于这种方案的成本较高、性能上的限制并且可能造成系统不稳定,通常采用基于Sql Server的方案来替代。

2.3             使用内存映射文件(Memory-Mapped File)
内存映射文件提供独一无二的特性,允许应用程序通过指针来访问磁盘上的文件——与访问动态内存趣的方式一样。所以你可以将应用程序进程中的某个地址段的数据映射到文件中,供多个跨应用程序域或跨进程访问。

在windows中,代码和数据是以以种方式处理的,表现形式都是内存页,而在内存页背后都是磁盘上的文件。唯一的不同磁盘上的文件类型不同。代码后面是可执行的镜像,而数据后面则是系统的页面文件。当多个应用程序共享内存时,系统的性能会有明显提升。

你可以使用内存映射文件的这种特性来实现同一台机器上的跨进程和跨应用程序域的缓存解决方案。基于内存映射文件的缓存方案包含以下组件:

·windows NT服务——启动时创建内存映射文件,停止时删除它。功能是向使用缓存的进程提供句柄。当然,也可以使用命名的内存映射文件来提供操作接口;

·缓存托管组件(Cache Management Dll)——实现特定的缓存功能,比如:

a.     插入和删除数据项到缓存中;

b.     使用算法清除缓存,比如最后使用算法(Least Recently Used);

c.     保证数据不被篡改;

基于内存映射文件的缓存方案可以用在应用程序的每个层中,但由于使用win32 API调用,所以并不容易实现。.Net 框架不支持内存映射文件,所以只能以非托管代码的方式运行,当然也不能利用.Net框架的有力特性,比如垃圾回收等。同时缓存数据项的管理功能需要定制开发,还要开发性能计数器来监控缓存的效果。

2.4             使用SQL Server缓存
如果需要在进程回收(重启)、机器重启或电源故障的过程中保持缓存数据的有效,基于内存的方案并不能满足要求。你可以使用基于永久数据存储的方案,如SQL server数据库或NTFS文件系统。

SQL Server在使用sql语句或存储过程得到数据时,对varchar和varBinary类型的数据有8k的大小限制,你必须使用.Net 框架提供的Ado.Net SQLDataAdapter对象来访问datatable或dataset。

使用SQL Server缓存数据的优点:

·易于实现——使用.Net 框架和Ado.Net访问数据库相当方便;

·完善的安全模型和很高的健壮性;

·数据非常方便的共享;

·数据的持久保留。

·支持很大的数据量。

·方便的管理工具

当然,也有缺点:

·需要安装SQL Server,对小型应用来说不合适;

·重新构造数据的性能和读取数据库的性能比较;

·网络负担。

2.5             使用静态变量缓存
静态变量常用来记录类的状态,你可以用它来创建定制的缓存对象。在定制的缓存类中将你的数据存储器声明为静态变量,并且提供维护接口(插入、删除和访问等)。如果没有特殊的缓存需求(比如依赖、失效策略等),使用静态变量缓存数据是很方便的。由于是在内存中,这种方案可提供对缓存数据的直接、高速的访问,当没有替代方案解决键值对的存储且对速度要求很高时,可以使用静态变量。当然,在asp.net中,应该使用Cache对象。

你可以使用这种方案保存大数据的对象,前提是它不经常更改。由于没有清除机制,大数据的内存消耗会影响性能。

你需要保证定制线程安全机制,或者使用.Net框架提供的同步对象,比如Hashtable。以下代码是使用Hashtable实现的例子:

static Hashtable mCacheData = new Hashtable();

应用范围:本方案的应用范围可以限制到类、模块或整个项目。如果变量定义为public,整个项目中的代码都能访问它,范围是整个应用程序域,实现了高效的共享。而它的生存期是和范围紧密相关的。

2.6             使用asp.net session state
你可以使用基于HttpSessionState对象的asp.net session state来缓存单个用户的会话状态信息。它解决了asp中会话状态的很多限制,包括:

·asp session要求客户端接受cookies,否则就不能使用session;而asp.net可以配置为不使用cookie;

·对web server场的情况,asp的session不能支持;当稳定性和可用性要求很高时,asp.net session state虽然效果不好,但对比较小的单个值scalar Value(比如登录信息),还是很有效。

Asp.net session有很大改进,下面描述使用范围和使用方式。

Asp.net session state有三种操作模式:

1、  进程内模式InProc——Session State信息在asp.net工作进程aspnet_wp.exe的进程的内存中存储。这是默认选项,这种情况下,如果进程或应用程序域被回收,则Session 状态信息也被回收;

2、   进程外模式State Server——状态信息序列化后保存在独立的状态进程中(AspNet_State.exe),所以状态信息可以保存在专门的服务器上(一个状态服务器State Server);

3、   Sql server模式——状态信息序列化后保存在SQL Server数据库中。

你可以通过调整配置文件中<sessionState>标签的mode属性来设置要使用的状态模式,比如使用SQL Server模式来在Web server场中共享状态信息。当然,这个优势也有缺点,就是状态信息需要序列化和反序列化,同时多了对数据库的写入和读取,所以性能上有开销,这是要仔细评估的。

2.6.1  选择使用模式
2.6.1.1 使用InProc模式
当使用进程内模式时,状态信息保存在aspnet_wp.exe的进程中。由于在web场的情况下aspnet_wp.exe的多个实例在同一台服务器上运行,所以进程内模式不适用与web场的情况。

进程内模式是唯一支持Session_End事件的session模式,当用户会话超时或中止时,可以运行Session_End中的事件处理代码来清除资源。

2.6.1.2 使用StateServer模式
StateServer模式使用指定的进程储存状态信息。因为它也是一种进程外模式,所以要保证你存储的对象是可序列化的,以支持跨进程传输。

当使用Session对象在web场的情况下使用时,必须保证web.config文件中的<MachineKey>元素在所有服务器上是唯一的。这样所有的服务器使用同样的加密方式,才能访问缓存中的数据。参考msdn中的“MachineKey元素”。

2.6.1.3 使用SQL Server模式
SQL Server模式下,当你使用信任连接(trusted_connection=true 或 integrated security=sspi)访问Session state信息时,不能在asp.net中使用身份用户模拟。

默认情况下,SQL Server将状态信息存储在TempDb数据库中,它在每次Sql server服务启动时会自动重新创建,当然,你可以指定自己的数据库以便在数据库重启的过程中也能保持数据。

2.6.2  决定使用Session对象要存储的内容
你可以使用Session对象缓存任何类型的.net框架数据,但是要了解对某种类型来说最好的方式是什么。有以下几点需要说明:

1、  对基本类型(比如Int,Byte,String)来说,可以使用任何方式。因为在选用进程外方式时,asp.net使用一个优化的内部方法来序列化和反序列化基本类型的数据;

2、  对复杂类型(如ArrayList)来说,只选用进程内方式。因为asp.net使用BinaryFormatter来序列化和反序列化这类数据,而这会影响性能的。当然,只有在State Server和SQL Server的方式下,才会进行序列化操作;

3、  缓存的安全问题,当在缓存中存储敏感数据时,需要考虑安全性,其它页面可以访问到缓存中的数据;

4、  避免缓存大数据,那会降低性能;

5、  这种缓存方式不支持过期策略、清除和依赖。

2.6.3  实现Session State
Asp.net提供了简单接口来操作Session State,并可使用Web.Config进行简单设置,当配置文件中的设置改变时,能够在页面上立刻体现出来,而不需要重新启动asp.net进程。

以下代码演示了使用SQL Server来实现Session数据的存储和使用。

<sessionState

mode="SQLServer"

stateConnectionString="tcpip=127.0.0.1:42424"

sqlConnectionString="data source=127.0.0.1; Integrated Security=SSPI"

cookieless="false"

timeout="20"

/>

private void SaveSession(string CartID)

{

Session["ShoppingCartID"] = CartID;

}

private void CheckOut()

{

string CartID = (string)Session["ShoppingCartID"];

if(CartID != null)

{

// Transfer execution to payment page.

Server.Transfer("Payment.aspx");

}

else

{

// Display error message.

}

}

2.7             使用Asp.net客户端缓存和状态
你还可以使用客户端存储页面信息的方式来降低服务器的负担,这种方法提供最低的安全保障,但却有最快的性能表现。由于需要将数据发送到客户端存储,所以数据量有限。

实现客户端缓存的机制有以下五种,接下来将依次介绍:

·隐藏栏位(Hidden Field)

·View State

·隐藏帧(Hidden Frame)

·Cookies

·Query String

这五种方式分别适合于存储不同类型的数据。

2.7.1  使用Hidden Field
你可以将经常改变的少量数据保存在HtmlInputHidden中来维护页面的状态。当每次页面回送的过程中,这些数据都会包含在表单中大送到服务器,所以你要使用HTTP POST方式来提交页面。

使用这种方式的优点如下:

不需要服务器资源,直接从页面中读取; 
几乎所有的浏览器都支持; 
实现简单; 
由于数据在页面中,所以在web Farm的情况下也可使用。
缺点:

由于可以通过查看源码看到,所以可能会被篡改; 
不支持复杂格式的数据,复杂数据必须使用解析字符串的方式来间接得到; 
当存储大数据的时候会影响性能。
示例:

<input id="HiddenValue" type="hidden" value="Initial Value" runat="server" NAME="HiddenValue">

2.7.2  使用View State
所有的Web Form页面和控件都包含有一个ViewState属性,在对同一页面多次请求时可以保持页面内的值。它的内部实现是维护相应的hidden field,只不过是加密了的,所以比hidden field的安全性要好。

使用View State的性能表现很大程度上依赖于服务器控件的类型。一般来说,Label,TextBox,CheckBox,RadioButton,HyperLink的性能要好一些,而DropdownList,ListBox,DataGrid和DataList就要差很多,因为包含的数据量太大,所以每次页面回送都很耗时间。

有些情况下不推荐使用ViewState,比如:

1、  不需要回送的页面避免使用;

2、  避免使用ViewState保存大数据量;

3、  在需要使用会话超时的情况下避免使用它,因为它没有超时操作。

ViewState的性能表现和Hidden Field的是类似的,但是具有更高的安全性。

优点:

数据在页面中自动维护,不需要服务器资源; 
实现简单; 
数据是经过加密和压缩的,比hidden field有更高的安全性; 
数据存在客户端,可以在Web Farm情况下使用。
缺点:

存储大数据量时会降低性能; 
和hidden field类似,在客户端数据仍然有潜在的安全威胁。
示例代码如下:

public class ViewStateSample : System.Web.UI.Page

{

private void Page_Load(object sender, System.EventArgs e)

{

if (!Page.IsPostBack)

{

// Save some data in the ViewState property.

this.ViewState["EnterTime"] = DateTime.Now.ToString();

this.ViewState["UserName"] = "John Smith";

this.ViewState["Country"] = "USA";

}

}



private void btnRefresh_Click(object sender, System.EventArgs e)

{

// Get the saved data in the view state and display it.

this.lblTime.Text = this.ViewState["EnterTime"].ToString();

this.lblUserName.Text = this.ViewState["UserName"].ToString();

this.lblCountry.Text = this.ViewState["Country"].ToString();

}

}

2.7.3  使用Hidden Frame
你可以使用Hidden Frame在客户端缓存数据,这就避免了使用hidden field和使用view state时每次页面回送时的缓存数据往返。比如你可以秘密的加载多个页面所需要的图片,这并不会消耗服务器资源。

优点:

a.     可以加载较多数据而不只是单个栏位的值;

b.     避免了不必要的多次回送中的数据往来;

c.     可以缓存和读取在不同表单中存储的数据项(可以同时缓存多个页面的数据);

d.     可以访问同一站点不同frame中的客户端脚本数据。

缺点:

a.     有些浏览器不支持frame;

b.     源代码可以在客户端看到,有潜在的安全威胁;

c.     隐藏frame的数量没有限制,如果框架页面包含较多hidden frame的话,在首次加载时速度会有限制。

示例代码如下:

<FRAMESET cols="100%,*">

<FRAMESET rows="100%,*">

<FRAME src="contents_of_frame1.html">

</FRAMESET>

<FRAME src="contents_of_hidden_frame.html">

<FRAME src="contents_of_hidden_frame.html" frameborder="0" noresize scrolling="yes">

<NOFRAMES>

<P>This frameset document contains:

<A href="contents_of_frame1.html" TARGET="_top">Some neat contents</A>

</NOFRAMES>

</FRAMESET>

2.7.4  使用Cookies
Cookie是可以在客户端存储数据另一种方案,这里不过多介绍。

优点:

不需要服务器资源;数据保存在客户端,在用户请求时发送到服务器上。 
使用简单。Cookie包含简单的键值对,主要保存轻量级的文本数据。 
支持过期策略;可以指定当会话结束时过期,也可指定一个时间策略。
缺点:

数据量的限制; 
用户可能设置为拒绝Cookie; 
安全问题;用户可能更改机器上的cookie信息,造成基于cookie的系统运行失败; 
可能过期或被用户删除,造成一定程度的不可用。
参看示例代码:

public class CookiesSample : System.Web.UI.Page

{

private void Page_Load(object sender, System.EventArgs e)

{

if (this.Request.Cookies["preferences1"] == null)

{

HttpCookie cookie = new HttpCookie("preferences1");

cookie.Values.Add("ForeColor","black");

cookie.Values.Add("BackColor","beige");

cookie.Values.Add("FontSize","8pt");

cookie.Values.Add("FontName","Verdana");

this.Response.AppendCookie(cookie);

}

}

private string getStyle(string key)

{

string val = null;

HttpCookie cookie= this.Request.Cookies["preferences1"];

if (cookie != null)

{

val = cookie.Values[key];

}

return val;

}

}

2.7.5  使用Query String
Query String是在用户请求的URL后加上相应的参数来使用的,只能在使用HTTP GET方式调用URL时可用。

优点:

d.     不需要服务器资源,参数附在URL里面;

e.     应用面广,几乎所有浏览器都支持;

f.     实现简单,服务端使用Request对象可直接读取。

缺点:

a.     参数直接对用户可见,不安全;

b.     URL长度的限制,多数浏览器不支持超过255字符的URL。

示例代码:

http://www.cache.com/login.asp?user=ronen

string user = Request.QueryString["User"];

2.7.6  小结
下表是使用客户端缓存的建议:

缓存机制
 适用情况
 
Hidden Field
 当安全性要求不高时,在页面中存储少量数据以提交到服务器上的本页面或其它页面。
 
ViewState
 在单个页面中存储少量信息满足页面多次回传的要求。提供基本的安全机制。
 
Hidden Frame
 在客户端存储数据,避免了数据到服务器的回传。
 
Cookie
 当安全性要求不高时,存储少量数据在客户端。
 
Query String
 当使用页面地址连接页面时传输少量参数。
 

 

2.8             使用Internet Explorer缓存
IE提供了缓存机制,可以实现对页面的数据进行缓存,同时可以指定过期时间。用户在IE中请求此页面,如果当过期时间没有到,则自动从缓存中提取并呈现;否则,就到服务器上获取新版本。IE对页面的缓存可以在IIS中设置。

适合在Internet Explorer中缓存的内容

页面中的图像文件; 
静态的文本内容; 
页面的标题栏和页脚内容——改变频率很低,可以给用户一个迅速相应; 
网站的首页——更改次数页时相对较少的; 
使用动态HTML在客户端保存的特定数据。比如客户自定义的颜色和布局设置信息。
优点:

减少对服务器的请求和网络负担; 
支持离线浏览; 
可以实现基于XML的客户端复杂应用。
缺点:

客户端的过期时间必须预先指定而不能依赖于服务器更新;IE采用的是Lazy更新机制,优先从缓存中提取数据; 
对其它客户端浏览器没有作用; 
存储的数据没有加密,不能保证客户端数据安全。
示例代码:

<META HTTP-EQUIV="expires" CONTENT="Tue, 23 Jun 2002 01:46:05 GMT">

3         总结
本文档介绍了缓存和状态数据存储的相关概念,以及可供使用的缓存技术,介绍了各种技术的适用范围,并对其优缺点进行了说明,另外有简单的性能比较和简单的示例代码。更多内容请参看相应的参考资料。
四月 12 2007

IIS 7.0

【原文地址】IIS 7.0
【原文发表日期】 Monday, April 02, 2007 11:10 PM

IIS 7.0是我的团队今年稍后将推出的最让我激动不已的产品之一。它是自IIS 1.0之后我们所做过的最重大的web服务器发布,它将为管理人员和开发人员引入不计其数的改进。

IIS开发团队的Mike Volodarsky为MSDN杂志的2007年3月期撰写了一篇精彩的文章 (英文版),总结了IIS 7.0的一些主要改进。我强烈建议你去这里 (英文版)阅读一下他的精彩文章,以对这些改进有个简短了解。

IIS 7.0是包括在Windows Vista客户机上的,该操作系统的家庭版本也带有IIS 7.0(而不象IIS 5.1,只有在XP Professional上才有)。服务器的IIS 7.0版本将在今年稍后随Windows Longhorn服务器发布,将添加一堆额外的部署特性,包括更加丰富的主机支持,安全的FTP支持,以及内置的web farm部署支持等。

Web farm支持将是特别地酷,它将允许你在一个包含了运行一个服务器所需的所有编码,配置,内容和密钥的文件共享上部署你的web应用。然后你可以添加任意数目的无状态,无配置的web服务器到一个web farm上,只需将它们指向那个文件共享,来动态装载它们的配置设置(包括绑定,虚拟目录,应用池设置等等)和应用内容即可。这使得在多个机器上扩缩一个应用简直是小菜一碟,可避免使用复制方法来做配置和应用部署(只要把文件拷贝到文件共享上,web farm里的所有机器就会马上装载变动过的文件)。

即将推出Windows Longhorn服务器的Beta3版本将会支持go-live许可,所以你不久就能利用这个功能。我们已经在用IIS 7.0集群运行 www.Microsoft.com 了,所以你不会寂寞的!(【译注:在本文的中文版发表之前,Windows Longhorn服务器的Beta3版本已经发布】)

ASP.NET和IIS 7.0之集成

在早期的IIS版本中,开发人员需要编写ISAPI扩展/过滤器来扩展服务器的功能。除了写起来非常痛苦外,ISAPI在如何接入服务器以及允许开发人员定制方面也是非常有限。譬如,你无法在ISAPI扩展中实现URL重写代码(注:ASP.NET是以ISAPI扩展的方式实现的)。假如你把运行时间长的代码编写成ISAPI过滤器的话,结果是你将占用web服务器的I/O线程(这就是我们不让托管代码在请求的过滤器执行阶段运行的原因)。

我们在IIS7中对核心IIS处理引擎做的一个重大的架构级变动是通过一个新的模块化的请求管道架构来促成极其丰富的扩展性。你现在可以通过与web服务器注册一个HTTP扩展性模块(HTTP Extensibility Module),在任意一个HTTP请求的生命周期的任何地方编写代码。这些扩展性模块可以使用native的C++代码或.NET托管代码来编写(你可以使用现有的ASP.NET System.Web.IHttpModule接口来实现)。

所有“内置”的IIS7功能(认证,授权,静态文件供应,目录清单支持,经典的ASP,记录日志等),现在都是使用这个公开的模块化的管道API来实现的。这意味着你可以除去这些IIS7“内置”功能的任意一个,而以你自己的实现来替换/扩展这些功能。

IIS 7.0上的ASP.NET本身也从以ISAPI的实现形式变成直接接入IIS7管道的模块:

这带来诸多好处:

1) 你现在可以对服务器的所有请求(譬如, .htm,.php,.jsp文件)使用ASP.NET表单认证,成员/角色,以及任何其他特性。

2) 你现在可以轻松地重写任何web请求的URL或者以种种有趣的方式对请求做改动。

3) 你可以使用VB或C#替换或扩展任何现有的IIS特性(譬如,你可以除去内置的目录清单模块,接入你自己的模块)。

这确实给.NET开发人员带来了无穷多的扩展性机会。

IIS 7.0 下载中心

为帮助开发人员共享他们编写的扩展性模块和其他add-in,IIS开发团队最近在www.iis.net上发起了一个 “下载中心”,这允许开发人员浏览/下载以及上传和共享IIS的模块扩展。你可以在这里查看一下。

注意,除了让你编写托管的HTTP模块的扩展性功能外,IIS7现在也允许你编写托管的管理工具的UI扩展(管理工具本身是使用 Windows Forms编写的),以及使用.NET的System.Configuration 命名空间来管理IIS7配置系统。

可为ASP.NET开发人员所用的酷场景

除了IIS 7.0提供的既酷又新的扩展性选项外,还有ASP.NET开发人员将会非常欣赏的成堆的大大小小的改进。我将在接下来的几个星期/月份里在博客里讨论一系列的改进,指出你能利用的一些非常酷的东西。

我也强烈建议你订阅IIS 7开发团队在这里的博客feed。

希望本文对你有所帮助,

Scott