Implementation of ASP. NET cache in petshop 4.0 (2)

Source: Internet
Author: User
Tags protected constructor

4.3.4 introduce the facade Mode

The facade mode can be used to encapsulate some complex logics to facilitate callers to call these complex logics. It seems that a uniform facade is provided, which encapsulates internal subsystems into a high-level interface. A typical facade mode is as follows:


Figure 4-4 facade Mode

The purpose of the facade mode is not to introduce a new function, but to provide a higher level of abstraction based on the existing function, so that the caller can directly call the function without worrying about the internal implementation method. Taking the cachedependency factory as an example, we need to provide the caller with a simple method to obtain the aggregatecachedependency object, so we have created the dependencyfacade class:
Public static class dependencyfacade
{
Private Static readonly string Path = configurationmanager. etettings ["cachedependencyassembly"];
Public static aggregatecachedependency getcategorydependency ()
{
If (! String. isnullorempty (PATH ))
Return dependencyaccess. createcategorydependency (). getdependency ();
Else
Return NULL;
}
Public static aggregatecachedependency getproductdependency ()
{
If (! String. isnullorempty (PATH ))
Return dependencyaccess. createproductdependency (). getdependency ();
Else
Return NULL;
}
Public static aggregatecachedependency getitemdependency ()
{
If (! String. isnullorempty (PATH ))
Return dependencyaccess. createitemdependency (). getdependency ();
Else
Return NULL;
}
}

The dependencyfacade class encapsulates the logic for obtaining the aggregatecachedependency type object. As a result, the caller can call relevant methods to obtain the aggregatecachedependency type object for creating the relevant dependency:
Aggregatecachedependency dependency = dependencyfacade. getcategorydependency ();

Compared to calling the getdependency () method of the dependencyaccess class directly, in addition to the simpler method, it also judges the cachedependencyassembly configuration section. If its value is null, a null object is returned.

In the app_code folder of petshop. Web, the dependencyfacade class is called by the getcategoryname () and getproductname () Methods of the static webutility class. For example, getcategoryname () method:
Public static string getcategoryname (string categoryid)
{
CATEGORY Category = new category ();
If (! Enablecaching)
Return category. getcategory (categoryid). Name;

String cachekey = string. Format (category_name_key, categoryid );

// Check whether the data item exists in the cache;
String data = (string) httpruntime. cache [cachekey];
If (Data = NULL)
{
// Obtain the duration value through the Web. config configuration;
Int cacheduration = int. parse (configurationmanager. receivettings ["categorycacheduration"]);
// If this data item does not exist in the cache, access the database through the business logic layer;
Data = category. getcategory (categoryid). Name;
// Create an aggregatecachedependency object through the facade class;
Aggregatecachedependency Cd = dependencyfacade. getcategorydependency ();
// Store data items and aggregatecachedependency objects in the cache;
Httpruntime. cache. Add (cachekey, Data, CD, datetime. Now. addhours (cacheduration), cache. nosdomainingexpiration, cacheitempriority. High, null );
}
Return data;
}

The getcategoryname () method first checks whether the categoryname data item already exists in the cache. If yes, the data is directly obtained through the cache; otherwise, the business logic layer calls the data access layer to access the database to obtain the categoryname. After obtaining the categoryname, the newly acquired data along with the aggregatecachedependency object created by the dependencyfacade class will be added to the cache.

The webutility static class is called by many pages of the presentation layer, such as the product page:
Public partial class products: system. Web. UI. Page
{
Protected void page_load (Object sender, eventargs E)
{
Page. Title = webutility. getcategoryname (request. querystring ["categoryid"]);
}
}

The logic for displaying the page title is put in the page_load event method. Therefore, each time you open the page, you must execute the method for obtaining the categoryname. If the caching mechanism is not used, when there is a large amount of category data, the page display will be very slow.

4.3.5 introduce proxy Mode

Business methods related to product, category, and item in bll in the business logic layer. The implementation logic is to call the data access layer (DAL) object to access the database to obtain relevant data. To improve system performance, we need to add the logic of the cache mechanism for these implementation methods. When we increase the cache mechanism for business objects, the caller should be consistent with the BLL business object call. That is to say, we need to introduce a new object to control the original BLL Business Object. This new object is the proxy object in proxy mode.

