Dennis’s Weblog

Archive for the ‘Web Design’ Category

  • In: Web Design
  • Enter your password to view comments.

This content is password protected. To view it please enter your password below:

Caching Architecture Guide for .NET Framework Applications

patterns and practices home

Understanding Caching Technologies

Summary: This chapter discusses the various caching technologies available in distributed .NET applications, and details the benefits and limitations of each method.

Chapter 1 introduced you to the different types of state that can be used within a distributed .NET-based application and provided an overview of why and where to cache some of this state. You also learned about some of the issues you must bear in mind when you design the caching policy for your applications.

This chapter describes the different caching technologies you can use when you build distributed enterprise applications using the Microsoft .NET Framework. Other technologies can be used to cache data, such as NTFS file system caching, Active Directory® directory service caching, and the COM+ Shared Property Manager. However, in most cases, these methods are not recommended and are not described in this chapter.

This chapter describes the different caching technologies and Chapter 3, “Caching in Distributed Applications,” describes how to select the right technology to fit your needs.

This chapter contains the following sections:

Using the ASP.NET Cache

Storing frequently used data in memory is not a new concept for ASP developers. ASP offers the Session and Application objects, which enable storing key/value pairs in memory. The Session object is used to store per-user data across multiple requests, and the Application object is used to store per-application data for use by requests from multiple users.

ASP.NET introduces a new key/value pair object—the Cache object. The scope of the ASP.NET cache is the application domain; therefore, you cannot access it from other application domains or processes. The ASP.NET Cache object’s life span is tied to the application, and the Cache object is re-created every time the application restarts, similar to the ASP Application object. The main difference between the Cache and Application objects is that the Cache object provides cache-specific features, such as dependencies and expiration policies.

You can programmatically access the Cache object and work with it using its properties and methods. This allows you full access to the object and the ability to use advanced caching features. You can also use the ASP.NET cache to store output responses that are transmitted to a Web browser.

Using Programmatic Caching

The Cache object is defined in the System.Web.Caching namespace. You can get a reference to the Cache object by using the Cache property of the HttpContext class in the System.Web namespace or by using the Cache property of the Page object. You can also access cached information in a user control through the Cache property of the UserControl class. This is useful particularly when you need to access the cache directly from a user control. In addition to simply storing key/value pairs, the Cache object provides additional functionality specifically designed to store transient data, such as .NET Framework objects. Cache features, such as dependencies and expiration policies, also extend the capabilities of the ASP.NET cache.

Using Dependencies and Expirations

