By RAF CASERT, Associated Press Writer
Mon Apr 23, 4:50 PM ET
BRUSSELS, Belgium - Microsoft responded Monday to European Union allegations that it is overcharging rivals for information that would make their products work better with Windows. The software maker also repeated its request for more guidance on what regulators consider to be an acceptable price.
To level the software industry's playing field, EU officials want Microsoft's competitors to have access at a "reasonable cost" to material that would help their programs interoperate with Windows-based servers. Regulators have called the current prices excessive and the Microsoft's information insufficient.
The EU had given Microsoft until Monday night to come through with a response on the fees it seeks from competitors to share computer information, and threatened daily fines that could go as high as $4 million a day. It said it will consider the company's reply and decide whether to impose a daily penalty.
Microsoft declined to provide details about the company's response.
Microsoft previously said the prices it charges are fair and that the EU has failed to provide clear guidance.
Also Monday, Microsoft declined the opportunity to have a hearing with the Commission on the EU's Statement of Objections.
"We need greater clarity on what prices the (European) Commission wants us to charge, and we believe that is more likely to come from a constructive conversation than from a formal hearing," said Brad Smith, Microsoft's general counsel.
Microsoft's license program sets a maximum 5.95 percent royalty rate for products that use its server protocols and the company has said it believes the prices reflect the code's value. It claims the Commission wants it to license the technology to competitors for free.
On March 1, the EU's executive arm said there was "no significant innovation" in the requested information Microsoft had to provide rivals — and therefore Microsoft did not have the right to charge high fees for licenses.
Microsoft has complained that the treatment it receives from the 27-nation EU is unmatched around the world and hurt Europe's efforts to become a thriving high-tech economy.
In a landmark 2004 ruling, EU regulators found the company broke competition laws and abused its dominant market position.
Besides a record $674 million fine it imposed at the time of the ruling, the EU levied a $380 million fine last summer, saying Microsoft did not supply — as demanded — complete interoperability documentation.
In the meantime, Microsoft has reached licensing agreements with several of the companies that originally took issue with the software maker's practices and pricing, including Sun Microsystems Inc. and Novell Inc.
Microsoft has appealed the 2004 ruling and a court decision is expected by September.
Shares of Microsoft fell 24 cents to close at $28.78 on the Nasdaq Stock Market.
(AP)
Wednesday, April 25, 2007
Sunday, April 22, 2007
ASP.NET 2.0 Caching Features
ASP.NET 2.0 Caching Features
Thiru Thangarathinam
In ASP.NET 2.0, caching has been improved in a couple of notable ways. Probably the most interesting feature is the introduction of database-triggered cache invalidation. In ASP.NET 1.x, you can invalidate a cached item based on some pre-defined conditions such as change in an XML file or change in another cache item. Using this feature, you can remove or invalidate an item from the cache when the data or another cached item changes. However, the ASP.NET 1.x Cache API does not allow you to invalidate an item in the cache when data in a SQL Server database changes. This is a very common capability most applications will require. ASP.NET 2.0 addresses this by providing the database triggered cache invalidation capability to ensure that the items in the cache are kept up-to-date with the changes in the database. You can accomplish this using any one of the following methods.
Declarative Output caching - This is similar to declarative output caching in ASP.NET 1.x, wherein you configure caching by specifying the OutputCache directive and their related attributes.
Programmatic Output caching - In this method, you will use the SqlCacheDependency object programmatically to specify the items to be cached and set their attributes.
Cache API - In this option, you will use the static methods of the Cache class such as Insert, Remove, Add and so on to add or remove items from the ASP.NET cache, while still using the SqlCacheDependency object to trigger the cache invalidation.
Another important caching feature in ASP.NET 2.0 is the ability to create custom cache dependencies, which is not possible with ASP.NET 1.x Cache API. To accomplish this, you need to inherit from the CacheDependency class. Since the CacheDependency is a sealed class in ASP.NET 1.x, you can't inherit and extend it. However, in ASP.NET 2.0, this is no longer the case. You can inherit from CacheDependency class and create your own custom cache dependencies. This opens up a world of opportunities where you can roll your own custom cache dependencies required for a particular class of applications. For example, you can create a StockPriceCacheDependency class that automatically invalidates the cached data when the stock price changes.
SQL Server-Based Cache Invalidation Mechanism
The SQL Server based cache invalidation mechanism works with SQL Server 7.0 and above. However, with SQL Server 7.0 and 2000, only Table level cache invalidation mechanism is supported. This means that the cached items will be automatically invalidated any time the data in the table changes. The next release of SQL Server (code-named Yukon) will also feature row-level cache invalidation mechanism, providing a finer level of accuracy over the cached data.
In SQL Server 7 and SQL Server 2000, table level cache invalidation is supported using a polling system. Through this system, the ASP.NET process will poll the database (pull model) every so many seconds to check and see which tables have changed since it last checked. Even though the pull model works for most cases, it is not an efficient approach. However, this will be enhanced in Yukon to have Yukon actually notify (Push model) ASP.NET whenever a particular row of data has been modified. Yukon accomplishes this by using a feature called Notification Delivery Services (that uses ports 80), which directly interacts with HTTP.SYS of IIS 6.0 to notify the Web server of updates to the specific rows. For the purposes of this article, you will consider SQL Server 7 and 2000 and understand how to configure caching for those versions.
Before you can establish cache dependency with SQL Server 7 or SQL Server 2000, you need to perform the following steps.
You must haveelement in the configuration file (web.config)
You also need to perform one-time setup of the tables or databases you want to monitor using either the aspnet_regsqlcache utility or the EnableTableForNotifications method.
After you have completed the above steps, ASP.NET can start invalidating the data in the cache when the SQL Server data changes, which is accomplished by a polling mechanism approach. Note that with Yukon, the above steps are not required. Before looking at the three different ways to enable SQL Server based caching, you should understand the steps that are required for the caching to work.
First and foremost, you need to ensure your web.config file contains the appropriate cache related settings. The web.config file should contain a cache element as shown below.
???
???????
???
???
?????? ??
?????? ?? ???
?????? ???????????
???????????????????? ???? ? ?
????????????? ?????
?????????????
? ???? ????
???
?????? ---------
?????? ---------
In the above configuration entries, you specify the name of the database in which you want to enable the cache notification mechanism using theelement. As you can see, there is a new section in web.config called in which the connection strings to the database is added. Once you add the connectionString to the connectionStrings section, you can then reference it from the sqlcachedependency/databases section.
Next step is to enable the specific tables in the Northwind database for notification. You can perform this using any one of the following two ways:
Using the aspnet_regsqlcache utiltity. You will see an example of this shortly.
Using the EnableTableForNotifications method of the SqlCacheDependencyAdmin class.
Once you configure the table to send notifications, any time data in the table changes, it notifies ASP.NET to invalidate the specific item in the cache. For the purposes of this article, consider the aspnet_regsqlcache utility to configure the tables. Basically this utility creates an extra table named AspNet_SqlCacheTablesForChangeNotification that is used to keep track of the changes to all the monitored tables in the database. It also creates a number of triggers and stored procedures to enable this capability. To run the aspnet_regsqlcache utility, open up the Visual Studio .NET command prompt and enter the command shown in the following screenshot.
In the above command:
S - Name of the Server
U - User ID to use to connect to the SQL Server
P - Password to use to connect to the SQL Server
d - Specifies the name of the database
t - Table to configure
et - enables the tables for SQL Server database triggered invalidation
As mentioned before, you need to follow the above steps only when you use SQL Server 7 or SQL Server 2000. If you are using the next version of SQL Server (code-named Yukon), the above configurations are not necessary. Moreover the cache invalidating mechanism works through a highly efficient notification model, wherein the Notification Delivery Service component of SQL Server directly notifies IIS using TCP Port 80 when the data in a SQL Server changes.
Now that you understood the steps required for enabling this, take a look at the different ways of caching ASP.NET pages so that it takes advantage of SQL Server trigger based cache invalidation mechanism.
Declaratively enabling caching using OutputCache directive
In this section, you will see how to enable output caching declaratively using the OutputCache directive. Here's the complete code of the ASP.NET page.
???????
?????? DataSet categories = new DataSet();
?????? adapter.Fill(categories);
?????? SqlCacheDependency dependency = new
SqlCacheDependency("Northwind", "Categories");
?????? Response.AddCacheDependency(dependency);
?????? Response.Cache.SetValidUntilExpires(true);
?????? Response.Cache.SetExpires(DateTime.Now.AddMinutes(60));
?? ????Response.Cache.SetCacheability(HttpCacheability.Public);
?????? gridCategories.DataSource = categories;
?????? gridCategories.DataBind();????????????????
?????? Response.Write("Page created on : "? + DateTime.Now.ToString());??????????????
??? }???
???
???
???????
???????
?? ?
The above code is similar to the previous example except that the caching is performed programmatically. In the Page_Load event, you create an instance of the SqlConnection object and pass in the connection string as an argument. To retrieve the connection string from the web.config file, you use one of the new classes supplied by ASP.NET 2.0, named ConnectionStrings, that provides helper properties to retrieve the connection strings specified in the connectionStrings section of the web.config file. You then create an instance of the SqlDataAdapter object passing in the SQL to be executed and the SqlConnection object as its arguments. After that you create an instance of the DataSet object and then fill the dataset object by invoking the Fill method of the SqlDataAdapter object. Then you create an instance of the class named SqlCacheDependency passing in the database and the table to be monitored as its arguments. You also add the SqlCacheDependency object to the Response object using the AddCacheDependency method. After that, you set various attributes of the Cache object. Finally, you bind the output of the returned data to the GridView control. Navigating to the above page results in an output that is similar to our previous example. To test this page, change the data in the categories table and see the change in the date time displayed in the page.
Cache API Example
So far, you have seen how to use the output caching declaratively and programmatically to enable caching on ASP.NET pages. In this section, you will see how to use the Cache API to accomplish the same functionality. As in ASP.NET 1.x, the Cache API is very powerful in that it not only provides complete control over how items are cached but also enables the execution of some code when an item is removed or invalidated from the cache. The following code shows an example of using Cache API to control caching for an ASP.NET page.
?
??? void Page_Load(object sender, System.EventArgs e)
??? {???????
??????? DataSet categories;???????
??????? categories = (DataSet)Cache["Categories"];
??????? if (categories == null)
??????? {
??????????? SqlConnection conn = new
?SqlConnection(
ConfigurationSettings.ConnectionStrings["Northwind"]);
??????????? SqlDataAdapter adapter = new
SqlDataAdapter("Select * from Categories", conn);
??????????? categories = new DataSet();
??????????? adapter.Fill(categories);
??????????? SqlCacheDependency dependency = new
????????????? SqlCacheDependency("Northwind", "Categories");
??????????? Cache.Insert("Categories", categories, dependency);
??????????? Response.Write("Categories retrieved from the database");???????
??????? }
??????? else
??????????? Response.Write("Categories retrieved from the Cache");???????
???????
??????? gridCategories.DataSource = categories;
??????? gridCategories.DataBind();????????????????????????
??? }
???
???
???
????????
???????
???
The above code is very similar to the previous example, except in this case, the Insert method of the Cache class is used to add items to the cache. In the above code, you start by creating an instance of the SqlConnection object passing in the connection string that is retrieved from the web.config file. Then you create an instance of the SqlDataAdapter object and pass in the SQL statement to be executed and the previously created SqlConnection object as its arguments. Then you execute the SQL query using the Fill method of the SqlDataAdapter object. After that you create an instance of the SqlCacheDependency object and supply the database name (that corresponds to the database name specified in the web.config file) and the table name as its arguments. Then you insert the categories dataset to the cache using the Insert method of the Cache object. At the time of inserting, you should also specify the SqlCacheDependency object so that the categories dataset can be invalidated when the data in the categories table changes. Finally, you sould bind the categories dataset to the GridView control.
When you navigate to the above page using the browser, you get the following output, which clearly shows that for the first time the categories information is retrieved from the database.
If you refresh the browser, you will see the following output, in which the categories information is retrieved from the cache.
To test if the SQL Server based cache invalidation mechanism works, modify the data in the Categories table and then if you navigate to the page using the browser, you will get a message stating that the categories is retrieved from the database.
Creating Custom Cache Dependencies
So far, you have used the built-in SqlCacheDependency class for invalidating the cached item when the data in the SQL Server database changes. Even though this approach is very useful, there are times you might want to create your own custom cache dependency. For example, when the stock price changes, you might want to invalidate an item in the cache. ASP.NET 2.0 Cache API enables these types of scenarios by providing the ability to create custom cache dependency classes that are inherited from the CacheDependency class.
Conclusion
The Cache API introduced with ASP.NET 1.0 was a powerful feature that could be immensely useful in increasing the performance of a Web application. The Cache API in ASP.NET 2.0 builds on the foundation provided by the ASP.NET 1.0 and makes it extremely easy and seamless to build high performance ASP.NET applications. Being able to invalidate a cached item when the data in the database changes is a capability that can go a long way in revolutionizing the way ASP.NET applications are built and deployed. Furthermore, the ability to create custom cache dependencies (when one of the built-in cache dependency classes does not suit your needs) enables a whole lot of impressive caching scenarios for the developers to take advantage of.
About the Author
Thiru has many years of experience in architecting, designing, developing and implementing applications using Object Oriented Application development methodologies. He also possesses a thorough understanding of software life cycle (design, development and testing).
He is an expert with ASP.NET, .NET Framework, Visual C#.NET, Visual Basic.NET, ADO.NET, XML Web Services and .NET Remoting and holds MCAD for .NET, MCSD and MCP certifications.
Thiru has authored numerous books and articles. He can be reached at thiruthangarathinam@yahoo.com.
Back to article
Copyright 2005 Jupitermedia Corp. All Rights Reserved.
Legal Notices, Licensing, Reprints, & Permissions, Privacy Policy.
http://www.internet.com
Thiru Thangarathinam
In ASP.NET 2.0, caching has been improved in a couple of notable ways. Probably the most interesting feature is the introduction of database-triggered cache invalidation. In ASP.NET 1.x, you can invalidate a cached item based on some pre-defined conditions such as change in an XML file or change in another cache item. Using this feature, you can remove or invalidate an item from the cache when the data or another cached item changes. However, the ASP.NET 1.x Cache API does not allow you to invalidate an item in the cache when data in a SQL Server database changes. This is a very common capability most applications will require. ASP.NET 2.0 addresses this by providing the database triggered cache invalidation capability to ensure that the items in the cache are kept up-to-date with the changes in the database. You can accomplish this using any one of the following methods.
Declarative Output caching - This is similar to declarative output caching in ASP.NET 1.x, wherein you configure caching by specifying the OutputCache directive and their related attributes.
Programmatic Output caching - In this method, you will use the SqlCacheDependency object programmatically to specify the items to be cached and set their attributes.
Cache API - In this option, you will use the static methods of the Cache class such as Insert, Remove, Add and so on to add or remove items from the ASP.NET cache, while still using the SqlCacheDependency object to trigger the cache invalidation.
Another important caching feature in ASP.NET 2.0 is the ability to create custom cache dependencies, which is not possible with ASP.NET 1.x Cache API. To accomplish this, you need to inherit from the CacheDependency class. Since the CacheDependency is a sealed class in ASP.NET 1.x, you can't inherit and extend it. However, in ASP.NET 2.0, this is no longer the case. You can inherit from CacheDependency class and create your own custom cache dependencies. This opens up a world of opportunities where you can roll your own custom cache dependencies required for a particular class of applications. For example, you can create a StockPriceCacheDependency class that automatically invalidates the cached data when the stock price changes.
SQL Server-Based Cache Invalidation Mechanism
The SQL Server based cache invalidation mechanism works with SQL Server 7.0 and above. However, with SQL Server 7.0 and 2000, only Table level cache invalidation mechanism is supported. This means that the cached items will be automatically invalidated any time the data in the table changes. The next release of SQL Server (code-named Yukon) will also feature row-level cache invalidation mechanism, providing a finer level of accuracy over the cached data.
In SQL Server 7 and SQL Server 2000, table level cache invalidation is supported using a polling system. Through this system, the ASP.NET process will poll the database (pull model) every so many seconds to check and see which tables have changed since it last checked. Even though the pull model works for most cases, it is not an efficient approach. However, this will be enhanced in Yukon to have Yukon actually notify (Push model) ASP.NET whenever a particular row of data has been modified. Yukon accomplishes this by using a feature called Notification Delivery Services (that uses ports 80), which directly interacts with HTTP.SYS of IIS 6.0 to notify the Web server of updates to the specific rows. For the purposes of this article, you will consider SQL Server 7 and 2000 and understand how to configure caching for those versions.
Before you can establish cache dependency with SQL Server 7 or SQL Server 2000, you need to perform the following steps.
You must have
You also need to perform one-time setup of the tables or databases you want to monitor using either the aspnet_regsqlcache utility or the EnableTableForNotifications method.
After you have completed the above steps, ASP.NET can start invalidating the data in the cache when the SQL Server data changes, which is accomplished by a polling mechanism approach. Note that with Yukon, the above steps are not required. Before looking at the three different ways to enable SQL Server based caching, you should understand the steps that are required for the caching to work.
First and foremost, you need to ensure your web.config file contains the appropriate cache related settings. The web.config file should contain a cache element as shown below.
???
???????
???
???
?????? ??
?????? ?? ???
?????? ???????????
???????????????????? ???? ? ?
????????????? ?????
?????????????
? ???? ????
???
?????? ---------
?????? ---------
In the above configuration entries, you specify the name of the database in which you want to enable the cache notification mechanism using the
Next step is to enable the specific tables in the Northwind database for notification. You can perform this using any one of the following two ways:
Using the aspnet_regsqlcache utiltity. You will see an example of this shortly.
Using the EnableTableForNotifications method of the SqlCacheDependencyAdmin class.
Once you configure the table to send notifications, any time data in the table changes, it notifies ASP.NET to invalidate the specific item in the cache. For the purposes of this article, consider the aspnet_regsqlcache utility to configure the tables. Basically this utility creates an extra table named AspNet_SqlCacheTablesForChangeNotification that is used to keep track of the changes to all the monitored tables in the database. It also creates a number of triggers and stored procedures to enable this capability. To run the aspnet_regsqlcache utility, open up the Visual Studio .NET command prompt and enter the command shown in the following screenshot.
In the above command:
S - Name of the Server
U - User ID to use to connect to the SQL Server
P - Password to use to connect to the SQL Server
d - Specifies the name of the database
t - Table to configure
et - enables the tables for SQL Server database triggered invalidation
As mentioned before, you need to follow the above steps only when you use SQL Server 7 or SQL Server 2000. If you are using the next version of SQL Server (code-named Yukon), the above configurations are not necessary. Moreover the cache invalidating mechanism works through a highly efficient notification model, wherein the Notification Delivery Service component of SQL Server directly notifies IIS using TCP Port 80 when the data in a SQL Server changes.
Now that you understood the steps required for enabling this, take a look at the different ways of caching ASP.NET pages so that it takes advantage of SQL Server trigger based cache invalidation mechanism.
Declaratively enabling caching using OutputCache directive
In this section, you will see how to enable output caching declaratively using the OutputCache directive. Here's the complete code of the ASP.NET page.
???????
?????? DataSet categories = new DataSet();
?????? adapter.Fill(categories);
?????? SqlCacheDependency dependency = new
SqlCacheDependency("Northwind", "Categories");
?????? Response.AddCacheDependency(dependency);
?????? Response.Cache.SetValidUntilExpires(true);
?????? Response.Cache.SetExpires(DateTime.Now.AddMinutes(60));
?? ????Response.Cache.SetCacheability(HttpCacheability.Public);
?????? gridCategories.DataSource = categories;
?????? gridCategories.DataBind();????????????????
?????? Response.Write("Page created on : "? + DateTime.Now.ToString());??????????????
??? }???
???
???
???????
???????
?? ?
The above code is similar to the previous example except that the caching is performed programmatically. In the Page_Load event, you create an instance of the SqlConnection object and pass in the connection string as an argument. To retrieve the connection string from the web.config file, you use one of the new classes supplied by ASP.NET 2.0, named ConnectionStrings, that provides helper properties to retrieve the connection strings specified in the connectionStrings section of the web.config file. You then create an instance of the SqlDataAdapter object passing in the SQL to be executed and the SqlConnection object as its arguments. After that you create an instance of the DataSet object and then fill the dataset object by invoking the Fill method of the SqlDataAdapter object. Then you create an instance of the class named SqlCacheDependency passing in the database and the table to be monitored as its arguments. You also add the SqlCacheDependency object to the Response object using the AddCacheDependency method. After that, you set various attributes of the Cache object. Finally, you bind the output of the returned data to the GridView control. Navigating to the above page results in an output that is similar to our previous example. To test this page, change the data in the categories table and see the change in the date time displayed in the page.
Cache API Example
So far, you have seen how to use the output caching declaratively and programmatically to enable caching on ASP.NET pages. In this section, you will see how to use the Cache API to accomplish the same functionality. As in ASP.NET 1.x, the Cache API is very powerful in that it not only provides complete control over how items are cached but also enables the execution of some code when an item is removed or invalidated from the cache. The following code shows an example of using Cache API to control caching for an ASP.NET page.
?
??? void Page_Load(object sender, System.EventArgs e)
??? {???????
??????? DataSet categories;???????
??????? categories = (DataSet)Cache["Categories"];
??????? if (categories == null)
??????? {
??????????? SqlConnection conn = new
?SqlConnection(
ConfigurationSettings.ConnectionStrings["Northwind"]);
??????????? SqlDataAdapter adapter = new
SqlDataAdapter("Select * from Categories", conn);
??????????? categories = new DataSet();
??????????? adapter.Fill(categories);
??????????? SqlCacheDependency dependency = new
????????????? SqlCacheDependency("Northwind", "Categories");
??????????? Cache.Insert("Categories", categories, dependency);
??????????? Response.Write("Categories retrieved from the database");???????
??????? }
??????? else
??????????? Response.Write("Categories retrieved from the Cache");???????
???????
??????? gridCategories.DataSource = categories;
??????? gridCategories.DataBind();????????????????????????
??? }
???
???
???
????????
???????
???
The above code is very similar to the previous example, except in this case, the Insert method of the Cache class is used to add items to the cache. In the above code, you start by creating an instance of the SqlConnection object passing in the connection string that is retrieved from the web.config file. Then you create an instance of the SqlDataAdapter object and pass in the SQL statement to be executed and the previously created SqlConnection object as its arguments. Then you execute the SQL query using the Fill method of the SqlDataAdapter object. After that you create an instance of the SqlCacheDependency object and supply the database name (that corresponds to the database name specified in the web.config file) and the table name as its arguments. Then you insert the categories dataset to the cache using the Insert method of the Cache object. At the time of inserting, you should also specify the SqlCacheDependency object so that the categories dataset can be invalidated when the data in the categories table changes. Finally, you sould bind the categories dataset to the GridView control.
When you navigate to the above page using the browser, you get the following output, which clearly shows that for the first time the categories information is retrieved from the database.
If you refresh the browser, you will see the following output, in which the categories information is retrieved from the cache.
To test if the SQL Server based cache invalidation mechanism works, modify the data in the Categories table and then if you navigate to the page using the browser, you will get a message stating that the categories is retrieved from the database.
Creating Custom Cache Dependencies
So far, you have used the built-in SqlCacheDependency class for invalidating the cached item when the data in the SQL Server database changes. Even though this approach is very useful, there are times you might want to create your own custom cache dependency. For example, when the stock price changes, you might want to invalidate an item in the cache. ASP.NET 2.0 Cache API enables these types of scenarios by providing the ability to create custom cache dependency classes that are inherited from the CacheDependency class.
Conclusion
The Cache API introduced with ASP.NET 1.0 was a powerful feature that could be immensely useful in increasing the performance of a Web application. The Cache API in ASP.NET 2.0 builds on the foundation provided by the ASP.NET 1.0 and makes it extremely easy and seamless to build high performance ASP.NET applications. Being able to invalidate a cached item when the data in the database changes is a capability that can go a long way in revolutionizing the way ASP.NET applications are built and deployed. Furthermore, the ability to create custom cache dependencies (when one of the built-in cache dependency classes does not suit your needs) enables a whole lot of impressive caching scenarios for the developers to take advantage of.
About the Author
Thiru has many years of experience in architecting, designing, developing and implementing applications using Object Oriented Application development methodologies. He also possesses a thorough understanding of software life cycle (design, development and testing).
He is an expert with ASP.NET, .NET Framework, Visual C#.NET, Visual Basic.NET, ADO.NET, XML Web Services and .NET Remoting and holds MCAD for .NET, MCSD and MCP certifications.
Thiru has authored numerous books and articles. He can be reached at thiruthangarathinam@yahoo.com.
Back to article
Copyright 2005 Jupitermedia Corp. All Rights Reserved.
Legal Notices, Licensing, Reprints, & Permissions, Privacy Policy.
http://www.internet.com
Wednesday, April 4, 2007
Tip/Trick: Url Rewriting with ASP.NET
(http://weblogs.asp.net/scottgu/archive/2007/02/26/tip-trick-url-rewriting-with-asp-net.aspx)
People often ask me for guidance on how they can dynamically "re-write" URLs and/or have the ability to publish cleaner URL end-points within their ASP.NET web applications. This blog post summarizes a few approaches you can take to cleanly map or rewrite URLs with ASP.NET, and have the option to structure the URLs of your application however you want.
Why does URL mapping and rewriting matter?
The most common scenarios where developers want greater flexibility with URLs are:
1) Handling cases where you want to restructure the pages within your web application, and you want to ensure that people who have bookmarked old URLs don't break when you move pages around. Url-rewriting enables you to transparently forward requests to the new page location without breaking browsers.
2) Improving the search relevancy of pages on your site with search engines like Google, Yahoo and Live. Specifically, URL Rewriting can often make it easier to embed common keywords into the URLs of the pages on your sites, which can often increase the chance of someone clicking your link. Moving from using querystring arguments to instead use fully qualified URL's can also in some cases increase your priority in search engine results. Using techniques that force referring links to use the same case and URL entrypoint (for example: weblogs.asp.net/scottgu instead of weblogs.asp.net/scottgu/default.aspx) can also avoid diluting your pagerank across multiple URLs, and increase your search results.
In a world where search engines increasingly drive traffic to sites, extracting any little improvement in your page ranking can yield very good ROI to your business. Increasingly this is driving developers to use URL-Rewriting and other SEO (search engine optimization) techniques to optimize sites (note that SEO is a fast moving space, and the recommendations for increasing your search relevancy evolve monthly). For a list of some good search engine optimization suggestions, I'd recommend reading the SSW Rules to Better Google Rankings, as well as MarketPosition's article on how URLs can affect top search engine ranking.
Sample URL Rewriting Scenario
For the purpose of this blog post, I'm going to assume we are building a set of e-commerce catalog pages within an application, and that the products are organized by categories (for example: books, videos, CDs, DVDs, etc).
Let's assume that we initially have a page called "Products.aspx" that takes a category name as a querystring argument, and filters the products accordingly. The corresponding URLs to this Products.aspx page look like this:
http://www.store.com/products.aspx?category=bookshttp://www.store.com/products.aspx?category=DVDshttp://www.store.com/products.aspx?category=CDs
Rather than use a querystring to expose each category, we want to modify the application so that each product category looks like a unique URL to a search engine, and has the category keyword embedded in the actual URL (and not as a querystring argument). We'll spend the rest of this blog post going over 4 different approaches that we could take to achieve this.
Approach 1: Use Request.PathInfo Parameters Instead of QueryStrings
The first approach I'm going to demonstrate doesn't use Url-Rewriting at all, and instead uses a little-known feature of ASP.NET - the Request.PathInfo property. To help explain the usefulness of this property, consider the below URL scenario for our e-commerce store:
http://www.store.com/products.aspx/Bookshttp://www.store.com/products.aspx/DVDshttp://www.store.com/products.aspx/CDs
One thing you'll notice with the above URLs is that they no longer have Querystring values - instead the category parameter value is appended on to the URL as a trailing /param value after the Products.aspx page handler name. An automated search engine crawler will then interpret these URLs as three different URLs, and not as one URL with three different input values (search engines ignore the filename extension and just treat it as another character within the URL).
You might wonder how you handle this appended parameter scenario within ASP.NET. The good news is that it is pretty simple. Simply use the Request.PathInfo property, which will return the content immediately following the products.aspx portion of the URL. So for the above URLs, Request.PathInfo would return "/Books", "/DVDs", and "/CDs" (in case you are wondering, the Request.Path property would return "/products.aspx").
You could then easily write a function to retrieve the category like so (the below function strips out the leading slash and returning just "Books", "DVDs" or "CDs"):
Function GetCategory() As String
If (Request.PathInfo.Length = 0) Then
Return ""
Else
Return Request.PathInfo.Substring(1)
End If
End Function
Sample Download: A sample application that I've built that shows using this technique can be downloaded here. What is nice about this sample and technique is that no server configuration changes are required in order to deploy an ASP.NET application using this approach. It will also work fine in a shared hosting environment.
Approach 2: Using an HttpModule to Perform URL Rewriting
An alternative approach to the above Request.PathInfo technique would be to take advantage of the HttpContext.RewritePath() method that ASP.NET provides. This method allows a developer to dynamically rewrite the processing path of an incoming URL, and for ASP.NET to then continue executing the request using the newly re-written path.
For example, we could choose to expose the following URLs to the public:
http://www.store.com/products/Books.aspxhttp://www.store.com/products/DVDs.aspxhttp://www.store.com/products/CDs.aspx
This looks to the outside world like there are three separate pages on the site (and will look great to a search crawler). By using the HttpContext.RewritePath() method we can dynamically re-write the incoming URLs when they first reach the server to instead call a single Products.aspx page that takes the category name as a Querystring or PathInfo parameter instead. For example, we could use an an Application_BeginRequest event in Global.asax like so to do this:
void Application_BeginRequest(object sender, EventArgs e) {
string fullOrigionalpath = Request.Url.ToString();
if (fullOrigionalpath.Contains("/Products/Books.aspx"))
{
Context.RewritePath("/Products.aspx?Category=Books");
}
else if (fullOrigionalpath.Contains("/Products/DVDs.aspx"))
{
Context.RewritePath("/Products.aspx?Category=DVDs");
}
}
The downside of manually writing code like above is that it can be tedious and error prone. Rather than do it yourself, I'd recommend using one of the already built HttpModules available on the web for free to perform this work for you. Here a few free ones that you can download and use today:
UrlRewriter.net
UrlRewriting.net
These modules allow you to declaratively express matching rules within your application's web.config file. For example, to use the UrlRewriter.Net module within your application's web.config file to map the above URLs to a single Products.aspx page, we could simply add this web.config file to our application (no code is required):
The HttpModule URL rewriters above also add support for regular expression and URL pattern matching (to avoid you having to hard-code every URL in your web.config file). So instead of hard-coding the category list, you could re-write the rules like below to dynamically pull the category from the URL for any "/products/[category].aspx" combination:
This makes your code much cleaner and super extensible.
Sample Download: A sample application that I've built that shows using this technique with the UrlRewriter.Net module can be downloaded here.
What is nice about this sample and technique is that no server configuration changes are required in order to deploy an ASP.NET application using this approach. It will also work fine in a medium trust shared hosting environment (just ftp/xcopy to the remote server and you are good to go - no installation required).
Approach 3: Using an HttpModule to Perform Extension-Less URL Rewriting with IIS7
The above HttpModule approach works great for scenarios where the URL you are re-writing has a .aspx extension, or another file extension that is configured to be processed by ASP.NET. When you do this no custom server configuration is required - you can just copy your web application up to a remote server and it will work fine.
There are times, though, when you want the URL to re-write to either have a non-ASP.NET file extension (for example: .jpg, .gif, or .htm) or no file-extension at all. For example, we might want to expose these URLs as our public catalog pages (note they have no .aspx extension):
http://www.store.com/products/Bookshttp://www.store.com/products/DVDshttp://www.store.com/products/CDs
With IIS5 and IIS6, processing the above URLs using ASP.NET is not super easy. IIS 5/6 makes it hard to perform URL rewriting on these types of URLs within ISAPI Extensions (which is how ASP.NET is implemented). Instead you need to perform the rewriting earlier in the IIS request pipeline using an ISAPI Filter. I'll show how to-do this on IIS5/6 in the Approach 4 section below.
The good news, though, is that IIS 7.0 makes handling these types of scenarios super easy. You can now have an HttpModule execute anywhere within the IIS request pipeline - which means you can use the URLRewriter module above to process and rewrite extension-less URLs (or even URLs with a .asp, .php, or .jsp extension). Below is how you would configure this with IIS7:
Note the "runAllManagedModulesForAllRequests" attribute that is set to true on the section within . This will ensure that the UrlRewriter.Net module from Intelligencia, which was written before IIS7 shipped, will be called and have a chance to re-write all URL requests to the server (including for folders). What is really cool about the above web.config file is that:
1) It will work on any IIS 7.0 machine. You don't need an administrator to enable anything on the remote host. It will also work in medium trust shared hosting scenarios.
2) Because I've configured the UrlRewriter in both the and IIS7 section, I can use the same URL Rewriting rules for both the built-in VS web-server (aka Cassini) as well as on IIS7. Both fully support extension-less URLRewriting. This makes testing and development really easy.
IIS 7.0 server will ship later this year as part of Windows Longhorn Server, and will support a go-live license with the Beta3 release in a few weeks. Because of all the new hosting features that have been added to IIS7, we expect hosters to start aggressively offering IIS7 accounts relatively quickly - which means you should be able to start to take advantage of the above extension-less rewriting support soon. We'll also be shipping a Microsoft supported URL-Rewriting module in the IIS7 RTM timeframe that will be available for free as well that you'll be able to use on IIS7, and which will provide nice support for advanced re-writing scenarios for all content on your web-server.
Sample Download: A sample application that I've built that shows using this extension-less URL technique with IIS7 and the UrlRewriter.Net module can be downloaded here.
Approach 4: ISAPIRewrite to enable Extension-less URL Rewriting for IIS5 and IIS6
If you don't want to wait for IIS 7.0 in order to take advantage of extension-less URL Rewriting, then your best best is to use an ISAPI Filter in order to re-write URLs. There are two ISAPI Filter solutions that I'm aware of that you might want to check-out:
Helicon Tech's ISAPI Rewrite: They provide an ISAPI Rewrite full product version for $99 (with 30 day free trial), as well as a ISAPI Rewrite lite edition that is free.
Ionic's ISAPI Rewrite: This is a free download (both source and binary available)
I actually don't have any first-hand experience using either of the above solutions - although I've heard good things about them. Scott Hanselman and Jeff Atwood recently both wrote up great blog posts about their experiences using them, and also provided some samples of how to configure the rules for them. The rules for Helicon Tech's ISAPI Rewrite use the same syntax as Apache's mod_rewrite. For example (taken from Jeff's blog post):
[ISAPI_Rewrite]# fix missing slash on folders# note, this assumes we have no folders with periods!RewriteCond Host: (.*)RewriteRule ([^.?]+[^.?/]) http\://$1$2/ [RP]# remove index pages from URLsRewriteRule (.*)/default.htm$ $1/ [I,RP]RewriteRule (.*)/default.aspx$ $1/ [I,RP]RewriteRule (.*)/index.htm$ $1/ [I,RP]RewriteRule (.*)/index.html$ $1/ [I,RP]# force proper www. prefix on all requestsRewriteCond %HTTP_HOST ^test\.com [I]RewriteRule ^/(.*) http://www.test.com/$1 [RP]# only allow whitelisted referers to hotlink imagesRewriteCond Referer: (?!http://(?:www\.good\.comwww\.better\.com)).+RewriteRule .*\.(?:gifjpgjpegpng) /images/block.jpg [I,O]
Definitely check out Scott's post and Jeff's post to learn more about these ISAPI modules, and what you can do with them.
Note: One downside to using an ISAPI filter is that shared hosting environments typically won't allow you to install this component, and so you'll need either a virtual dedicated hosting server or a dedicated hosting server to use them. But, if you do have a hosting plan that allows you to install the ISAPI, it will provide maximum flexibility on IIS5/6 - and tide you over until IIS7 ships.
Handling ASP.NET PostBacks with URL Rewriting
One gotcha that people often run into when using ASP.NET and Url-Rewriting has to-do with handling postback scenarios. Specifically, when you place a
People often ask me for guidance on how they can dynamically "re-write" URLs and/or have the ability to publish cleaner URL end-points within their ASP.NET web applications. This blog post summarizes a few approaches you can take to cleanly map or rewrite URLs with ASP.NET, and have the option to structure the URLs of your application however you want.
Why does URL mapping and rewriting matter?
The most common scenarios where developers want greater flexibility with URLs are:
1) Handling cases where you want to restructure the pages within your web application, and you want to ensure that people who have bookmarked old URLs don't break when you move pages around. Url-rewriting enables you to transparently forward requests to the new page location without breaking browsers.
2) Improving the search relevancy of pages on your site with search engines like Google, Yahoo and Live. Specifically, URL Rewriting can often make it easier to embed common keywords into the URLs of the pages on your sites, which can often increase the chance of someone clicking your link. Moving from using querystring arguments to instead use fully qualified URL's can also in some cases increase your priority in search engine results. Using techniques that force referring links to use the same case and URL entrypoint (for example: weblogs.asp.net/scottgu instead of weblogs.asp.net/scottgu/default.aspx) can also avoid diluting your pagerank across multiple URLs, and increase your search results.
In a world where search engines increasingly drive traffic to sites, extracting any little improvement in your page ranking can yield very good ROI to your business. Increasingly this is driving developers to use URL-Rewriting and other SEO (search engine optimization) techniques to optimize sites (note that SEO is a fast moving space, and the recommendations for increasing your search relevancy evolve monthly). For a list of some good search engine optimization suggestions, I'd recommend reading the SSW Rules to Better Google Rankings, as well as MarketPosition's article on how URLs can affect top search engine ranking.
Sample URL Rewriting Scenario
For the purpose of this blog post, I'm going to assume we are building a set of e-commerce catalog pages within an application, and that the products are organized by categories (for example: books, videos, CDs, DVDs, etc).
Let's assume that we initially have a page called "Products.aspx" that takes a category name as a querystring argument, and filters the products accordingly. The corresponding URLs to this Products.aspx page look like this:
http://www.store.com/products.aspx?category=bookshttp://www.store.com/products.aspx?category=DVDshttp://www.store.com/products.aspx?category=CDs
Rather than use a querystring to expose each category, we want to modify the application so that each product category looks like a unique URL to a search engine, and has the category keyword embedded in the actual URL (and not as a querystring argument). We'll spend the rest of this blog post going over 4 different approaches that we could take to achieve this.
Approach 1: Use Request.PathInfo Parameters Instead of QueryStrings
The first approach I'm going to demonstrate doesn't use Url-Rewriting at all, and instead uses a little-known feature of ASP.NET - the Request.PathInfo property. To help explain the usefulness of this property, consider the below URL scenario for our e-commerce store:
http://www.store.com/products.aspx/Bookshttp://www.store.com/products.aspx/DVDshttp://www.store.com/products.aspx/CDs
One thing you'll notice with the above URLs is that they no longer have Querystring values - instead the category parameter value is appended on to the URL as a trailing /param value after the Products.aspx page handler name. An automated search engine crawler will then interpret these URLs as three different URLs, and not as one URL with three different input values (search engines ignore the filename extension and just treat it as another character within the URL).
You might wonder how you handle this appended parameter scenario within ASP.NET. The good news is that it is pretty simple. Simply use the Request.PathInfo property, which will return the content immediately following the products.aspx portion of the URL. So for the above URLs, Request.PathInfo would return "/Books", "/DVDs", and "/CDs" (in case you are wondering, the Request.Path property would return "/products.aspx").
You could then easily write a function to retrieve the category like so (the below function strips out the leading slash and returning just "Books", "DVDs" or "CDs"):
Function GetCategory() As String
If (Request.PathInfo.Length = 0) Then
Return ""
Else
Return Request.PathInfo.Substring(1)
End If
End Function
Sample Download: A sample application that I've built that shows using this technique can be downloaded here. What is nice about this sample and technique is that no server configuration changes are required in order to deploy an ASP.NET application using this approach. It will also work fine in a shared hosting environment.
Approach 2: Using an HttpModule to Perform URL Rewriting
An alternative approach to the above Request.PathInfo technique would be to take advantage of the HttpContext.RewritePath() method that ASP.NET provides. This method allows a developer to dynamically rewrite the processing path of an incoming URL, and for ASP.NET to then continue executing the request using the newly re-written path.
For example, we could choose to expose the following URLs to the public:
http://www.store.com/products/Books.aspxhttp://www.store.com/products/DVDs.aspxhttp://www.store.com/products/CDs.aspx
This looks to the outside world like there are three separate pages on the site (and will look great to a search crawler). By using the HttpContext.RewritePath() method we can dynamically re-write the incoming URLs when they first reach the server to instead call a single Products.aspx page that takes the category name as a Querystring or PathInfo parameter instead. For example, we could use an an Application_BeginRequest event in Global.asax like so to do this:
void Application_BeginRequest(object sender, EventArgs e) {
string fullOrigionalpath = Request.Url.ToString();
if (fullOrigionalpath.Contains("/Products/Books.aspx"))
{
Context.RewritePath("/Products.aspx?Category=Books");
}
else if (fullOrigionalpath.Contains("/Products/DVDs.aspx"))
{
Context.RewritePath("/Products.aspx?Category=DVDs");
}
}
The downside of manually writing code like above is that it can be tedious and error prone. Rather than do it yourself, I'd recommend using one of the already built HttpModules available on the web for free to perform this work for you. Here a few free ones that you can download and use today:
UrlRewriter.net
UrlRewriting.net
These modules allow you to declaratively express matching rules within your application's web.config file. For example, to use the UrlRewriter.Net module within your application's web.config file to map the above URLs to a single Products.aspx page, we could simply add this web.config file to our application (no code is required):
The HttpModule URL rewriters above also add support for regular expression and URL pattern matching (to avoid you having to hard-code every URL in your web.config file). So instead of hard-coding the category list, you could re-write the rules like below to dynamically pull the category from the URL for any "/products/[category].aspx" combination:
This makes your code much cleaner and super extensible.
Sample Download: A sample application that I've built that shows using this technique with the UrlRewriter.Net module can be downloaded here.
What is nice about this sample and technique is that no server configuration changes are required in order to deploy an ASP.NET application using this approach. It will also work fine in a medium trust shared hosting environment (just ftp/xcopy to the remote server and you are good to go - no installation required).
Approach 3: Using an HttpModule to Perform Extension-Less URL Rewriting with IIS7
The above HttpModule approach works great for scenarios where the URL you are re-writing has a .aspx extension, or another file extension that is configured to be processed by ASP.NET. When you do this no custom server configuration is required - you can just copy your web application up to a remote server and it will work fine.
There are times, though, when you want the URL to re-write to either have a non-ASP.NET file extension (for example: .jpg, .gif, or .htm) or no file-extension at all. For example, we might want to expose these URLs as our public catalog pages (note they have no .aspx extension):
http://www.store.com/products/Bookshttp://www.store.com/products/DVDshttp://www.store.com/products/CDs
With IIS5 and IIS6, processing the above URLs using ASP.NET is not super easy. IIS 5/6 makes it hard to perform URL rewriting on these types of URLs within ISAPI Extensions (which is how ASP.NET is implemented). Instead you need to perform the rewriting earlier in the IIS request pipeline using an ISAPI Filter. I'll show how to-do this on IIS5/6 in the Approach 4 section below.
The good news, though, is that IIS 7.0 makes handling these types of scenarios super easy. You can now have an HttpModule execute anywhere within the IIS request pipeline - which means you can use the URLRewriter module above to process and rewrite extension-less URLs (or even URLs with a .asp, .php, or .jsp extension). Below is how you would configure this with IIS7:
Note the "runAllManagedModulesForAllRequests" attribute that is set to true on the
1) It will work on any IIS 7.0 machine. You don't need an administrator to enable anything on the remote host. It will also work in medium trust shared hosting scenarios.
2) Because I've configured the UrlRewriter in both the
IIS 7.0 server will ship later this year as part of Windows Longhorn Server, and will support a go-live license with the Beta3 release in a few weeks. Because of all the new hosting features that have been added to IIS7, we expect hosters to start aggressively offering IIS7 accounts relatively quickly - which means you should be able to start to take advantage of the above extension-less rewriting support soon. We'll also be shipping a Microsoft supported URL-Rewriting module in the IIS7 RTM timeframe that will be available for free as well that you'll be able to use on IIS7, and which will provide nice support for advanced re-writing scenarios for all content on your web-server.
Sample Download: A sample application that I've built that shows using this extension-less URL technique with IIS7 and the UrlRewriter.Net module can be downloaded here.
Approach 4: ISAPIRewrite to enable Extension-less URL Rewriting for IIS5 and IIS6
If you don't want to wait for IIS 7.0 in order to take advantage of extension-less URL Rewriting, then your best best is to use an ISAPI Filter in order to re-write URLs. There are two ISAPI Filter solutions that I'm aware of that you might want to check-out:
Helicon Tech's ISAPI Rewrite: They provide an ISAPI Rewrite full product version for $99 (with 30 day free trial), as well as a ISAPI Rewrite lite edition that is free.
Ionic's ISAPI Rewrite: This is a free download (both source and binary available)
I actually don't have any first-hand experience using either of the above solutions - although I've heard good things about them. Scott Hanselman and Jeff Atwood recently both wrote up great blog posts about their experiences using them, and also provided some samples of how to configure the rules for them. The rules for Helicon Tech's ISAPI Rewrite use the same syntax as Apache's mod_rewrite. For example (taken from Jeff's blog post):
[ISAPI_Rewrite]# fix missing slash on folders# note, this assumes we have no folders with periods!RewriteCond Host: (.*)RewriteRule ([^.?]+[^.?/]) http\://$1$2/ [RP]# remove index pages from URLsRewriteRule (.*)/default.htm$ $1/ [I,RP]RewriteRule (.*)/default.aspx$ $1/ [I,RP]RewriteRule (.*)/index.htm$ $1/ [I,RP]RewriteRule (.*)/index.html$ $1/ [I,RP]# force proper www. prefix on all requestsRewriteCond %HTTP_HOST ^test\.com [I]RewriteRule ^/(.*) http://www.test.com/$1 [RP]# only allow whitelisted referers to hotlink imagesRewriteCond Referer: (?!http://(?:www\.good\.comwww\.better\.com)).+RewriteRule .*\.(?:gifjpgjpegpng) /images/block.jpg [I,O]
Definitely check out Scott's post and Jeff's post to learn more about these ISAPI modules, and what you can do with them.
Note: One downside to using an ISAPI filter is that shared hosting environments typically won't allow you to install this component, and so you'll need either a virtual dedicated hosting server or a dedicated hosting server to use them. But, if you do have a hosting plan that allows you to install the ISAPI, it will provide maximum flexibility on IIS5/6 - and tide you over until IIS7 ships.
Handling ASP.NET PostBacks with URL Rewriting
One gotcha that people often run into when using ASP.NET and Url-Rewriting has to-do with handling postback scenarios. Specifically, when you place a
Subscribe to:
Comments (Atom)