Taking petshop. BLL. product as an example, petshop creates a proxy object productdataproxy for it, and introduces a caching mechanism in methods such as getproductbycategory (). For example:
Public static class productdataproxy
{

Private Static readonly int producttimeout = int. parse (configurationmanager. deleettings ["productcacheduration"]);
Private Static readonly bool enablecaching = bool. parse (configurationmanager. deleettings ["enablecaching"]);

Public static ilist
Getproductsbycategory (string category)
{
Product = new product ();

If (! Enablecaching)
Return product. getproductsbycategory (category );

String key = "product_by_category _" + category;
Ilist DATA = (ilist) httpruntime. cache [Key];

// Check if the data exists in the data cache
If (Data = NULL)
{
Data = product. getproductsbycategory (category );

// Create a aggregatecachedependency object from the factory
Aggregatecachedependency Cd = dependencyfacade. getproductdependency ();

// Store the output in the data cache, and add the necessary aggregatecachedependency object
Httpruntime. cache. Add (Key, Data, CD, datetime. Now. addhours (producttimeout), cache. noslidingexpiration, cacheitempriority. High, null );
}
Return data;
}
}

Compared with the getproductsbycategory () method of the product object in the business logic layer, the cache mechanism is added. When no related data item exists in the cache, you can directly call the getproductsbycategory () method of the product in the business logic layer to obtain the data and store the data together with the corresponding aggregatecachedependency object in the cache.

The proxy mode is introduced to encapsulate business objects at the cache level and enhance control over business objects. Because the methods exposed outside the object are consistent, there is no substantive difference between the called proxy object and the real object for the caller.

From the perspective of separation of duties and hierarchical design, I prefer that these proxy objects are defined in the business logic layer, instead of being divided into the presentation layer UI as in the petshop design. In addition, if you need to consider the scalability and alternative of the program, we can also establish a unified interface or abstract class for the real object and the proxy object. However, from the perspective of the petshop presentation layer, it may be more reasonable to use static classes and static methods. We need to remember that "over-design" is a warning line for software design.

If you need to use a caching mechanism for the UI Layer to store application data in the cache, you can call these proxy objects. Take the productscontrol user control as an example. The call method is as follows:
Productslist. datasource = productdataproxy. getproductsbycategory (categorykey );

The productslist object belongs to the custom mlmlist type, which is a class derived from the system. Web. UI. webcontrols. datalist control. Its datasource attribute can accept the ilist set object.
However, in the design of petshop 4.0, for controls similar to the productscontrol type, the cache mechanism adopted is the page output cache. We can find clues from the source code on the productscontrol. ascx page:
<% @ Outputcache duration = "100000" varybyparam = "page; categoryid" %>

And ASP. net 1. different from the page output cache in ASP. NET 2.0, Asp.. Net user control introduces the cachepolicy attribute, which belongs to the controlcachepolicy class. It implements ASP programmatically.. Net user control output cache settings. You can set the dependencies related to the user control by setting the dependency attribute of the controlcachepolicy class. For example, in the productscontrol user control, set the following:
Protected void page_load (Object sender, eventargs E)
{
This. cachepolicy. Dependency = dependencyfacade. getproductdependency ();
}

The page output cache is used and the controlcachepolicy is used to set the output cache, so that business data and the entire page can be put into the cache. Compared with the application cache, this method greatly improves the performance. At the same time, it effectively avoids the disadvantages of "data expiration" through the introduced sqlcachedependency feature, so it is widely used in petshop 4.0. On the contrary, proxy objects previously created for product, category, and item business objects are "idle ", it is only used as a design method to demonstrate and "survive" with the source code of the entire system.

515) This. width = 515 "src =" file: // C: /docume % 7e1/zhangl/locals % 7e1/temp/moz-screenshot.jpg "alt =" "> As a B2C pet online store, petshop needs to fully consider the Guest user experience, if the response of the Web server is not timely due to a large amount of data, and the page and query data are delayed, the customer's Website access will be damaged, this part of customers may be lost. Undoubtedly, this is a very bad result. Therefore, when designing the system architecture, the performance of the entire system is very important. However, we cannot ignore data correctness because we focus on performance. In petshop 3.0 and earlier versions, this problem has not been well solved due to the limitations of ASP. NET cache. Petshop 4.0 introduces the sqlcachedependency feature, which greatly improves the system's processing of the cache.

4.3.1 cachedependency Interface