When you add an item to the cache, you can define dependency relationships that can force that item to be removed from the cache under specific circumstances. The supported dependencies include the following:

  • File dependency—Allows you to invalidate a specific cache item when a disk-based file or files change.The following example shows how to specify a file dependency when adding an item to the cache.
    CacheDependency cDependency = new 
    Cache.Insert("CachedItem", item, cDependency);
  • Key dependency—Invalidates a specific cache item when another cached item changes. For example, when you cache basic data alongside calculation results on that data, the calculated data should be invalidated when the basic data changes or becomes invalid. As another example, although you cannot directly remove a page from the ASP.NET output cache, you can use a key dependency on the page as a workaround. When the key on which your pages are dependent is removed from cache, your cached pages are removed as well.The following example shows how to make one cache item dependent on another.
    // 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);
  • Time-based expiration—Invalidates a specific cached item at a predefined time. The time for invalidation can be absolute—such as Monday, December 1, at 18:00—or sliding, which resets the time to expire relative to the current time whenever the cached item is accessed. Examples of both types of time dependency are as follows.
    /// Absolute expiration
    Cache.Insert("CachedItem", item, null, DateTime.Now.AddSeconds(5), 
    /// Sliding expiration
    Cache.Insert("CachedItem", item, null, Cache.NoAbsoluteExpiration, 

    Using a duration that’s too short limits a cache’s usefulness; using a duration that’s too long may return stale data and unnecessarily load the cache with pages that are not requested. If using page output caching does not improve performance, your cache duration might be too short. Try fast concurrent page requests to see whether pages are served rapidly from cache.

    If you do not have a mechanism, such as dependencies, to invalidate a cached page, keep the duration short. In general, keep your cache duration shorter than the update interval of the data in your page. Usually, if you receive pages that are out of date, your cache duration is too long.

A common question is how to implement a cache database dependency. This requires you to implement a notification mechanism that informs your cache of changes to the original data in the database. For information about using external notifications, see Chapter 5, “Managing the Contents of a Cache.” For a downloadable code sample that shows how to programmatically implement database dependencies, see Rob Howard’s team page.

When you need to access a cached item, you must check whether the item is in the cache before you try to use it. Because the ASP.NET cache invalidates cached items based on dependencies, time, or resources, you must always write code to create or retrieve the item you need if it does not currently exist in the cache.

The following example shows how you can do this.

string data = (string)Cache["MyItem"];
if (data == null)
   data = GetData();
   Cache.Insert("MyItem", data);

Check whether data in the cache is valid before using it to avoid using stale items. Use this method when the cost of validating a cached item is significantly less than the cost of regenerating or obtaining the data.

Items added to the cache automatically expire and are removed from the cache if they are not used for a given amount of time. Use the expiration cache feature when the data from which the cached items generate is updated at known intervals.

Dependencies and expirations initiate the removal of items from a cache. Sometimes you want to write code to be called when the removal occurs.

Using Cache Callbacks

You can add a callback method to a cached item to execute when that item is removed from the cache. You can implement such a callback to ensure that the cached item is not removed from the cache or that the cache is updated based on new data.

The following example shows how to define a callback function when adding an item to the cache.

CacheItemRemovedCallback onRemove = new 

// 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);

Notice that in addition to expiration parameters, the Insert method also uses a CacheItemPriority enumeration.

Applying Priority to Cache Items

When the server running your ASP.NET application runs low on memory resources, items are removed from cache to free memory in a process known as scavenging. When memory is low, the cache determines which items are removed from cache based on priority. You can set the cache item priority when you add the item to the cache. Doing so controls which items scavenging removes first.

For more information about cache item priorities, see “CacheItemPriority Enumeration,” in the MSDN Library.

Flushing a Cache

There is no direct support for automatic flushing of the ASP.NET output cache. One way to do so is to set an external dependency that clears all cached items automatically. For example, the following statement flushes the ASP.NET output cache as soon as it executes.


This code flushes the output cache, but the page does not reflect this until the original cache duration completes. For example, if you use the following directive to configure your cache, the cache resets after 10 seconds.

<%@ OutputCache Duration="10" VaryByParam="none" %>

Flushing the entire output cache is generally not required, and better alternatives are available to your application design. For example, when using the ASP.NET Cache object, you can reload your cache items when they become stale, overwriting the existing cache content.

Using an Output Cache

You can use two types of output caching to cache information that is to be transmitted to and displayed in a Web browser: page output caching and page fragment caching. Page output caching caches an entire Web page and is suitable only when the content of that page is fairly static. If parts of the page are changing, you can wrap the static sections as user controls and cache the user controls using page fragment caching.

Using Page Output Caching

Page output caching adds the response of a given request to the ASP.NET cache object. After the page is cached, future requests for that page return the cached information instead of re-creating the entire page. You can implement page output caching by adding the necessary page directives (high-level, declarative implementation) or by using the HTTPCachePolicy class in the System.Web namespace (low-level, programmatic implementation).

This guide does not discuss the technical details of how page output caching works but concentrates on guidelines and best practices of how to use it correctly. For more information about how to implement page output caching, see “Page Output Caching,” in the MSDN Library.

Determining What to Cache with Page Output Caching

You can use the page output cache to cache a variety of information, including:

  • Static pages that do not change often and that are frequently requested by clients.
  • Pages with a known update frequency. For example, a page that displays a stock price where that price is updated at given intervals.
  • Pages that have several possible outputs based on HTTP parameters, and those possible outputs do not often change—for example, a page that displays weather data based on country and city parameters.
  • Results being returned from Web services. The following example shows how you can declaratively cache these results.
    public string HelloWorld()
       return "Hello World";
  • Caching these types of output avoids the need to frequently process the same page or results.
  • Pages with content that varies—for example, based on HTTP parameters—are classed as dynamic, but they can still be stored in the page output cache. This is particularly useful when the range of outputs is small.

Caching Dynamic Pages

You may find yourself designing Web pages that contain dynamic content that is dependent upon input parameters, language, or browser type. ASP.NET lets you cache different versions of these pages based on changing data. By using applicable attributes on dynamically generated pages, you optimize cache usage and get better cache duration control.

You can use the following OutputCache attributes to implement this functionality:

  • VaryByParam—Lets you cache different versions of the same page based on the input parameters sent through the HTTP GET/POST.
  • VaryByHeader—Lets you cache different versions of the page based on the contents of the page header.
  • VaryByCustom—Lets you customize the way the cache handles page variations by declaring the attribute and overriding the GetVaryByCustomString handler.
  • VaryByControl—Lets you cache different versions of a user control based on the value of properties of ASP objects in the control.

The more specific you are in setting the values of these attributes—for example, supplying more of the HTTP parameters—the better the cache is used because it contains only relevant data. In essence, you are insulating what you are caching from changes in the underlying data. However, as the values become more specific, the cache uses memory less efficiently because the cache keeps more versions of the same page. The ASP.NET cache can erase pages by scavenging when memory becomes low.

Table 2.1 compares response times as the number of cached page versions increases.

Table 2.1: Caching performance

Pages cached Responses/sec. Time to first byte Time to last byte
Less than 1,000 15.37 124.73 643.43
More than 1,200 3.15 2,773.2 3,153.63
Note These figures are based on sample pages of 250 KB.

For more information about the caching attributes in ASP.NET cache, see “Page Output Caching” and “Caching Multiple Versions of a Page,” in the MSDN Library.

Configuring the Output Cache Location

You can control the location of your output cache by using the Location attribute of the @OutputCache directive. The Location attribute is supported only for page output caching and not for page fragment caching. You can use it to locate the cache on the originating server, the client, or a proxy server. For more information about the Location attribute, see “OutputCacheLocation Enumeration,” in the MSDN Library.

The location of the cache is specified when you are configuring the cache for your application.

Configuring Page Output Caching

You can configure the cache both declaratively (using page directives) and programmatically (using the cache API). The declarative method can meet most of common requirements for configuring a cache, but the cache API includes some additional functionality, such as the ability to register callbacks.

The following example shows how to declaratively configure the cache using page directives in the ASP.NET page header.

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

The following example shows how to programmatically configure the cache using the cache API.

private void Page_Load(object sender, System.EventArgs e)
        // Enable page output caching.
       // Set the Duration parameter to 20 seconds.
       // 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'.

Both of these examples result in the same cache configuration.

Using Page Fragment Caching

Page fragment caching involves the caching of a fragment of the page, as opposed to the entire page. Sometimes full page output caching is not feasible—for example, when portions of the page need to be dynamically created for each user request. In such cases, it can be worthwhile to identify portions of the page or controls that do not often change and that take considerable time and server resources to create. After you identify these portions, you can wrap them in a Web Forms user control and cache the control so that these portions of the page don’t need to be recreated each time.

You can implement page fragment caching by adding the necessary directives (high-level, declarative implementation) to the user control or by using metadata attributes in the user control class declaration.

For more information about page fragment caching, see “Caching Portions of an ASP.NET Page,” in the MSDN Library.

Determining What to Cache with Page Fragment Caching

Use page fragment caching when you cannot cache the entire Web page. There are many situations that can benefit from page fragment caching, including:

  • Page fragments (controls) that require high server resources to create.
  • Sections on a page that contain static data.
  • Page fragments that can be used more than once by multiple users.
  • Page fragments that multiple pages share, such as menu systems.
Note ASP.NET version 1.1, part of the Microsoft Visual Studio® .NET 2003 development system, introduces a new Shared attribute in the user control’s <%@ OutputCache %> directive. This attribute allows multiple pages to share a single instance of a cached user control. If you don’t specify the Shared attribute, each page gets its own instance of the cached control.

Configuring Page Fragment Caching

The following example shows how to implement fragment caching in a Web user control.

// Partial caching for 120 seconds
public class WebUserControl : System.Web.UI.UserControl
   // Your Web control code

When the page containing the user control is requested, only the user control—not the entire page—is cached.

Using the ASP.NET Cache in Non-Web Applications

The ASP.NET cache object is located in the System.Web namespace, and because it is a generic cache implementation, it can be used in any application that references this namespace.

The System.Web.Caching.Cache class is a cache of objects. It is accessed either through the static property System.Web.HttpRuntime.Cache or through the helper instance properties System.Web.UI.Page and System.Web.HttpContext.Cache. It is therefore available outside the context of a request. There is only one instance of this object throughout an entire application domain, so the HttpRuntime.Cache object can exist in each application domain outside of Aspnet_wp.exe.

The following code shows how you can access the ASP.NET cache object from a generic application.

HttpRuntime httpRT = new HttpRuntime();
Cache cache = HttpRuntime.Cache;

After you access the cache object for the current request, you can use its members in the usual way.

Managing the Cache Object

ASP.NET supports a host of application performance counters that you can use to monitor the cache object activity. Monitoring these performance counters can help you locate and resolve issues in the performance of your ASP.NET cache. Table 2.2 describes these counters.

Table 2.2: Application performance counters for monitoring a cache

Counter Description
Cache Total Entries The number of entries in the cache
Cache Total Hits The number of hits from the cache
Cache Total Misses The number of failed cache requests per application
Cache Total Hit Ratio The ratio of hits to misses for the cache
Cache Total Turnover Rate The number of additions and removals to the total cache per second
Cache API Entries The number of entries in the application (programmatic cache)
Cache API Hits The number of hits from the cache when the cache is accessed through the external cache APIs (programmatic cache)
Cache API Misses The number of failed requests to the cache when the cache is accessed through the external cache APIs (programmatic cache)
Cache API Hit Ratio The cache ratio of hits to misses when the cache is accessed through the external cache APIs (programmatic cache)
Cache API Turnover Rate The number of additions to and removals from the cache per second, when accessed through the external cache APIs (programmatic cache)
Output Cache Entries The number of entries in the output cache
Output Cache Hits The number of requests serviced from the output cache
Output Cache Misses The number of failed output cache requests per application
Output Cache Hit Ratio The percentage of requests serviced from the output cache
Output Cache Turnover Rate The number of additions to and removals from the output cache per second

Note that the Cache API counters do not track internal usage of the cache by ASP.NET. The Cache Total counters track all cache usage.

The Turnover Rate counters help determine how effectively the cache is being used. If the turnover is large, the cache is not being used efficiently because cache items are frequently added and removed from the cache. You can also track cache effectiveness by monitoring the Hit counters.

For more information about these performance counters, see “Performance Counters for ASP.NET,” in the MSDN Library.

Using Remoting Singleton Caching

Microsoft .NET remoting provides a rich and extensible framework for objects executing in different AppDomains, in different processes, and on different computers to communicate seamlessly with each other. Microsoft .NET remoting singleton objects service multiple clients and share data by storing state information between client invocations.

You can use .NET remoting when you need a custom cache that can be shared across processes in one or several computers. To do this, you must implement a caching service using a singleton object that serves multiple clients using .NET remoting.

To implement a cache mechanism using .NET remoting, ensure that the remote object lease does not expire and that the remoting object is not destroyed by the garbage collector. An object lease is the period of time that a particular object can remain in memory before the .NET remoting system begins to delete it and reclaim the memory. When implementing a remoting singleton cache, override the InitializeLifetimeService method of MarshalByRefObject to return null. Doing so ensures that the lease never times out and the object associated with it has an infinite lifetime. The following example shows how to cause your object to have infinite lifetime.

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

This code ensures that the garbage collector does not collect the object by returning a null lifetime manager for the object.

You can implement the remoting singleton as a caching mechanism in all layers of a multilayer architecture. However, because of the relatively high development cost of its implementation, you’re most likely to choose this solution when a custom cache is needed in the application and data tiers.

You can often use solutions based on Microsoft SQL Server™ instead of remoting singleton caching solutions. It is tempting to choose remoting singleton caching because it is simple to implement, but the poor performance and the lack of scalability it produces mean that this choice is often misguided.

Using Memory-Mapped Files

Memory-mapped files offer a unique memory management feature that allows applications to use pointers to access files on disk—the same way that applications access dynamic memory. With this capability, you can map a view of all or part of a file on disk to a specific range of addresses in your process’s address space. After you do so, accessing the content of a memory-mapped file is as simple as dereferencing a pointer in the designated range of addresses.

Both code and data are treated the same way in Windows: Both are represented by pages of memory, and both have those pages backed by a file on disk. The only difference is the type of file that backs them. Code is backed by the executable image, and data is backed by the system pagefile.

Because of the way that memory-mapped files function, they can also provide a mechanism for sharing data between processes. By extending the memory-mapped file capability to include portions of the system pagefile, applications are able to share data that is backed by that pagefile. Each application simply maps a view of the same portion of the pagefile, making the same pages of memory available to each application. Because all processes effectively share the same memory, application performance increases.

You can use these unique capabilities of memory-mapped files to develop an efficient custom cache that can be shared across multiple application domains and processes within the same computer. A custom cache based on memory-mapped files includes the following components:

  • Windows NT service—Creates the memory-mapped file when it is started and deletes the memory-mapped file when it is stopped. Because each process using the cache needs a pointer to the memory-mapped file, you need a Windows NT® service to create the memory-mapped file and to pass handles to processes that want to use the cache. Alternatively, you can use a named memory-mapped file and get a handle to the memory-mapped file for each process by using the memory-mapped file name.
  • Cache management DLL—Implements the specific cache functionality, such as:
    • Inserting and removing items from the cache.
    • Flushing the cache using algorithms such as Least Recently Used (LRU) and scavenging. For more information about flushing, see Chapter 5, “Managing the Contents of a Cache.”
    • Validating data to protect it against tampering or spoofing. For more information about security, see Chapter 6, “Understanding Advanced Caching Issues.”

Because a memory-mapped file is a custom cache, it is not limited to a specific layer or technology in your application.

Memory-mapped file caches are not easy to implement because they require the use of complex Win32® application programming interface (API) calls. The .NET Framework does not support memory-mapped files, so any implementations of a memory-mapped file cache run as unmanaged code and do not benefit from any .NET Framework features, including memory management features, such as garbage collection, and security features, such as code access security.

Management functionality for memory-mapped file custom cache needs to be custom developed for your needs. Performance counters can be developed to show cache use, scavenging rate, hit-to-miss ratios, and so on.

Using Microsoft SQL Server 2000 or MSDE for Caching

You can use Microsoft SQL Server 2000, and its scaled down version, Microsoft SQL Server 2000 Desktop Engine (MSDE), to cache large amounts of data. Although SQL Server is not the ideal choice when caching small data items because of its relatively slow performance, it can be a powerful choice when data persistency is critical or when you need to cache a very large amount of data.

Note In caching terms, SQL Server 2000 and MSDE provide the same capabilities. All references to SQL Server in this section equally apply to MSDE.

If your application requires cached data to persist across process recycles, reboots, and power failures, in-memory cache is not an option. In such cases, you can use a caching mechanism based on a persistent data store, such as SQL Server or the NTFS file system. It also makes sense to use SQL Server to cache smaller data items to gain persistency.

SQL Server 2000 limits you to 8 KB of data when you are accessing VarChar and VarBinary fields using Transact-SQL or stored procedures. If you need to store larger items in your cache, use an ADO.NET SQLDataAdapter object to access DataSet and DataRow objects.

SQL Server caching is easy to implement by using ADO.NET and the .NET Framework, and it provides a common development model to use with your existing data access components. It provides a robust security model that includes user authentication and authorization and can easily be configured to work across a Web farm using SQL Server replication.

Because the cache service needs to access SQL Server over a network and the data is retrieved using database queries, the data access is relatively slow. Carefully compare the cost of recreating the data versus retrieving it from the database.

SQL Server caching can be useful when you require a persistent cache for large data items and you do not require very fast data retrieval. Carefully consider how much memory you have and how big your data items are. Do not use all of your memory to cache a few large data items because doing so degrades overall server performance. In such cases, caching items in SQL Server can be a good practice. Remember, though, that implementing a SQL Server caching mechanism requires installing, managing, and licensing at least one copy of SQL Server or MSDE.

When you use SQL Server for caching, you can use the SQL Server management tools, which include a host of performance counters that can be used to monitor cache activity. For example, use the SQLServer:Databases counters on your cache database table to monitor your cache performance, and use counters such as Transactions/sec and Data File(s) Size (KB) to monitor usage.

Using Static Variables for Caching

Static variables are often used to save class state, and you can use them to create customized cache objects. To do so, declare your cache data store as a static variable in your custom-developed cache class, and implement interfaces to insert, remove, and access items in the data store.

Using static variables is simple if no special cache capabilities are needed, such as dependencies, expiration, and scavenging. Because the cache is an in-memory system, static variables provide fast, direct access to the cached data. Static variable caching is useful to store state when other mechanisms are not available or are too costly. Use a static variable cache when you do not have another key/value dictionary available or when you need a fast in-process caching mechanism. In ASP.NET applications, use the Cache object instead.

You can use static variables to cache large data items, provided that the items do not often change. Because there is no mechanism for invalidating items, obsolete large items can waste valuable memory.

When using a static variable cache, you must ensure that you either implement your own thread safety mechanism or use a .NET Framework object that can be synchronized, for example, a Hashtable object.

The following example shows how to implement a static variable cache by using a hash table.

static Hashtable mCacheData = new Hashtable();

The scope of the static variables can be limited to a class or a module or be defined with project-level scope. If the variable is defined as public in your class, it can be accessed from anywhere in your project code, and the cache will be available throughout the application domain. The lifetime of static variables is directly linked to their scope.

Using ASP.NET Session State

You can use ASP.NET session state (based on the HttpSessionState class) to cache per-user session state. It solves many of the limitations that are inherent in ASP session state, including:

  • The ASP session state is tied to the ASP hosting process, and as such is recycled when the ASP process is recycled. You can configure ASP.NET session state to avoid this.
  • ASP session state has no solution for functioning in a Web server farm. The ASP.NET session state solution does not provide the best results when scalability and availability are important, but it works well for small scalar values, such as authentication information.
  • ASP session state depends on the client browser accepting cookies. If cookies are disabled in the client browser, session state cannot be used. ASP.NET session state can be configured not to use cookies.

The ASP.NET session state is much improved. This section describes how and where session state is best used.

ASP.NET session state has three modes of operation:

  • InProc—Session state is held in the memory space of the Aspnet_wp.exe process. This is the default setting. In this setting, the session state is recycled if the process or the application domain is recycled.
  • StateServer—Session state is serialized and stored in a separate process (Aspnet_state.exe); therefore, the state can be stored on a separate computer (a state server).
  • SQLServer—Session state is serialized and stored in a SQL Server database.

You can specify the mode to be used for your application by using the mode attribute of the <sessionState> element of the application configuration file.

The scope of the ASP.NET session state is limited to the user session it is created in. In out-of-process configurations—for example, using StateServer or SQLServer mode in a Web farm configuration—the session state can be shared across all servers in the farm. However, this benefit does come at a cost. Performance may decrease because ASP.NET needs to serialize and deserialize the data and because moving the data over the network takes time.

For more information about ASP.NET session state, see “ASP.NET Session State,” in the MSDN Library.

Choosing the Session State Mode

Each mode of operation, InProc, StateServer, and SQLServer, presents different issues to consider when you design your caching policy.

Using InProc Mode

When you use InProc mode, you are storing the state in the Aspnet_wp.exe process. You cannot use InProc mode in a Web garden environment because multiple instances of Aspnet_wp.exe are running on the same computer.

InProc is the only mode that supports the Session_End event. The Session_End event fires when a user’s session times out or ends. You can write code in this event to clean up server resources.

Using StateServer Mode

StateServer mode stores the state in a dedicated process. Because it is an out-of-process system, you must ensure that the objects you cache in it are serializable to enable cross-process transmission.

When using the Session object for caching in a Web farm environment, ensure that the <machineKey> element in Web.config is identical across all of your Web servers. Doing so guarantees that all computers are using the same encryption formats so that all computers can successfully access the stored data. For more information about encryption, see “<machineKey> Elementin the MSDN Library.

For session state to be maintained across servers in a Web farm, the application path of the Web site (for example, \LM\W3SVC\2) in the Internet Information Services (IIS) metabase must be identical across all servers in that farm. For more information about this, see article Q325056, “PRB: Session State Is Lost in Web Farm If You Use SqlServer or StateServer Session Mode,” in the Microsoft Knowledge Base.

Using SQLServer Mode

In SQLServer mode, ASP.NET serializes the cache items and stores them in a SQL Server database, so you must ensure that the objects you are using are serializable.

If you specify integrated security in the connection string used to access your session state (for example, trusted_connection=true or integrated security=sspi), you cannot use impersonation in ASP.NET. For more information about this problem, see article Q324479, “FIX: ASP.NET SQL Server Session State Impersonation Is Lost Under Load,” in the Microsoft Knowledge Base.

For session state to be maintained across different servers in a Web farm, the application path of the Web site (for example, \LM\W3SVC\2) in the IIS metabase should be identical across all servers in that farm. For details about how to implement this, see article Q325056, “Session State Is Lost in Web Farm If You Use SqlServer or StateServer Session Mode,” in the Microsoft Knowledge Base.

You can configure ASP.NET session state to use SQL Server to store objects. Access to the session variables is slower in this situation than when you use an in-process cache; however, when session data persistency is critical, SQL Server provides a good solution. By default, SQL Server stores session state in the tempdb database, which is re-created after a SQL Server computer reboots. You can configure SQL Server to store session data outside of tempdb so that it is persisted even across reboots. For information about script files that you can use to configure SQL Server, see article Q311209, “Configure ASP.NET for Persistent SQL Server Session State Management,” in the Microsoft Knowledge Base.

When you use session state in SQL Server mode, you can monitor the SQL Server tempdb counters to provide some level of performance monitoring.

Determining What to Cache in the Session Object

You can use the Session object to store any of the .NET Framework data types; however, be aware of which sort of data is best suited to each cache mode.

You can use any mode to store .NET Framework basic types (such as Int, Byte, String) because ASP.NET uses an optimized internal method for serializing and deserializing basic data types when using out-of-process modes.

Store complex types (such as ArrayList) only in the InProc mode, because ASP.NET uses the BinaryFormatter to serialize data, which can affect performance. Note that serialization occurs only in the StateServer and SQLServer out-of-process modes.

For example, the Session object is ideal for storing user authentication data during a user session. Because this information is likely to be stored as a .NET Framework basic type (such as, String), it can use any of the three modes. However, you must consider the security aspects of storing sensitive data in a cache. The data can be accessed from different pages without requiring the user to re-enter logon information.

Try to avoid using session state for large data items because this degrades your application performance. Table 2.3 compares response times as the size of the cache changes.

Table 2.3: Caching performance

Cache size Responses per second Time to first byte Time to last byte
2 KB 447.38 19.43 19.65
200 KB 16.12 667.95 668.89

Because session state can function as both an in-process and an out-of-process cache, it is suitable for a wide range of solutions. It is simple to use, but it does not support data dependencies, expiration, or scavenging.

Implementing Session State

ASP.NET session state provides a simple interface for adding and removing variables and simple configuration using the Web.config file. Changes to this file are reflected immediately without the need to restart the ASP.NET process.

The following example shows how to implement SQLServer mode session caching in the Web.config file.

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

The following example shows how you can use the session object to store and access user credentials.

private void SaveSession(string CartID)
  Session["ShoppingCartID"] = CartID;

private void CheckOut()
  string CartID = (string)Session["ShoppingCartID"];
if(CartID != null)
// Transfer execution to payment page.
    // Display error message.

Remember to secure any credentials when caching them. For more information about securing custom caches, see “Securing a Custom Cache,” in Chapter 6, “Understanding Advanced Caching Issues.”

Using ASP.NET Client-Side Caching and State

You can reduce the workload on your server by storing page information using client-side options. This approach has minimal security support but results in faster server performance because the demand on server resources is modest. Because you must send information to the client before it can be stored, there is a practical limit on how much information you can store this way.

The main mechanisms to implement client-side caching are:

  • Hidden fields
  • View state
  • Hidden frames
  • Cookies
  • Query strings

Each of these mechanisms is useful for caching different types of state in different scenarios.

Using Hidden Fields

You can store page-specific information in a hidden field on your page to maintain the state of that page. For more information about hidden fields, see “Introduction to Web Forms State Management,” in the MSDN Library.

It is best to store only small amounts of frequently changing data in hidden fields because this data is included in the roundtrips to the server on every postback. Storing a large amount of data slows your application because of network traffic load. ASP.NET provides the HtmlInputHidden control, which offers hidden field functionality.

Note If you use hidden fields, you must submit your pages to the server by using the HTTP POST method rather than by requesting the page using the HTTP GET method.

Benefits and Limitations of Hidden Fields

The benefits of using hidden fields to store client-side state include:

  • No server resources are required. The hidden field is stored and read directly from the page.
  • Almost all browsers and client devices support forms with hidden fields.
  • Hidden fields are simple to implement.
  • Hidden fields are good for caching data in Web farm configurations because the data is cached on the client.

The limitations of using hidden fields are:

  • The hidden field can be tampered with. The information in this field can be seen if the page output source is viewed directly, creating a potential security issue.
  • The hidden field does not support rich structures; it offers only a single value field in which to place information. To store multiple values, you must implement delimited strings and write the code required to parse those strings.
  • Page performance decreases when you store large values because hidden fields are stored in the page.

Hidden fields are a good solution for caching small amounts of noncritical, string data for use in Web pages.

Implementing Hidden Fields

The following example shows how to declare a hidden field on a Web page.

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

This code creates a hidden field containing the string data Initial Value.

If you cannot use hidden fields because of the potential security risk, consider using view state instead.

Using View State

Web Forms pages and controls all have a ViewState property, which is a built-in structure for automatically retaining values among multiple requests for the same page. The view state is internally maintained as a hidden field on the page but is hashed, providing greater security than developer-implemented hidden fields do. For more information about view state, see “Introduction to Web Forms State Management,” in the MSDN Library.

You can use view state to store your own page-specific values across roundtrips when the page posts back to itself. For example, if your application is maintaining user-specific information, such as a user name that is displayed in a welcome message, you can store the information in the ViewState property.

Performance of view state varies depending on the type of server control to which it is applied. Label, TextBox, CheckBox, RadioButton, and HyperLink are server controls that perform well with ViewState. DropDownList, ListBox, DataGrid, and DataList suffer from poor performance because of their size and the large amounts of data making roundtrips to the server.

In some situations, using view state is not recommended because it can degrade performance. For example:

  • Avoid using view state in pages that do not post back to the server, such as logon pages.
  • Avoid using view state for large data items, such as DataSets, because doing so increases time in roundtrips to the server.
  • Avoid using view state when session timeout is required because you cannot time out data stored in the ViewState property.

View state is a simple and easy-to-use implementation of hidden fields that provides some security features. Most of the performance considerations, benefits, and limitations of view state and hidden fields are similar.

For more information about ViewState performance, see “Maintaining State in a Control” in the MSDN Library.

Benefits and Limitations of View State

The benefits of using view state include:

  • No server resources are required because state is contained in a structure in the page code.
  • It is simple to implement.
  • Pages and control state are automatically retained.
  • The values in view state are hashed, compressed, and encoded, thus representing a higher state of security than hidden fields.
  • View state is good for caching data in Web farm configurations because the data is cached on the client.

Limitations to using view state include:

  • Page loading and posting performance decreases when large values are stored because view state is stored in the page.
  • Although view state stores data in a hashed format, it can still be tampered with because it is stored in a hidden field on the page. The information in the hidden field can also be seen if the page output source is viewed directly, creating a potential security risk.

For more information about using view state, see “Saving Web Forms Page Values Using View State,” in the MSDN Library.

Implementing View State

The following example shows how to use the ViewState property to store and access user-specific information between page postbacks.

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";

  #region Web Form Designer generated code

  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();

When your application runs on a low bandwidth client—for example, a mobile device—you can store the view state on the server by using Losformatter and thereby returning slimmer pages with less of a security risk.

Using Hidden Frames

You can use hidden frames to cache data on the client, avoiding the roundtrips to the server that are inherent in hidden field and view state implementations. You create a hidden frame in your page that loads a Web page into the frame containing your data. The pages in your application can access this data using client-side scripting. This implementation does not require any server resources because the data fields in the hidden frame are stored and read directly from the page.

Hidden frames let you secretly load images for use on other pages within the site. When those images are required, they can be obtained from the client cache rather than from the server.

Benefits and Limitations of Hidden Frames

The benefits of using hidden frames to store client-side state include:

  • The ability to cache more than one data field.
  • The avoidance of roundtrips of data during postbacks.
  • The ability to cache and access data items stored in different hidden forms.
  • The ability to access JScript® variable values stored in different frames if they come from the same site.

The limitations of using hidden frames are:

  • Functionality of hidden frames is not supported by all browsers. Do not rely on hidden frames to provide data for the essential functionality of your pages.
  • The hidden frame can be tampered with, and the information in the page can be seen if the page output source is viewed directly, creating a potential security threat.
  • There is no limit to the number of frames (embedded or not) that you can hide. In frames that bring data from the database and that contain several Java applets or images, a large number of frames can negatively affect performance of the first page load.

Hidden frames are useful for storing many items of data and resolve some of the performance issues inherent in hidden field implementations.

Implementing Hidden Frames

The following example shows how to declare a hidden frame in a Web page.

<FRAMESET cols="100%,*">
   <FRAMESET rows="100%,*">
      <FRAME src="contents_of_frame1.html">
   <FRAME src="contents_of_hidden_frame.html">
   <FRAME src="contents_of_hidden_frame.html" frameborder="0" noresize 
      <P>This frameset document contains:
               <A href="contents_of_frame1.html" TARGET="_top">
Some neat contents</A>
               <A href="contents_of_hidden_frame.html" TARGET="_top">
Some other neat contents</A>

You can then use the data stored in this frame in your Web page by using client-side scripting.

Using Cookies

You can use cookies to store small amounts of infrequently changing information on the client. That information can then be included in a request to the server.

Benefits and Limitations of Cookies

Cookies are useful because:

  • No server resources are required. The cookie is stored on the client, sent to the server in a request, and then read by the server after the post.
  • They are simple to use. The cookie is a lightweight, text-based structure containing simple key/value pairs.
  • They support configurable expiration. The cookie can expire when the browser session ends, or it can exist indefinitely on the client computer, subject to the expiration rules on the client.

The use of cookies may be limited by:

  • Size restrictions. Most browsers place a 4096-byte limit on the size of a cookie, although support for 8192-byte cookies is becoming more common in the new browser and client-device versions available today.
  • User-configured refusal. Some users disable their browser or client device’s ability to receive cookies, thereby limiting the use of cookies.
  • Security breaches. Cookies can be subject to tampering. Users can manipulate cookies on their computer, which can potentially represent a security compromise or cause a cookie-dependent application to fail.
  • Possible expirations. The durability of the cookie on a client computer is subject to cookie expiration processes on the client and to user intervention.
Note Cookies are often used for personalization, in which content is customized for a known user. In most cases, identification is the issue rather than authentication, so it is usually enough to merely store the user name, account name, or a unique user ID (such as a GUID) in a cookie and use it to access the user personalization infrastructure of a site.

For more information about creating and reading cookies, see “HttpResponse.Cookies Property” and “HttpRequest.Cookies Property,” in the MSDN Library.

Implementing Cookies

The following example shows how to store and retrieve information that cookies hold.

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");

  private string getStyle(string key) 
          string val = null;         
          HttpCookie cookie= this.Request.Cookies["preferences1"];
          if (cookie != null) 
                val = cookie.Values[key]; 
          return val;

Never rely on cookies for essential functionality in your Web applications, because users can disable them in their browsers.

Using Query Strings

A query string is information sent to the server appended to the end of a page’s URL. You can use a query string to submit data back to your page or to another page through the URL.

Query strings provide a simple but limited way of maintaining some types of state information. For example, they make it easy to pass information from one page to another, such as passing a product number to another page, where it is processed.

Note Query strings are a viable option only when a page is requested through its URL using HTTP GET. You cannot read a query string from a page that has been submitted to the server using HTTP POST.

Benefits and Limitations of Query Strings

Query strings offer the following benefits:

  • No server resources are required. The query string is contained in the HTTP request for a specific URL.
  • They provide broad support. Almost all browsers and client devices support passing values in a query string.
  • They are simple to implement. ASP.NET provides full support for the query string method, including methods for reading query strings using the property.

Query strings do have some limitations; for example:

  • The information in the query string is directly visible to the user in the browser user interface. The query values are exposed to the Internet in the URL, so in some cases, security might be an issue.
  • Most browsers and client devices impose a 255-character limit on URL length.

Query strings can be very useful in certain circumstances, such as when you are passing parameters to the server to customize the formatting of the data returned.

Implementing Query Strings

The following example shows how to include a query string in a URL.

The following example shows how to access the information at the server.

// Check for a query string in a request.
string user = Request.QueryString["User"];
if( user != null )
  // Do something with the user name.

Query strings enable you to send string information directly from the client to the server for server-side processing.

Using Client-Side Caching Technologies

Table 2.4 shows recommendations for the use of each client-side caching technology discussed.

Table 2.4: Client-side state technologies

Mechanism Recommended uses
Hidden fields To store small amounts of information for a page that posts back to itself or to another page when security is not an issue. You can use a hidden field only on pages that are submitted to the server.
View state To store small amounts of information for a page that posts back to itself. Using view state provides basic security.
Hidden frames To cache data items on the client and to avoid the roundtrips of the data to the server.
Cookies To store small amounts of information on the client when security is not an issue.
Query strings To transfer small amounts of information from one page to another when security is not an issue. You can use query strings only if you are requesting the same page or another page using a link.

Using Internet Explorer Caching

Microsoft Internet Explorer provides mechanisms for caching pages or page objects on the user’s computer. In Internet Explorer, you can cache data on the client rather than on the server, thus reducing server and network load to a minimum.

Internet Explorer caching is not suitable for all situations. In this section, you learn how to best utilize Internet Explorer caching in your applications.

Note Internet Explorer caching is supported only in an Internet Explorer client environment. If other client browsers are also being used, you must use customization code in your application and provide an alternative caching solution for other types of browser.

Understanding Internet Explorer Cache Types

You can store information in the Internet Explorer cache either by specifying a time that the page should expire from the cache or by using dynamic HTML (DHTML) behaviors:

  • You can add an EXPIRES directive to the header of objects so that Internet Explorer caches them for the specified time period. When the browser needs to access this data, the expiration date is queried, and if the date is in the future, the cached version of the data is used. If the expiration date has passed, the browser contacts the server for the current version of the data. As a result, any sites that use the EXPIRES header perform better.
  • You can use persistence through DHTML behaviors that allow you to store information on the client. Doing so reduces the need to query a server database for client-specific information, such as color settings or screen layout preferences, and increases the overall performance of the page. Persistence stores the data hierarchically, making it easier for the Web developer to access.
Note Users can choose to work offline by selecting Work Offline on the File menu in Internet Explorer. When Work Offline is selected, the system enters a global offline state independent of any current network connection, and content is read exclusively from the cache.

Another method of making Internet Explorer cache pages is by manually setting the page expiration by using the Web pagefile properties in IIS. The site administrator can perform this operation.

To set the expiration date manually in IIS

  1. Right-click the file name.
  2. Click Properties, and then click the HTTP Headers tab.
  3. Select the Enable Content Expiration check box.
  4. Set a date in the future, and then click OK.

For more information about the Internet Explorer cache, see “Control Your Cache, Speed up Your Site” in the MSDN Library.

Determining What to Cache in the Internet Explorer Cache

You should use the Internet Explorer cache for static page items that do not change often, such as:

  • Images and bitmaps on HTML pages.
  • Static text objects.
  • Page banners and footers, which contain general information or navigation menus that seldom change. Retrieving this data from the Internet Explorer cache gives the user an immediate response while the rest of the page data is fetched from the server.
  • Site home pages that rarely change. This page can be stored in the Internet Explorer cache to give the user a prompt response when navigating to the site.
  • Client-specific data that uses DHTML persistence. This solution is limited to browsers that support DHTML, such as Internet Explorer 4.0 and later.

Do not use the Internet Explorer cache for dynamic content because the cached copy on the client soon becomes stale.

Understanding Benefits and Limitations of the Internet Explorer Cache

Using the Internet Explorer cache provides the following benefits:

  • Reduced network traffic because the client does not need to contact the server for all of the content.
  • Facilitation of offline browsing. Internet Explorer cached data is available for offline viewing on the client when it is disconnected from the network.
  • The ability to build hierarchical data structures using the XML Document Object Model (DOM), using only DHTML persistence. The page author can use the formatted data structure supported by XML standards to store complex data in the cache, which is easy to navigate and retrieve.

The Internet Explorer cache does have some limitations:

  • Cached data expiration time has to be predetermined and cannot depend on server data updates. Internet Explorer currently supports a lazy update scheme, in which cached data loads first. Then Internet Explorer queries the server to check whether the data has been updated. If so, the data is fetched to the Internet Explorer cache to prepare for the next time the client requests the data.
  • Internet Explorer cached data is not encrypted. You must avoid storing sensitive data such as credit card numbers in the Internet Explorer cache because anyone with access to the cache folder can view the files and the data cached in them.

The Internet Explorer cache is ideal for improving performance for pages containing large static items.

Implementing Internet Explorer Caching

Add the following header to your page or object header to control how Internet Explorer caches the page.

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

This example caches the page or object until the date specified by the value attribute.

Managing the Internet Explorer Cache

Internet Explorer lets the user manage the cache size and cache update frequency by changing the settings in the Internet Explorer Options dialog box. You can change the cache size to match the amount of free disk space on the client computer.

The Internet Explorer cache can be managed only from the client computer and cannot be controlled from the server.


This chapter introduced you to various technologies available for implementing caching mechanisms in .NET-based applications. You saw examples of how to implement these mechanisms and learned the benefits and limitations of each.

The next step is to map how to use these technologies in the various application layers and deployment scenarios in distributed applications.

Recently I write a ASP website, here list some tips:

The Point:

  1. There are two method to transfer information from static html to asp code for processing. One is GET and the other is POST. There are some build-in objects which you can use to retrieve the info. for example there are Request.QueryString(“”), and the other is Request.Form[“”]. Get is just to get what you want, and POST is to post all the information in Form.
  2. You should take care of the CharacterSet, otherwise it will cause your database problems. When you dispose a website, you should consider to this first.


The default script language is VBScript, and In my opinion, ASP is just like a platform while script language is actually the language gramma of this platform which can be change just use the clause above. But in some programming language it’s language gramma cann’t be changed, just like C++.

CodePage is the CharacterSet of this page, when you want to save to database, you should assign this right, at the same time, In HTML page which will send the data for processing should also designate the CharacterSet right!
<meta http-equiv=”Content-Type” content=”text/html; charset=utf-8″>

This is for HTML to designate the CharacterSEt

Session.CodePage = 65001

‘serverName = request.ServerVariables(“SERVER_NAME”)

 CFG_DB = “The path

CFG_DB_PW = “The password


And all the code in <% %>, you can consider as that the <%%> is not there to concatenation the code together.
<!–#include file=”../db/connect.asp”–>

use to include some code.
‘函数名: ConvertJF
‘参 数: Text — 要转换的文本内容
‘参 数: cType —- 转换类型 0[简转繁],1[繁转简]
‘返回值: 返回转换后的文本内容
Function ConvertJF(ByVal Text,ByVal cType)
Dim TextJ,TextF,tmpText,tmpLen,I,charAt
TextJ = “啊阿埃挨哎唉哀皑癌蔼矮艾碍爱隘鞍氨安俺按暗岸胺案肮昂盎凹敖熬翱袄傲奥懊澳芭捌扒叭吧笆八疤巴拔跋靶把耙坝霸罢爸白柏百摆佰败拜稗斑班搬扳般颁板版扮拌伴瓣半办绊邦帮梆榜膀绑棒磅蚌镑傍谤苞胞包褒剥薄雹保堡饱宝抱报暴豹鲍爆杯碑悲卑北辈背贝钡倍狈备惫焙被奔苯本笨崩绷甭泵蹦迸逼鼻比鄙笔彼碧蓖蔽毕毙毖币庇痹闭敝弊必辟壁臂避陛鞭边编贬扁便变卞辨辩辫遍标彪膘表鳖憋别瘪彬斌濒滨宾摈兵冰柄丙秉饼炳病并玻菠播拨钵波博勃搏铂箔伯帛舶脖膊渤泊驳捕卜哺补埠不布步簿部怖擦猜裁材才财睬踩采彩菜蔡餐参蚕残惭惨灿苍舱仓沧藏操糙槽曹草厕策侧册测层蹭插叉茬茶查碴搽察岔差诧拆柴豺搀掺蝉馋谗缠铲产阐颤昌猖场尝常长偿肠厂敞畅唱倡超抄钞朝嘲潮巢吵炒车扯撤掣彻澈郴臣辰尘晨忱沉陈趁衬撑称城橙成呈乘程惩澄诚承逞骋秤吃痴持匙池迟弛驰耻齿侈尺赤翅斥炽充冲虫崇宠抽酬畴踌稠愁筹仇绸瞅丑臭初出橱厨躇锄雏滁除楚础储矗搐触处揣川穿椽传船喘串疮窗幢床闯创吹炊捶锤垂春椿醇唇淳纯蠢戳绰疵茨磁雌辞慈瓷词此刺赐次聪葱囱匆从丛凑粗醋簇促蹿篡窜摧崔催脆瘁粹淬翠村存寸磋撮搓措挫错搭达答瘩打大呆歹傣戴带殆代贷袋待逮怠耽担丹单郸掸胆旦氮但惮淡诞弹蛋当挡党荡档刀捣蹈倒岛祷导到稻悼道盗德得的蹬灯登等瞪凳邓堤低滴迪敌笛狄涤翟嫡抵底地蒂第帝弟递缔颠掂滇碘点典靛垫电佃甸店惦奠淀殿碉叼雕凋刁掉吊钓调跌爹碟蝶迭谍叠丁盯叮钉顶鼎锭定订丢东冬董懂动栋侗恫冻洞兜抖斗陡豆逗痘都督毒犊独读堵睹赌杜镀肚度渡妒端短锻段断缎堆兑队对墩吨蹲敦顿囤钝盾遁掇哆多夺垛躲朵跺舵剁惰堕蛾峨鹅俄额讹娥恶厄扼遏鄂饿恩而儿耳尔饵洱二贰发罚筏伐乏阀法珐藩帆番翻樊矾钒繁凡烦反返范贩犯饭泛坊芳方肪房防妨仿访纺放菲非啡飞肥匪诽吠肺废沸费芬酚吩氛分纷坟焚汾粉奋份忿愤粪丰封枫蜂峰锋风疯烽逢冯缝讽奉凤佛否夫敷肤孵扶拂辐幅氟符伏俘服浮涪福袱弗甫抚辅俯釜斧脯腑府腐赴副覆赋复傅付阜父腹负富讣附妇缚咐噶嘎该改概钙盖溉干甘杆柑竿肝赶感秆敢赣冈刚钢缸肛纲岗港杠篙皋高膏羔糕搞镐稿告哥歌搁戈鸽胳疙割革葛格蛤阁隔铬个各给根跟耕更庚羹埂耿梗工攻功恭龚供躬公宫弓巩汞拱贡共钩勾沟苟狗垢构购够辜菇咕箍估沽孤姑鼓古蛊骨谷股故顾固雇刮瓜剐寡挂褂乖拐怪棺关官冠观管馆罐惯灌贯光广逛瑰规圭硅归龟闺轨鬼诡癸桂柜跪贵刽辊滚棍锅郭国果裹过哈骸孩海氦亥害骇酣憨邯韩含涵寒函喊罕翰撼捍旱憾悍焊汗汉夯杭航壕嚎豪毫郝好耗号浩呵喝荷菏核禾和何合盒貉阂河涸赫褐鹤贺嘿黑痕很狠恨哼亨横衡恒轰哄烘虹鸿洪宏弘红喉侯猴吼厚候后呼乎忽瑚壶葫胡蝴狐糊湖弧虎唬护互沪户花哗华猾滑画划化话槐徊怀淮坏欢环桓还缓换患唤痪豢焕涣宦幻荒慌黄磺蝗簧皇凰惶煌晃幌恍谎灰挥辉徽恢蛔回毁悔慧卉惠晦贿秽会烩汇讳诲绘荤昏婚魂浑混豁活伙火获或惑霍货祸击圾基机畸稽积箕肌饥迹激讥鸡姬绩缉吉极棘辑籍集及急疾汲即嫉级挤几脊己蓟技冀季伎祭剂悸济寄寂计记既忌际妓继纪嘉枷夹佳家加荚颊贾甲钾假稼价架驾嫁歼监坚尖笺间煎兼肩艰奸缄茧检柬碱硷拣捡简俭剪减荐槛鉴践贱见键箭件健舰剑饯渐溅涧建僵姜将浆江疆蒋桨奖讲匠酱降蕉椒礁焦胶交郊浇骄娇嚼搅铰矫侥脚狡角饺缴绞剿教酵轿较叫窖揭接皆秸街阶截劫节茎睛晶鲸京惊精粳经井警景颈静境敬镜径痉靖竟竞净炯窘揪究纠玖韭久灸九酒厩救旧臼舅咎就疚鞠拘狙疽居驹菊局咀矩举沮聚拒据巨具距踞锯俱句惧炬剧捐鹃娟倦眷卷绢撅攫抉掘倔爵桔杰捷睫竭洁结解姐戒藉芥界借介疥诫届巾筋斤金今津襟紧锦仅谨进靳晋禁近烬浸尽劲荆兢觉决诀绝均菌钧军君峻俊竣浚郡骏喀咖卡咯开揩楷凯慨刊堪勘坎砍看康慷糠扛抗亢炕考拷烤靠坷苛柯棵磕颗科壳咳可渴克刻客课肯啃垦恳坑吭空恐孔控抠口扣寇枯哭窟苦酷库裤夸垮挎跨胯块筷侩快宽款匡筐狂框矿眶旷况亏盔岿窥葵奎魁傀馈愧溃坤昆捆困括扩廓阔垃拉喇蜡腊辣啦莱来赖蓝婪栏拦篮阑兰澜谰揽览懒缆烂滥琅榔狼廊郎朗浪捞劳牢老佬姥酪烙涝勒乐雷镭蕾磊累儡垒擂肋类泪棱楞冷厘梨犁黎篱狸离漓理李里鲤礼莉荔吏栗丽厉励砾历利傈例俐痢立粒沥隶力璃哩俩联莲连镰廉怜涟帘敛脸链恋炼练粮凉梁粱良两辆量晾亮谅撩聊僚疗燎寥辽潦了撂镣廖料列裂烈劣猎琳林磷霖临邻鳞淋凛赁吝拎玲菱零龄铃伶羚凌灵陵岭领另令溜琉榴硫馏留刘瘤流柳六龙聋咙笼窿隆垄拢陇楼娄搂篓漏陋芦卢颅庐炉掳卤虏鲁麓碌露路赂鹿潞禄录陆戮驴吕铝侣旅履屡缕虑氯律率滤绿峦挛孪滦卵乱掠略抡轮伦仑沦纶论萝螺罗逻锣箩骡裸落洛骆络妈麻玛码蚂马骂嘛吗埋买麦卖迈脉瞒馒蛮满蔓曼慢漫谩芒茫盲氓忙莽猫茅锚毛矛铆卯茂冒帽貌贸么玫枚梅酶霉煤没眉媒镁每美昧寐妹媚门闷们萌蒙檬盟锰猛梦孟眯醚靡糜迷谜弥米秘觅泌蜜密幂棉眠绵冕免勉娩缅面苗描瞄藐秒渺庙妙蔑灭民抿皿敏悯闽明螟鸣铭名命谬摸摹蘑模膜磨摩魔抹末莫墨默沫漠寞陌谋牟某拇牡亩姆母墓暮幕募慕木目睦牧穆拿哪呐钠那娜纳氖乃奶耐奈南男难囊挠脑恼闹淖呢馁内嫩能妮霓倪泥尼拟你匿腻逆溺蔫拈年碾撵捻念娘酿鸟尿捏聂孽啮镊镍涅您柠狞凝宁拧泞牛扭钮纽脓浓农弄奴努怒女暖虐疟挪懦糯诺哦欧鸥殴藕呕偶沤啪趴爬帕怕琶拍排牌徘湃派攀潘盘磐盼畔判叛乓庞旁耪胖抛咆刨炮袍跑泡呸胚培裴赔陪配佩沛喷盆砰抨烹澎彭蓬棚硼篷膨朋鹏捧碰坯砒霹批披劈琵毗啤脾疲皮匹痞僻屁譬篇偏片骗飘漂瓢票撇瞥拼频贫品聘乒坪苹萍平凭瓶评屏坡泼颇婆破魄迫粕剖扑铺仆莆葡菩蒲埔朴圃普浦谱曝瀑期欺栖戚妻七凄漆柒沏其棋奇歧畦崎脐齐旗祈祁骑起岂乞企启契砌器气迄弃汽泣讫掐洽牵扦钎铅千迁签仟谦乾黔钱钳前潜遣浅谴堑嵌欠歉枪呛腔羌墙蔷强抢橇锹敲悄桥瞧乔侨巧鞘撬翘峭俏窍切茄且怯窃钦侵亲秦琴勤芹擒禽寝沁青轻氢倾卿清擎晴氰情顷请庆琼穷秋丘邱球求囚酋泅趋区蛆曲躯屈驱渠取娶龋趣去圈颧权醛泉全痊拳犬券劝缺炔瘸却鹊榷确雀裙群然燃冉染瓤壤攘嚷让饶扰绕惹热壬仁人忍韧任认刃妊纫扔仍日戎茸蓉荣融熔溶容绒冗揉柔肉茹蠕儒孺如辱乳汝入褥软阮蕊瑞锐闰润若弱撒洒萨腮鳃塞赛三叁伞散桑嗓丧搔骚扫嫂瑟色涩森僧莎砂杀刹沙纱傻啥煞筛晒珊苫杉山删煽衫闪陕擅赡膳善汕扇缮墒伤商赏晌上尚裳梢捎稍烧芍勺韶少哨邵绍奢赊蛇舌舍赦摄射慑涉社设砷申呻伸身深娠绅神沈审婶甚肾慎渗声生甥牲升绳省盛剩胜圣师失狮施湿诗尸虱十石拾时什食蚀实识史矢使屎驶始式示士世柿事拭誓逝势是嗜噬适仕侍释饰氏市恃室视试收手首守寿授售受瘦兽蔬枢梳殊抒输叔舒淑疏书赎孰熟薯暑曙署蜀黍鼠属术述树束戍竖墅庶数漱恕刷耍摔衰甩帅栓拴霜双爽谁水睡税吮瞬顺舜说硕朔烁斯撕嘶思私司丝死肆寺嗣四伺似饲巳松耸怂颂送宋讼诵搜艘擞嗽苏酥俗素速粟僳塑溯宿诉肃酸蒜算虽隋随绥髓碎岁穗遂隧祟孙损笋蓑梭唆缩琐索锁所塌他它她塔獭挞蹋踏胎苔抬台泰酞太态汰坍摊贪瘫滩坛檀痰潭谭谈坦毯袒碳探叹炭汤塘搪堂棠膛唐糖倘躺淌趟烫掏涛滔绦萄桃逃淘陶讨套特藤腾疼誊梯剔踢锑提题蹄啼体替嚏惕涕剃屉天添填田甜恬舔腆挑条迢眺跳贴铁帖厅听烃汀廷停亭庭挺艇通桐酮瞳同铜彤童桶捅筒统痛偷投头透凸秃突图徒途涂屠土吐兔湍团推颓腿蜕褪退吞屯臀拖托脱鸵陀驮驼椭妥拓唾挖哇蛙洼娃瓦袜歪外豌弯湾玩顽丸烷完碗挽晚皖惋宛婉万腕汪王亡枉网往旺望忘妄威巍微危韦违桅围唯惟为潍维苇萎委伟伪尾纬未蔚味畏胃喂魏位渭谓尉慰卫瘟温蚊文闻纹吻稳紊问嗡翁瓮挝蜗涡窝我斡卧握沃巫呜钨乌污诬屋无芜梧吾吴毋武五捂午舞伍侮坞戊雾晤物勿务悟误昔熙析西硒矽晰嘻吸锡牺稀息希悉膝夕惜熄烯溪汐犀檄袭席习媳喜铣洗系隙戏细瞎虾匣霞辖暇峡侠狭下厦夏吓掀锨先仙鲜纤咸贤衔舷闲涎弦嫌显险现献县腺馅羡宪陷限线相厢镶香箱襄湘乡翔祥详想响享项巷橡像向象萧硝霄削哮嚣销消宵淆晓小孝校肖啸笑效楔些歇蝎鞋协挟携邪斜胁谐写械卸蟹懈泄泻谢屑薪芯锌欣辛新忻心信衅星腥猩惺兴刑型形邢行醒幸杏性姓兄凶胸匈汹雄熊休修羞朽嗅锈秀袖绣墟戌需虚嘘须徐许蓄酗叙旭序畜恤絮婿绪续轩喧宣悬旋玄选癣眩绚靴薛学穴雪血勋熏循旬询寻驯巡殉汛训讯逊迅压押鸦鸭呀丫芽牙蚜崖衙涯雅哑亚讶焉咽阉烟淹盐严研蜒岩延言颜阎炎沿奄掩眼衍演艳堰燕厌砚雁唁彦焰宴谚验殃央鸯秧杨扬佯疡羊洋阳氧仰痒养样漾邀腰妖瑶摇尧遥窑谣姚咬舀药要耀椰噎耶爷野冶也页掖业叶曳腋夜液一壹医揖铱依伊衣颐夷遗移仪胰疑沂宜姨彝椅蚁倚已乙矣以艺抑易邑屹亿役臆逸肄疫亦裔意毅忆义益溢诣议谊译异翼翌绎茵荫因殷音阴姻吟银淫寅饮尹引隐印英樱婴鹰应缨莹萤营荧蝇迎赢盈影颖硬映哟拥佣臃痈庸雍踊蛹咏泳涌永恿勇用幽优悠忧尤由邮铀犹油游酉有友右佑釉诱又幼迂淤于盂榆虞愚舆余俞逾鱼愉渝渔隅予娱雨与屿禹宇语羽玉域芋郁吁遇喻峪御愈欲狱育誉浴寓裕预豫驭鸳渊冤元垣袁原援辕园员圆猿源缘远苑愿怨院曰约越跃钥岳粤月悦阅耘云郧匀陨允运蕴酝晕韵孕匝砸杂栽哉灾宰载再在咱攒暂赞赃脏葬遭糟凿藻枣早澡蚤躁噪造皂灶燥责择则泽贼怎增憎曾赠扎喳渣札轧铡闸眨栅榨咋乍炸诈摘斋宅窄债寨瞻毡詹粘沾盏斩辗崭展蘸栈占战站湛绽樟章彰漳张掌涨杖丈帐账仗胀瘴障招昭找沼赵照罩兆肇召遮折哲蛰辙者锗蔗这浙珍斟真甄砧臻贞针侦枕疹诊震振镇阵蒸挣睁征狰争怔整拯正政帧症郑证芝枝支吱蜘知肢脂汁之织职直植殖执值侄址指止趾只旨纸志挚掷至致置帜峙制智秩稚质炙痔滞治窒中盅忠钟衷终种肿重仲众舟周州洲诌粥轴肘帚咒皱宙昼骤珠株蛛朱猪诸诛逐竹烛煮拄瞩嘱主著柱助蛀贮铸筑住注祝驻抓爪拽专砖转撰赚篆桩庄装妆撞壮状椎锥追赘坠缀谆准捉拙卓桌琢茁酌啄着灼浊兹咨资姿滋淄孜紫仔籽滓子自渍字鬃棕踪宗综总纵邹走奏揍租足卒族祖诅阻组钻纂嘴醉最罪尊遵昨左佐柞做作坐座”

TextF = “啊阿埃挨哎唉哀皚癌藹矮艾礙愛隘鞍氨安俺按暗岸胺案肮昂盎凹敖熬翺襖傲奧懊澳芭捌扒叭吧笆八疤巴拔跋靶把耙壩霸罷爸白柏百擺佰敗拜稗斑班搬扳般頒板版扮拌伴瓣半辦絆邦幫梆榜膀綁棒磅蚌鎊傍謗苞胞包褒剝薄雹保堡飽寶抱報暴豹鮑爆杯碑悲卑北輩背貝鋇倍狽備憊焙被奔苯本笨崩繃甭泵蹦迸逼鼻比鄙筆彼碧蓖蔽畢斃毖幣庇痹閉敝弊必辟壁臂避陛鞭邊編貶扁便變卞辨辯辮遍標彪膘表鼈憋別癟彬斌瀕濱賓擯兵冰柄丙秉餅炳病並玻菠播撥缽波博勃搏鉑箔伯帛舶脖膊渤泊駁捕蔔哺補埠不布步簿部怖擦猜裁材才財睬踩采彩菜蔡餐參蠶殘慚慘燦蒼艙倉滄藏操糙槽曹草廁策側冊測層蹭插叉茬茶查碴搽察岔差詫拆柴豺攙摻蟬饞讒纏鏟産闡顫昌猖場嘗常長償腸廠敞暢唱倡超抄鈔朝嘲潮巢吵炒車扯撤掣徹澈郴臣辰塵晨忱沈陳趁襯撐稱城橙成呈乘程懲澄誠承逞騁秤吃癡持匙池遲弛馳恥齒侈尺赤翅斥熾充沖蟲崇寵抽酬疇躊稠愁籌仇綢瞅醜臭初出櫥廚躇鋤雛滁除楚礎儲矗搐觸處揣川穿椽傳船喘串瘡窗幢床闖創吹炊捶錘垂春椿醇唇淳純蠢戳綽疵茨磁雌辭慈瓷詞此刺賜次聰蔥囪匆從叢湊粗醋簇促躥篡竄摧崔催脆瘁粹淬翠村存寸磋撮搓措挫錯搭達答瘩打大呆歹傣戴帶殆代貸袋待逮怠耽擔丹單鄲撣膽旦氮但憚淡誕彈蛋當擋黨蕩檔刀搗蹈倒島禱導到稻悼道盜德得的蹬燈登等瞪凳鄧堤低滴迪敵笛狄滌翟嫡抵底地蒂第帝弟遞締顛掂滇碘點典靛墊電佃甸店惦奠澱殿碉叼雕凋刁掉吊釣調跌爹碟蝶叠諜疊丁盯叮釘頂鼎錠定訂丟東冬董懂動棟侗恫凍洞兜抖鬥陡豆逗痘都督毒犢獨讀堵睹賭杜鍍肚度渡妒端短鍛段斷緞堆兌隊對墩噸蹲敦頓囤鈍盾遁掇哆多奪垛躲朵跺舵剁惰墮蛾峨鵝俄額訛娥惡厄扼遏鄂餓恩而兒耳爾餌洱二貳發罰筏伐乏閥法琺藩帆番翻樊礬釩繁凡煩反返範販犯飯泛坊芳方肪房防妨仿訪紡放菲非啡飛肥匪誹吠肺廢沸費芬酚吩氛分紛墳焚汾粉奮份忿憤糞豐封楓蜂峰鋒風瘋烽逢馮縫諷奉鳳佛否夫敷膚孵扶拂輻幅氟符伏俘服浮涪福袱弗甫撫輔俯釜斧脯腑府腐赴副覆賦複傅付阜父腹負富訃附婦縛咐噶嘎該改概鈣蓋溉幹甘杆柑竿肝趕感稈敢贛岡剛鋼缸肛綱崗港杠篙臯高膏羔糕搞鎬稿告哥歌擱戈鴿胳疙割革葛格蛤閣隔鉻個各給根跟耕更庚羹埂耿梗工攻功恭龔供躬公宮弓鞏汞拱貢共鈎勾溝苟狗垢構購夠辜菇咕箍估沽孤姑鼓古蠱骨谷股故顧固雇刮瓜剮寡挂褂乖拐怪棺關官冠觀管館罐慣灌貫光廣逛瑰規圭矽歸龜閨軌鬼詭癸桂櫃跪貴劊輥滾棍鍋郭國果裹過哈骸孩海氦亥害駭酣憨邯韓含涵寒函喊罕翰撼捍旱憾悍焊汗漢夯杭航壕嚎豪毫郝好耗號浩呵喝荷菏核禾和何合盒貉閡河涸赫褐鶴賀嘿黑痕很狠恨哼亨橫衡恒轟哄烘虹鴻洪宏弘紅喉侯猴吼厚候後呼乎忽瑚壺葫胡蝴狐糊湖弧虎唬護互滬戶花嘩華猾滑畫劃化話槐徊懷淮壞歡環桓還緩換患喚瘓豢煥渙宦幻荒慌黃磺蝗簧皇凰惶煌晃幌恍謊灰揮輝徽恢蛔回毀悔慧卉惠晦賄穢會燴彙諱誨繪葷昏婚魂渾混豁活夥火獲或惑霍貨禍擊圾基機畸稽積箕肌饑迹激譏雞姬績緝吉極棘輯籍集及急疾汲即嫉級擠幾脊己薊技冀季伎祭劑悸濟寄寂計記既忌際妓繼紀嘉枷夾佳家加莢頰賈甲鉀假稼價架駕嫁殲監堅尖箋間煎兼肩艱奸緘繭檢柬堿鹼揀撿簡儉剪減薦檻鑒踐賤見鍵箭件健艦劍餞漸濺澗建僵姜將漿江疆蔣槳獎講匠醬降蕉椒礁焦膠交郊澆驕嬌嚼攪鉸矯僥腳狡角餃繳絞剿教酵轎較叫窖揭接皆稭街階截劫節莖睛晶鯨京驚精粳經井警景頸靜境敬鏡徑痙靖竟競淨炯窘揪究糾玖韭久灸九酒廄救舊臼舅咎就疚鞠拘狙疽居駒菊局咀矩舉沮聚拒據巨具距踞鋸俱句懼炬劇捐鵑娟倦眷卷絹撅攫抉掘倔爵桔傑捷睫竭潔結解姐戒藉芥界借介疥誡屆巾筋斤金今津襟緊錦僅謹進靳晉禁近燼浸盡勁荊兢覺決訣絕均菌鈞軍君峻俊竣浚郡駿喀咖卡咯開揩楷凱慨刊堪勘坎砍看康慷糠扛抗亢炕考拷烤靠坷苛柯棵磕顆科殼咳可渴克刻客課肯啃墾懇坑吭空恐孔控摳口扣寇枯哭窟苦酷庫褲誇垮挎跨胯塊筷儈快寬款匡筐狂框礦眶曠況虧盔巋窺葵奎魁傀饋愧潰坤昆捆困括擴廓闊垃拉喇蠟臘辣啦萊來賴藍婪欄攔籃闌蘭瀾讕攬覽懶纜爛濫琅榔狼廊郎朗浪撈勞牢老佬姥酪烙澇勒樂雷鐳蕾磊累儡壘擂肋類淚棱楞冷厘梨犁黎籬狸離漓理李裏鯉禮莉荔吏栗麗厲勵礫曆利傈例俐痢立粒瀝隸力璃哩倆聯蓮連鐮廉憐漣簾斂臉鏈戀煉練糧涼梁粱良兩輛量晾亮諒撩聊僚療燎寥遼潦了撂鐐廖料列裂烈劣獵琳林磷霖臨鄰鱗淋凜賃吝拎玲菱零齡鈴伶羚淩靈陵嶺領另令溜琉榴硫餾留劉瘤流柳六龍聾嚨籠窿隆壟攏隴樓婁摟簍漏陋蘆盧顱廬爐擄鹵虜魯麓碌露路賂鹿潞祿錄陸戮驢呂鋁侶旅履屢縷慮氯律率濾綠巒攣孿灤卵亂掠略掄輪倫侖淪綸論蘿螺羅邏鑼籮騾裸落洛駱絡媽麻瑪碼螞馬罵嘛嗎埋買麥賣邁脈瞞饅蠻滿蔓曼慢漫謾芒茫盲氓忙莽貓茅錨毛矛鉚卯茂冒帽貌貿麽玫枚梅酶黴煤沒眉媒鎂每美昧寐妹媚門悶們萌蒙檬盟錳猛夢孟眯醚靡糜迷謎彌米秘覓泌蜜密冪棉眠綿冕免勉娩緬面苗描瞄藐秒渺廟妙蔑滅民抿皿敏憫閩明螟鳴銘名命謬摸摹蘑模膜磨摩魔抹末莫墨默沫漠寞陌謀牟某拇牡畝姆母墓暮幕募慕木目睦牧穆拿哪呐鈉那娜納氖乃奶耐奈南男難囊撓腦惱鬧淖呢餒內嫩能妮霓倪泥尼擬你匿膩逆溺蔫拈年碾攆撚念娘釀鳥尿捏聶孽齧鑷鎳涅您檸獰凝甯擰濘牛扭鈕紐膿濃農弄奴努怒女暖虐瘧挪懦糯諾哦歐鷗毆藕嘔偶漚啪趴爬帕怕琶拍排牌徘湃派攀潘盤磐盼畔判叛乓龐旁耪胖抛咆刨炮袍跑泡呸胚培裴賠陪配佩沛噴盆砰抨烹澎彭蓬棚硼篷膨朋鵬捧碰坯砒霹批披劈琵毗啤脾疲皮匹痞僻屁譬篇偏片騙飄漂瓢票撇瞥拼頻貧品聘乒坪蘋萍平憑瓶評屏坡潑頗婆破魄迫粕剖撲鋪仆莆葡菩蒲埔樸圃普浦譜曝瀑期欺棲戚妻七淒漆柒沏其棋奇歧畦崎臍齊旗祈祁騎起豈乞企啓契砌器氣迄棄汽泣訖掐洽牽扡釺鉛千遷簽仟謙乾黔錢鉗前潛遣淺譴塹嵌欠歉槍嗆腔羌牆薔強搶橇鍬敲悄橋瞧喬僑巧鞘撬翹峭俏竅切茄且怯竊欽侵親秦琴勤芹擒禽寢沁青輕氫傾卿清擎晴氰情頃請慶瓊窮秋丘邱球求囚酋泅趨區蛆曲軀屈驅渠取娶齲趣去圈顴權醛泉全痊拳犬券勸缺炔瘸卻鵲榷確雀裙群然燃冉染瓤壤攘嚷讓饒擾繞惹熱壬仁人忍韌任認刃妊紉扔仍日戎茸蓉榮融熔溶容絨冗揉柔肉茹蠕儒孺如辱乳汝入褥軟阮蕊瑞銳閏潤若弱撒灑薩腮鰓塞賽三三傘散桑嗓喪搔騷掃嫂瑟色澀森僧莎砂殺刹沙紗傻啥煞篩曬珊苫杉山刪煽衫閃陝擅贍膳善汕扇繕墒傷商賞晌上尚裳梢捎稍燒芍勺韶少哨邵紹奢賒蛇舌舍赦攝射懾涉社設砷申呻伸身深娠紳神沈審嬸甚腎慎滲聲生甥牲升繩省盛剩勝聖師失獅施濕詩屍虱十石拾時什食蝕實識史矢使屎駛始式示士世柿事拭誓逝勢是嗜噬適仕侍釋飾氏市恃室視試收手首守壽授售受瘦獸蔬樞梳殊抒輸叔舒淑疏書贖孰熟薯暑曙署蜀黍鼠屬術述樹束戍豎墅庶數漱恕刷耍摔衰甩帥栓拴霜雙爽誰水睡稅吮瞬順舜說碩朔爍斯撕嘶思私司絲死肆寺嗣四伺似飼巳松聳慫頌送宋訟誦搜艘擻嗽蘇酥俗素速粟僳塑溯宿訴肅酸蒜算雖隋隨綏髓碎歲穗遂隧祟孫損筍蓑梭唆縮瑣索鎖所塌他它她塔獺撻蹋踏胎苔擡台泰酞太態汰坍攤貪癱灘壇檀痰潭譚談坦毯袒碳探歎炭湯塘搪堂棠膛唐糖倘躺淌趟燙掏濤滔縧萄桃逃淘陶討套特藤騰疼謄梯剔踢銻提題蹄啼體替嚏惕涕剃屜天添填田甜恬舔腆挑條迢眺跳貼鐵帖廳聽烴汀廷停亭庭挺艇通桐酮瞳同銅彤童桶捅筒統痛偷投頭透凸禿突圖徒途塗屠土吐兔湍團推頹腿蛻褪退吞屯臀拖托脫鴕陀馱駝橢妥拓唾挖哇蛙窪娃瓦襪歪外豌彎灣玩頑丸烷完碗挽晚皖惋宛婉萬腕汪王亡枉網往旺望忘妄威巍微危韋違桅圍唯惟爲濰維葦萎委偉僞尾緯未蔚味畏胃喂魏位渭謂尉慰衛瘟溫蚊文聞紋吻穩紊問嗡翁甕撾蝸渦窩我斡臥握沃巫嗚鎢烏汙誣屋無蕪梧吾吳毋武五捂午舞伍侮塢戊霧晤物勿務悟誤昔熙析西硒矽晰嘻吸錫犧稀息希悉膝夕惜熄烯溪汐犀檄襲席習媳喜銑洗系隙戲細瞎蝦匣霞轄暇峽俠狹下廈夏嚇掀鍁先仙鮮纖鹹賢銜舷閑涎弦嫌顯險現獻縣腺餡羨憲陷限線相廂鑲香箱襄湘鄉翔祥詳想響享項巷橡像向象蕭硝霄削哮囂銷消宵淆曉小孝校肖嘯笑效楔些歇蠍鞋協挾攜邪斜脅諧寫械卸蟹懈泄瀉謝屑薪芯鋅欣辛新忻心信釁星腥猩惺興刑型形邢行醒幸杏性姓兄凶胸匈洶雄熊休修羞朽嗅鏽秀袖繡墟戌需虛噓須徐許蓄酗敘旭序畜恤絮婿緒續軒喧宣懸旋玄選癬眩絢靴薛學穴雪血勳熏循旬詢尋馴巡殉汛訓訊遜迅壓押鴉鴨呀丫芽牙蚜崖衙涯雅啞亞訝焉咽閹煙淹鹽嚴研蜒岩延言顔閻炎沿奄掩眼衍演豔堰燕厭硯雁唁彥焰宴諺驗殃央鴦秧楊揚佯瘍羊洋陽氧仰癢養樣漾邀腰妖瑤搖堯遙窯謠姚咬舀藥要耀椰噎耶爺野冶也頁掖業葉曳腋夜液一壹醫揖銥依伊衣頤夷遺移儀胰疑沂宜姨彜椅蟻倚已乙矣以藝抑易邑屹億役臆逸肄疫亦裔意毅憶義益溢詣議誼譯異翼翌繹茵蔭因殷音陰姻吟銀淫寅飲尹引隱印英櫻嬰鷹應纓瑩螢營熒蠅迎贏盈影穎硬映喲擁傭臃癰庸雍踴蛹詠泳湧永恿勇用幽優悠憂尤由郵鈾猶油遊酉有友右佑釉誘又幼迂淤于盂榆虞愚輿余俞逾魚愉渝漁隅予娛雨與嶼禹宇語羽玉域芋郁籲遇喻峪禦愈欲獄育譽浴寓裕預豫馭鴛淵冤元垣袁原援轅園員圓猿源緣遠苑願怨院曰約越躍鑰嶽粵月悅閱耘雲鄖勻隕允運蘊醞暈韻孕匝砸雜栽哉災宰載再在咱攢暫贊贓髒葬遭糟鑿藻棗早澡蚤躁噪造皂竈燥責擇則澤賊怎增憎曾贈紮喳渣劄軋鍘閘眨柵榨咋乍炸詐摘齋宅窄債寨瞻氈詹粘沾盞斬輾嶄展蘸棧占戰站湛綻樟章彰漳張掌漲杖丈帳賬仗脹瘴障招昭找沼趙照罩兆肇召遮折哲蟄轍者鍺蔗這浙珍斟真甄砧臻貞針偵枕疹診震振鎮陣蒸掙睜征猙爭怔整拯正政幀症鄭證芝枝支吱蜘知肢脂汁之織職直植殖執值侄址指止趾只旨紙志摯擲至致置幟峙制智秩稚質炙痔滯治窒中盅忠鍾衷終種腫重仲衆舟周州洲謅粥軸肘帚咒皺宙晝驟珠株蛛朱豬諸誅逐竹燭煮拄矚囑主著柱助蛀貯鑄築住注祝駐抓爪拽專磚轉撰賺篆樁莊裝妝撞壯狀椎錐追贅墜綴諄准捉拙卓桌琢茁酌啄著灼濁茲咨資姿滋淄孜紫仔籽滓子自漬字鬃棕蹤宗綜總縱鄒走奏揍租足卒族祖詛阻組鑽纂嘴醉最罪尊遵昨左佐柞做作坐座”
tmpText = “”
tmpLen = Len(Text)
If tmpLen = 0 Then
    ConvertJF = “”
    Exit Function
End If
If cType = 0 Then
    For I=1 To tmpLen
        charAt = Instr(TextJ,Mid(Text,I,1))
        If charAt<>0 Then
            tmpText = tmpText & Mid(TextF,charAt,1)
            tmpText = tmpText & Mid(Text,I,1)
        End If
ElseIf cType = 1 Then
    For I=1 To tmpLen
        charAt = Instr(TextF,Mid(Text,I,1))
        If charAt<>0 Then
            tmpText = tmpText & Mid(TextJ,charAt,1)
            tmpText = tmpText & Mid(Text,I,1)
        End If
    tmpText = Text
End If
ConvertJF = tmpText
End Function

These language is really quite easy, you just need to find the differences comparing to other programming language. And then just use it as you know it very much by look up the language gramma reference, and use the build-in objects and functions.


Function CreateFile(ByVal pstrFile, ByVal pstrData)
Dim objStream
  ‘Create the stream
  Set objStream = CreateObject(“ADODB.Stream”)
  ‘Initialize the stream
  ‘Reset the position and indicate the charactor encoding
  objStream.Position = 0
  objStream.Charset = “UTF-8”
  ‘Write to the steam
  objStream.WriteText pstrData
  ‘Save the stream to a file
  objStream.SaveToFile pstrFile
end Function

%>To save a file as UTF-8, you cann’t use TestStream.

Dim CTitle, ETitle, CContent, EContent, NumberOfImage, ZDTitle, CDTitle, EDTitle, FileName, CurDate, NewType, Special

CTitle = Request.Form(“CTitle”)
ETitle = Request.Form(“ETitle”)
CContent = Request.Form(“CContent”)
EContent = Request.Form(“EContent”)
NumberOfImage = Request.Form(“NumberOfImage”)
ZDTitle = Request.Form(“ZDTitle”)
EDTitle = Request.Form(“EDTitle”)
FileName = Request.Form(“FileName”)
CurDate = Request.Form(“CurDate”)
NewType = Request.Form(“NewType”)
Special = Request.Form(“Special”)
Dim ImageNames(10)
ImageNames(1) = Request.Form(“ImageName1”)
ImageNames(2) = Request.Form(“ImageName2”)
ImageNames(3) = Request.Form(“ImageName3”)
ImageNames(4) = Request.Form(“ImageName4”)

rs.Open “tbl_notice”,Conn, 3, 3

rs.AddNew varfields,varvalues

dim hello
hello = ConvertJF(ZDTitle,0)

rs.AddNew varfields,varvalues

rs.AddNew varfields,varvalues

dim head1, head2, head3, head4, head5
head1 = “<table width=’100%’ border=’0′ cellspacing=’0′ cellpadding=’0′><tr><td width=’441′><span class=’titleFont2′><li><p align=’center’>”
head2 = “</p></span></td></tr><tr><td height=’2′></td></tr><tr><td width=’441′ valign=’top’><span class=’normalFont’><div style=’text-align:justify; text-justify:inter-ideograph’>”
head3 = “<p align=’center’>&nbsp;</p></div></p></span></td><td width=’309′ align=’center’ valign=’top’>”

head4 = “”
for num = 1 to CInt(NumberOfImage)
head4 = head4 & “<p><a href=’../images/notice/” & ImageNames(num) & “‘ target=’_blank’><img src=’../images/notice/” & ImageNames(num) & “‘ width=’180′ height=’216′ border=’0′></a></p>”

head5 = “</td></tr><tr><td valign=’top’>&nbsp;</td><td align=’center’ valign=’top’>&nbsp;</td></tr></table>”
dim whole
whole = head1 & CTitle & head2 & CContent & head3 & head4 & head5
dim path
path = Server.MapPath(“../zh/notice/”& FileName &”.html”)
CreateFile path, “”&whole

TCTitle = ConvertJF(CTitle, 0)
TCContent = ConvertJF(CContent, 0)
whole = head1 & TCTitle & head2 & TCContent & head3 & head4 & head5
path = Server.MapPath(“../cn/notice/”& FileName &”.html”)
whole = ConvertJF(whole,0)
CreateFile path, “”&whole

dim pthead1, pthead2, pthead3, pthead4, pthead5
pthead1 = “<table width=’100%’ border=’0′ cellspacing=’0′ cellpadding=’0′><tr> <td width=’441′>  <span class=’titleFont2′>   <li>     <p align=’center’>”
pthead2 = “</p>   </span>    </td></tr><tr><td height=’2′></td></tr> <tr> <td width=’441′ valign=’top’><span class=’normalFont’><div style=’text-align:justify; text-justify:inter-ideograph’> ”
pthead3 = “<p align=’center’>&nbsp;</p>  </div></p></span> </td> <td width=’309′ align=’center’ valign=’top’>”

  Once you want to concatenate some string together, you should use &, and then is there are ” s in (” “), you should convert it to ‘ first.      
pthead4 = “”
for num = 1 to CInt(NumberOfImage)
pthead4 = pthead4 & “<p><a href=’../images/notice/” & ImageNames(num) & “‘ target=’_blank’><img src=’../images/notice/” & ImageNames(num) & “‘ width=’180′ height=’216′ border=’0′></a></p>”
pthead5 = “</td></tr><tr>  <td valign=’top’>&nbsp;</td>  <td align=’center’ valign=’top’>&nbsp;</td></tr><tr>  <td valign=’top’>&nbsp;</td>  <td align=’center’ valign=’top’>&nbsp;</td></tr></table>”
whole = pthead1 & ETitle & pthead2 & EContent & pthead3 & pthead4 & pthead5

path = Server.MapPath(“../pt/notice/”& FileName &”.html”)

Map the path to server’s local path, which is necessary for the functions.
CreateFile path, “”&whole
Response.Redirect “




function getAnnounceList(cp, lang)
rs.Open “SELECT * FROM tbl_announce WHERE status=’0′ and lang = ‘”&lang&”‘ ORDER BY id DESC”,Conn,3

if rs.Eof = false then  
  if cp = Empty then
   cp = 1
  end if

  ps = 20
  rs.pagesize = ps        In asp , it maintain database isn’t just like the regular database, It keep them as a page, which make you can use to display them easy just like forum, page by page, in one page there are several news.

  and this things you can set it all by yourself.
  pagecount = rs.pagecount
  if pagecount < cp then cp = pagecount
  rs.absolutepage = cp
  pagelist = “”
  for i = 1 to pagecount
   if cp = i then
    pagelist = pagelist & “[ <font color=red>”&i&”</font> ]”
    pagelist = pagelist & “[ <a href=”&Request.ServerVariables(“SCRIPT_NAME”)&”?cp=”&i&”>”&i&”</a> ]”  
   end if
  nStart = (cp-1)*ps + 1
  nEnd = nStart + ps – 1
  if nEnd > rs.recordcount Then nEnd = rs.recordcount

 print(“<table width=’100%’ border=’0′>”)
 print(“<tr><td><span class=’normalFont’>”)
 if lang = “zh” then
  %>页次: <%
 elseif lang = “cn” then
  %>頁次: <%
 elseif lang = “pt” then
  %>Page: <%
 end if

 print(“<td align=’right’><span class=’normalFont’>”)

 if lang = “zh” then
 elseif lang = “cn” then
 elseif lang = “pt” then
  %>Total <% 
 end if

 print(“<font color=’green’>”&rs.recordcount&”</font>”)
 if lang = “zh” then
 elseif lang = “cn” then
 elseif lang = “pt” then
  %> Records<%
 end if
 print(“<table width=’100%’ border=’0′ cellpadding=’0′ cellspacing=’0′>”)

 prevDate = “”
 while rs.EOF = False AND ps <> Empty  
  ps = ps – 1

  print(“<td valign=’top’><li></td>”)
  print(“<a class=’notice’ href=’announce.asp?a=”& rs(“fileName”) & “‘>”)
  print(rs(“title”) & “&nbsp;” & rs(“dateDisplay”))
  print(“<tr><td height=’10’ colspan=’2′><img src=’../images/bgreendot.gif’ width=’400′ height=’1’/></td></tr>”)
end if
end function

XUL (XML User Interface Language) is Mozilla’s XML-based language that lets you build feature-rich cross platform applications that can run connected or disconnected from the Internet. These applications are easily customized with alternative text, graphics and layout so they can be readily branded or localized for various markets. Web developers already familiar with Dynamic HTML (DHTML) will learn XUL quickly and can start building applications right away.

In my opinion, XUL is just like HTML,  We can use this language to customize our software view. And use Javascript to assign functions to our program.

When you want to learn how to develop thunderbird Add-on, you should know what is DTD.

For Example:

<?xml-stylesheet href=”chrome://messenger/skin/addressbook/addressbook.css” type=”text/css”?>

In XUL, developers use chrome:// to Locate the root path. In the Chrome folder, messenger is the main file that we need to modify. It contains some jar files. When thunderbird start, it read the XUL files in these Jar to generate it’s view instead of generating at compile time. So what we need to do is to modify these Jar to customize our program.

These Jar file is just a simple zip file that contain some special manifest according to Thunderbird program.  We can simple Unzip it into a folder and then let the Thunderbird to directly read from the folder instead of from Jar, this will simplify our development.

Just like HTML, in XUL we can use some kind of CSS to beautify our program.
<!DOCTYPE window [
<!ENTITY % abMainWindowDTD SYSTEM “chrome://messenger/locale/addressbook/abMainWindow.dtd” >
<!ENTITY % brandDTD SYSTEM “chrome://branding/locale/brand.dtd” >
]>use these external files to verify our file. it’s DTD. Entity is a kind of reference, you can just replace the Entity name with the external file.

<window xmlns=””(XML namespace)
    persist=”width height screenX screenY sizemode”

In this window element, we can assign some attributes, Which is for calling from Javascript. For example “windowtype”
function toAddressBook()
  toOpenWindowByType(“mail:addressbook”, “chrome://messenger/content/addressbook/addressbook.xul”);
Here in javascript it can call the addressbook.
And onload is some kind of EventHandler, it just likes Javascript. If you want to know more details on this topic , refer to it’s website.

function toOpenWindowByType( inType, uri )
 var windowManager = Components.classes[‘;1’].getService();

 var windowManagerInterface = windowManager.QueryInterface(nsIWindowMediator);

 var topWindow = windowManagerInterface.getMostRecentWindow( inType );
 if ( topWindow )
 else, “_blank”, “chrome,extrachrome,menubar,resizable,scrollbars,status,toolbar”);

It is also some kind of Javascript, you just need to refer to it’s website and use this objects.

And the functions In Messenger/mailcore, Messenger can action like a global function, In your add on program XUL file can directly refer to it without designating path or other thing.

By the way, when we write our add-on, it evently will be merge to main program’s XUL file according to XML regulate(match the same element and merge). It seem to use Install object , you can refer to the website.
if (!(‘extensions’ in window))//simply use in to judge whether there are extension element in window. And then it can define a new field for the object .
    window.extensions = new Array();

if (!(‘mook’ in window.extensions))
    window.extensions.mook = new Array();

if (!(‘minimizetotray’ in window.extensions.mook))
    window.extensions.mook.minimizetotray = use the method we can assign some functions to the array.
        // this is a stub only
        throw Components.results.NS_ERROR_ABORT;

.trayCallback =
function ( aEvent, aParam, aKeyMask, aMouseX, aMouseY ) {
    // note that |this| is the function itself         In Javascript we can use object-oritented, google!




<!ENTITY  cmd.totray.label        “最小化至系统栏”>
<!ENTITY  cmd.totray.accesskey    “小”>
<!ENTITY  cmd.totray.key          “最”>
<!ENTITY  cmd.totray.modifier     “accel,shift”>

<!– tray popup –>
<!ENTITY  cmd.restore.label       “还原窗口”>
<!ENTITY  cmd.restore.accesskey   “R”>
<!ENTITY  cmd.newwindow.label     “新建窗口”>
<!ENTITY  cmd.newwindow.accesskey “O”>
<!ENTITY  cmd.downloads.label     “查看下载项”>
<!ENTITY  cmd.downloads.accesskey “V”>
<!ENTITY  cmd.close.label         “关闭窗口”>
<!ENTITY  cmd.close.accesskey     “C”>
<!ENTITY  cmd.quit.label          “退出快速启动”>
<!ENTITY  cmd.quit.accesskey      “E”>
<!ENTITY  cmd.cancel.label        “Cancel”>

<!DOCTYPE overlay [
<!ENTITY % extensionDTD SYSTEM “chrome://minimizetotray/locale/browser.dtd”>
In XUL, it use this code snippet to refer to the DTD.

  <menupopup id=”menu_FilePopup”>
      accesskey=”&cmd.totray.accesskey;”  Here it refer to the DTD, and Program will replace this reference with the string in the DTD.

So this is Entity, it is a kind of reference.   Format:   & entity_name  ;
      key=”extensions.mook.minimizetotray.key.menuitem”//this kind action just like a namespace. It assing a key attribute to the menuitem.
      oncommand=”window.extensions.mook.minimizetotray.minimizeWindow();”//Call the method defined before.


Conclusion: XUL is just like Html, and use Javascript to give the program functions. Use CSS to beautify your program.

At the same time we should know what is DTD, and how to use it. About XML, we should know how to read element/node or trverse them.






Build a Thunderbird add-on:

<%@ page language=”java” contentType=”text/html; charset=UTF-8″
        import=”java.sql.*”you should download the connector to connect to mysql from jsp.

Here we can know how to import java library to JSP

<%@ taglib uri=”” prefix=”display” %>

It use some java Decorator, it’s a free library.     Display:table   library.

It can do sorting, exporting, and many other thing,  —-Decorator Design Pattern, just for decorate the data, for example, in database data, a date :1908.09.20, and this Decorator class can transform it to 1908年09月20日.


Here we can know the most important part to

Connection con = null;
Statement stmt = null;
ResultSet rs;
String sql = “”;
List resultList = new ArrayList();

String reqType=”S”;
if (request.getParameter(“r”)!=null)
 reqType = request.getParameter(“r”);

use request.getParameter(“r”) to get the value send by Html form, as blow “green color”
String url=””;variables
if (reqType.equals(“S”))
 url = “<font color=’red’ size=’3′>Success</font>&nbsp;<a href=‘/report.jsp?r=P’>Pending</a>”;
else if (reqType.equals(“P”))
 url = “<a href=’/report.jsp?r=S’>Success</a>&nbsp;<font color=’red’ size=’3′>Pending</font>”;

try {
    Class.forName(“com.mysql.jdbc.Driver”).newInstance();create instance
    con = DriverManager.getConnection(“jdbc:mysql:///apccas2008″,”root”, “”);//use the driver connect to database mysql, and JSP programm just direct connect to Drive on local machine, this is much quicker than direct connect to mysql database (ODBC have the same sense)

    if(!con.isClosed()) {
  sql = “Select * from payment where paymentstatus='” + reqType + “‘ order by id desc”;
  rs = stmt.executeQuery(sql);
  while ( {

   Payment payment = new Payment();
  } catch(Exception e) {
  } finally {
    try {
      if(con != null)
    } catch(SQLException e) {

request.setAttribute( “paymentList”, resultList);
 <title>APCCAS 2008 Report</title>
 <link href=”css/screen.css” rel=”stylesheet” type=”text/css” />

the library refer to previously
<display:table name=”paymentList” export=”true” class=”ISIS” pagesize=”20″ defaultsort=”1″ defaultorder=”descending” decorator=””>
     APCCAS 2008 Registration – <%=url %>     
 <display:column property=”id” title=”ID”/>
 <display:column property=”userid” title=”User id”/>
 <display:column property=”amount” title=”Amount(MOP)”/>
 <display:column property=”amount_usd” title=”Amount(USD)”/>
 <display:column property=”paymentmethod” title=”Payment Method”/>
 <display:column property=”paymentstatus” title=”Payment Status”/>
 <display:column property=”serialno” title=”Serial No”/>
 <display:column property=”txn_date_time” title=”Date”/>  
 <display:column property=”title” title=”Title”/>
 <display:column property=”con_priceclass” title=”Price Class”/>  
 <display:column property=”ticket1″ title=”Welcome Reception”/>
 <display:column property=”ticket2″ title=”Conference Banquet”/>
 <display:column property=”paperid” title=”Paper id”/>
 <display:column property=”tut_role” title=”Tutorial Identity”/>
 <display:column property=”tutorial” title=”Tutorial”/>


<p align=”center”>
Generated by Mango Conference System

Model-view-controller (MVC) is an architectural pattern used in software engineering. Successful use of the pattern isolates business logic from user interface considerations, resulting in an application where it is easier to modify either the visual appearance of the application or the underlying business rules without affecting the other. In MVC, the model represents the information (the data) of the application and the business rules used to manipulate the data; the view corresponds to elements of the user interface such as text, checkbox items, and so forth; and the controller manages details involving the communication to the model of user actions such as keystrokes and mouse movements.

As an architectural pattern

It is common to split an application into separate layers that run on different computers: presentation (UI), domain logic, and data access. In MVC the presentation layer is further separated into view and controller.

MVC is often seen in web applications, where the view is the actual HTML page, and the controller is the code that gathers dynamic data and generates the content within the HTML. Finally, the model is represented by the actual content, usually stored in a database or in XML nodes, and the business rules that transform that content based on user actions.

Though MVC comes in different flavors, control flow generally works as follows:

  1. The user interacts with the user interface in some way (e.g. presses a button).
  2. A controller handles the input event from the user interface, often via a registered handler or callback.
  3. The controller notifies the model of the user action, possibly resulting in a change in the model’s state. (e.g. controller updates user’s Shopping cart).[3]
  4. A view uses the model (indirectly) to generate an appropriate user interface (e.g. the view produces a screen listing the shopping cart contents). The view gets its own data from the model. The model has no direct knowledge of the view.
  5. The user interface waits for further user interactions, which begins the cycle anew.

As a design pattern

MVC encompasses more of the architecture of an application than is typical for a design pattern.

The domain-specific representation of the information on which the application operates. Domain logic adds meaning to raw data (e.g., calculating whether today is the user’s birthday, or the totals, taxes, and shipping charges for shopping cart items).
Many applications use a persistent storage mechanism (such as a database) to store data. MVC does not specifically mention the data access layer because it is understood to be underneath or encapsulated by the Model.
Renders the model into a form suitable for interaction, typically a user interface element. Multiple views can exist for a single model for different purposes.
Processes and responds to events, typically user actions, and may invoke changes on the model


Alfresco Share – MVC

This product was divided into three part:

  1.  If you want to motify the data relatived, you just simple modify its XML file, and can refer to

For example: motify the login language.

    2.    modify the View, you should modify it’s JSP file, you can find in alfresco home directory.

    3.    modify the logic, you should compile the “Jar” file yourself, and you can find the path of these jar in Jsp code.  (java servlet/beans).


You can aware of two important design concept:    Hierarchy and Loosely Coupled.

Hierarchy:    different part have their own functions.

Loosely Coupled:   They are not interdependented.  Couple only through some interface (Or standard, such as XML).