Petshop 4.0 introduces the sqlcachedependency feature and implements the SQL cache invalidation Technology for the cache corresponding to the category, product, and item data tables. When the corresponding data table changes, this technology can remove related items from the cache. The core of this technology is the sqlcachedependency class, which inherits the cachedependency class. However, to ensure the scalability of the entire architecture, we also allow designers to establish a custom cachedependency class to extend cache dependencies. Therefore, it is necessary to create an abstract interface for cachedependency and configure it in the web. config file.

In the namespace petshop. icachedependency of petshop 4.0, The ipetshopcachedependency interface is defined. It only contains one interface method:
Public interface ipetshopcachedependency
{
Aggregatecachedependency getdependency ();
}

Aggregatecachedependency is a new class added by. NET Framework 2.0. It monitors the collection of dependency objects. When any dependency object in this set changes, the cache object corresponding to the dependency object will be automatically removed.
The aggregatecachedependency class combines cachedependency objects. It can associate multiple cachedependency objects or even different types of cachedependency objects with cache items. Because petshop needs to create dependencies for the category, product, and item data tables, the ipetshopcachedependency interface method getdependency () is to return the aggregatecachedependency object with these dependencies established.

4.3.2 cachedependency implementation

The implementation of cachedependency is to create a corresponding sqlcachedependency type dependency for the category, product, and item data tables, as shown in the Code:
Public abstract class tabledependency: ipetshopcachedependency
{
// This is the separator that's used in Web. config
Protected char [] configurationseparator = new char [] {','};

Protected aggregatecachedependency dependency = new aggregatecachedependency ();
Protected tabledependency (string configkey)
{
String dbname = configurationmanager. receivettings ["cachedatabasename"];
String tableconfig = configurationmanager. configurettings [configkey];
String [] tables = tableconfig. Split (configurationseparator );

Foreach (string tablename in tables)
Dependency. Add (New sqlcachedependency (dbname, tablename ));
}
Public aggregatecachedependency getdependency ()
{
Return dependency;
}
}

The databases and data tables for which dependencies need to be created are configured in the web. config file. The settings are as follows:

Depending on the dependencies between data tables, the dependencies required for different data tables are also different. The value in the configuration file can be seen. However, no matter how many dependencies are created, the created behavior logic is similar. Therefore, a common class tabledependency is abstracted during design and a constructor with parameters is created, establish the dependency. In the implementation of the interface method getdependency (), the returned object dependency is created in a protected constructor. Therefore, the implementation method here can be seen as the flexible use of the template method mode. For example, the child product of tabledependency builds the sqlcachedependency dependency of the product and category data tables by using the constructor of the parent class:
Public class product: tabledependency
{
Public Product (): Base ("producttabledependency "){}
}

If you need to customize cachedependency, you can create dependencies in different ways. However, whether you create a sqlcachedependency object or a custom cachedependency object, you can add these dependencies to the aggregatecachedependency class. Therefore, you can also create a special class for the custom cachedependency, you only need to implement the ipetshopcachedependency interface.

4.3.3 cachedependency Factory

The product, category, and item classes that inherit the abstract class tabledependency must create their own objects during the call. Because their parent class tabledependency implements the ipetshopcachedependency interface, they also indirectly implement the ipetshopcachedependency interface, which provides a prerequisite for implementing the factory mode.

In petshop 4.0, the configuration file and reflection technology are still used to implement the factory model. In the namespace petshop. cachedependencyfactory, dependencyaccess is the factory class for creating ipetshopcachedependency objects:
Public static class dependencyaccess
{
Public static ipetshopcachedependency createcategorydependency ()
{
Return loadinstance ("category ");
}
Public static ipetshopcachedependency createproductdependency ()
{
Return loadinstance ("product ");
}
Public static ipetshopcachedependency createitemdependency ()
{
Return loadinstance ("item ");
}
Private Static ipetshopcachedependency loadinstance (string classname)
{
String Path = configurationmanager. etettings ["cachedependencyassembly"];
String fullyqualifiedclass = path + "." + classname;
Return (ipetshopcachedependency) Assembly. Load (PATH). createinstance (fullyqualifiedclass );
}
}
The implementation of the entire factory model is 4-3:

C: \ Documents and Settings \ zhangl \ Desktop \ 20061102063110194.gif
Figure 4-3 cachedependency Factory

Although the dependencyaccess class creates categories, products, and items that implement the ipetshopcachedependency interface, the purpose of introducing the ipetshopcachedependency interface is to obtain the aggregatecachedependency type object of the dependency item. We can call the object's interface method getdependency (), as shown below:
Aggregatecachedependency dependency = dependencyaccess. createcategorydependency (). getdependency ();

To facilitate callers, it seems that we can improve the dependencyaccess class and change the original createcategorydependency () method to the method for creating an aggregatecachedependency object.

However, this method disrupts the dependencyaccess responsibilities of the factory class, and the behavior of creating an ipetshopcachedependency interface object may still be called by the caller. Therefore, it is necessary to retain the original dependencyaccess class.

In the design of petshop 4.0, the facade mode is introduced to facilitate callers to obtain aggregatecachedependency type objects more easily.

4.3.4 introduce the facade Mode

The facade mode can be used to encapsulate some complex logics to facilitate callers to call these complex logics. It seems that a uniform facade is provided, which encapsulates internal subsystems into a high-level interface. A typical facade mode is as follows:

Figure 4-4 facade Mode

The purpose of the facade mode is not to introduce a new function, but to provide a higher level of abstraction based on the existing function, so that the caller can directly call the function without worrying about the internal implementation method. Taking the cachedependency factory as an example, we need to provide the caller with a simple method to obtain the aggregatecachedependency object, so we have created the dependencyfacade class:
Public static class dependencyfacade
{
Private Static readonly string Path = configurationmanager. etettings ["cachedependencyassembly"];
Public static aggregatecachedependency getcategorydependency ()
{
If (! String. isnullorempty (PATH ))
Return dependencyaccess. createcategorydependency (). getdependency ();
Else
Return NULL;
}
Public static aggregatecachedependency getproductdependency ()
{
If (! String. isnullorempty (PATH ))
Return dependencyaccess. createproductdependency (). getdependency ();
Else
Return NULL;
}
Public static aggregatecachedependency getitemdependency ()
{
If (! String. isnullorempty (PATH ))
Return dependencyaccess. createitemdependency (). getdependency ();
Else
Return NULL;
}
}

The dependencyfacade class encapsulates the logic for obtaining the aggregatecachedependency type object. As a result, the caller can call relevant methods to obtain the aggregatecachedependency type object for creating the relevant dependency:
Aggregatecachedependency dependency = dependencyfacade. getcategorydependency ();

Compared to calling the getdependency () method of the dependencyaccess class directly, in addition to the simpler method, it also judges the cachedependencyassembly configuration section. If its value is null, a null object is returned.

In the app_code folder of petshop. Web, the dependencyfacade class is called by the getcategoryname () and getproductname () Methods of the static webutility class. For example, getcategoryname () method:
Public static string getcategoryname (string categoryid)
{
CATEGORY Category = new category ();
If (! Enablecaching)
Return category. getcategory (categoryid). Name;

String cachekey = string. Format (category_name_key, categoryid );

// Check whether the data item exists in the cache;
String data = (string) httpruntime. cache [cachekey];
If (Data = NULL)
{
// Obtain the duration value through the Web. config configuration;
Int cacheduration = int. parse (configurationmanager. receivettings ["categorycacheduration"]);
// If this data item does not exist in the cache, access the database through the business logic layer;
Data = category. getcategory (categoryid). Name;
// Create an aggregatecachedependency object through the facade class;
Aggregatecachedependency Cd = dependencyfacade. getcategorydependency ();
// Store data items and aggregatecachedependency objects in the cache;
Httpruntime. cache. Add (cachekey, Data, CD, datetime. Now. addhours (cacheduration), cache. nosdomainingexpiration, cacheitempriority. High, null );
}
Return data;
}

The getcategoryname () method first checks whether the categoryname data item already exists in the cache. If yes, the data is directly obtained through the cache; otherwise, the business logic layer calls the data access layer to access the database to obtain the categoryname. After obtaining the categoryname, the newly acquired data along with the aggregatecachedependency object created by the dependencyfacade class will be added to the cache.

The webutility static class is called by many pages of the presentation layer, such as the product page:
Public partial class products: system. Web. UI. Page
{
Protected void page_load (Object sender, eventargs E)
{
Page. Title = webutility. getcategoryname (request. querystring ["categoryid"]);
}
}

The logic for displaying the page title is put in the page_load event method. Therefore, each time you open the page, you must execute the method for obtaining the categoryname. If the caching mechanism is not used, when there is a large amount of category data, the page display will be very slow.

4.3.5 introduce proxy Mode

Business methods related to product, category, and item in bll in the business logic layer. The implementation logic is to call the data access layer (DAL) object to access the database to obtain relevant data. To improve system performance, we need to add the logic of the cache mechanism for these implementation methods. When we increase the cache mechanism for business objects, the caller should be consistent with the BLL business object call. That is to say, we need to introduce a new object to control the original BLL Business Object. This new object is the proxy object in proxy mode.

Taking petshop. BLL. product as an example, petshop creates a proxy object productdataproxy for it, and introduces a caching mechanism in methods such as getproductbycategory (). For example:
Public static class productdataproxy
{

Private Static readonly int producttimeout = int. parse (configurationmanager. deleettings ["productcacheduration"]);
Private Static readonly bool enablecaching = bool. parse (configurationmanager. deleettings ["enablecaching"]);

Public static ilist
Getproductsbycategory (string category)
{
Product = new product ();

If (! Enablecaching)
Return product. getproductsbycategory (category );

String key = "product_by_category _" + category;
Ilist DATA = (ilist) httpruntime. cache [Key];

// Check if the data exists in the data cache
If (Data = NULL)
{
Data = product. getproductsbycategory (category );

// Create a aggregatecachedependency object from the factory
Aggregatecachedependency Cd = dependencyfacade. getproductdependency ();

// Store the output in the data cache, and add the necessary aggregatecachedependency object
Httpruntime. cache. Add (Key, Data, CD, datetime. Now. addhours (producttimeout), cache. noslidingexpiration, cacheitempriority. High, null );
}
Return data;
}
}

Compared with the getproductsbycategory () method of the product object in the business logic layer, the cache mechanism is added. When no related data item exists in the cache, you can directly call the getproductsbycategory () method of the product in the business logic layer to obtain the data and store the data together with the corresponding aggregatecachedependency object in the cache.

The proxy mode is introduced to encapsulate business objects at the cache level and enhance control over business objects. Because the methods exposed outside the object are consistent, there is no substantive difference between the called proxy object and the real object for the caller.

From the perspective of separation of duties and hierarchical design, I prefer that these proxy objects are defined in the business logic layer, instead of being divided into the presentation layer UI as in the petshop design. In addition, if you need to consider the scalability and alternative of the program, we can also establish a unified interface or abstract class for the real object and the proxy object. However, from the perspective of the petshop presentation layer, it may be more reasonable to use static classes and static methods. We need to remember that "over-design" is a warning line for software design.

If you need to use a caching mechanism for the UI Layer to store application data in the cache, you can call these proxy objects. Take the productscontrol user control as an example. The call method is as follows:
Productslist. datasource = productdataproxy. getproductsbycategory (categorykey );

The productslist object belongs to the custom mlmlist type, which is a class derived from the system. Web. UI. webcontrols. datalist control. Its datasource attribute can accept the ilist set object.
However, in the design of petshop 4.0, for controls similar to the productscontrol type, the cache mechanism adopted is the page output cache. We can find clues from the source code on the productscontrol. ascx page:
& Lt; % @ outputcache duration = "100000" varybyparam = "page; categoryid" % & gt;

And ASP. net 1. different from the page output cache in ASP. NET 2.0, Asp.. Net user control introduces the cachepolicy attribute, which belongs to the controlcachepolicy class. It implements ASP programmatically.. Net user control output cache settings. You can set the dependencies related to the user control by setting the dependency attribute of the controlcachepolicy class. For example, in the productscontrol user control, set the following:
Protected void page_load (Object sender, eventargs E)
{
This. cachepolicy. Dependency = dependencyfacade. getproductdependency ();
}

The page output cache is used and the controlcachepolicy is used to set the output cache, so that business data and the entire page can be put into the cache. Compared with the application cache, this method greatly improves the performance. At the same time, it effectively avoids the disadvantages of "data expiration" through the introduced sqlcachedependency feature, so it is widely used in petshop 4.0. On the contrary, proxy objects previously created for product, category, and item business objects are "idle ", it is only used as a design method to demonstrate and "survive" with the source code of the entire system.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.