<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet type="text/xsl" href="http://blogs.msdn.com/utility/FeedStylesheets/atom.xsl" media="screen"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en-US"><title type="html">Thomas Marquardt&amp;#39;s Blog</title><subtitle type="html" /><id>http://blogs.msdn.com/b/tmarq/atom.aspx</id><link rel="alternate" type="text/html" href="http://blogs.msdn.com/b/tmarq/" /><link rel="self" type="application/atom+xml" href="http://blogs.msdn.com/b/tmarq/atom.aspx" /><generator uri="http://telligent.com" version="5.6.583.17018">Telligent Community 5.6.583.17018 (Build: 5.6.583.17018)</generator><updated>2007-06-09T01:25:00Z</updated><entry><title>ASP.NET v4.0 Security Update and ISAPI Filters on IIS 6.0</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/b/tmarq/archive/2010/10/08/asp-net-v4-0-security-update-and-isapi-filters-on-iis-6-0.aspx" /><id>http://blogs.msdn.com/b/tmarq/archive/2010/10/08/asp-net-v4-0-security-update-and-isapi-filters-on-iis-6-0.aspx</id><published>2010-10-08T17:54:00Z</published><updated>2010-10-08T17:54:00Z</updated><content type="html">&lt;p&gt;A security patch for ASP.NET v4.0 was released recently.&amp;nbsp; The details of the patch can be found at &lt;a href="http://www.microsoft.com/technet/security/bulletin/ms10-070.mspx"&gt;http://www.microsoft.com/technet/security/bulletin/ms10-070.mspx&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I really only have one thing to say in this blog post:&amp;nbsp; If you use an ISAPI Rewrite Filter on IIS 6.0 and you're application pool is using ASP.NET v4.0, then you should&amp;nbsp;&lt;a href="http://blogs.msdn.com/b/tmarq/archive/2010/06/18/how-to-disable-the-asp-net-v4-0-extensionless-url-feature-on-iis-6-0.aspx"&gt;disable the ASP.NET extensionless URL feature&lt;/a&gt;.&amp;nbsp; The security patch unfortunately deletes that registry setting, so you will have to reset it after you install the patch.&amp;nbsp; The&amp;nbsp;security patch deletes all registry values beneath HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\ASP.NET\4.0.30319.0, and restores them to their default values.&amp;nbsp;&amp;nbsp;The rest of this blog post provides more detail.&amp;nbsp; &lt;/p&gt;
&lt;p&gt;The security patch also has an unfortunate side-effect on IIS 6.0 of moving the ASP.NET v4.0&amp;nbsp;ISAPI Filter&amp;nbsp;(aspnet_filter.dll) to the&amp;nbsp;bottom of the global&amp;nbsp;(effects all web sites)&amp;nbsp;ISAPI Filter list in IIS configuration.&amp;nbsp; This could break applications if they have an ISAPI Filter that needs to come after aspnet_filter.dll.&amp;nbsp; If&amp;nbsp;you installed the patch and&amp;nbsp;suddenly started seeing failures with "/eurl.axd/{GUID}" (where {GUID} is a hexadecimal number)&amp;nbsp;in the URL, then this is the most likely cause.&amp;nbsp; You&amp;nbsp;can try re-ordering&amp;nbsp;your ISAPI Filters list, but if you're not using the ASP.NET extensionless URL&amp;nbsp;feature on IIS 6.0&amp;nbsp;then you should disable it as described at &lt;a href="http://blogs.msdn.com/b/tmarq/archive/2010/06/18/how-to-disable-the-asp-net-v4-0-extensionless-url-feature-on-iis-6-0.aspx"&gt;http://blogs.msdn.com/b/tmarq/archive/2010/06/18/how-to-disable-the-asp-net-v4-0-extensionless-url-feature-on-iis-6-0.aspx&lt;/a&gt;.&amp;nbsp; That blog post also explains how the ASP.NET v4.0&amp;nbsp;Extensionless URL feature works on IIS 6.0.&lt;/p&gt;
&lt;p&gt;To change the order of the ISAPI Filters on IIS 6.0, open IIS Manager, find the "Web Sites" folder in the navigation tree, right click that folder and select "Properties", and&amp;nbsp;finally select the "ISAPI Filters" tab.&amp;nbsp; From here, you can select an ISAPI&amp;nbsp;Filter in the list&amp;nbsp;and click the "Move up" button to change the order.&amp;nbsp; The ASP.NET v4.0 filter is the one that appends "/eurl.axd/{GUID}" to URLs, and in the IIS Manager user interface it appears under the name "ASP.NET_4.0.30319.0".&amp;nbsp; For more details on ISAPI Filters, see &lt;a href="http://msdn.microsoft.com/en-us/library/ms524610(VS.90).aspx"&gt;http://msdn.microsoft.com/en-us/library/ms524610(VS.90).aspx&lt;/a&gt;.&amp;nbsp; Note that ISAPI Filters can also be installed at the web site level, so you might need to move your custom ISAPI Filter from the web site level (local) to the web sites level (global) in order to re-order it relative to the ASP.NET v4.0 Filter.&lt;/p&gt;
&lt;p&gt;The ASP.NET ISAPI Filter (aspnet_filter.dll) appends "/eurl.axd/{GUID}" to extensionless URLs in order to direct URLs to the ASP.NET ISAPI Extension (aspnet_isapi.dll).&amp;nbsp;&amp;nbsp;The ASP.NET ISAPI Extension then removes "/eurl.axd/{GUID}" from the URL.&amp;nbsp; The problems with "/eurl.axd/{GUID}" appearing in URLs occur when an ISAPI Rewrite Filter gets between the ASP.NET ISAPI Filter&amp;nbsp;and the ASP.NET ISAPI Extension, and does something (modifies the URL) that makes it impossible for ASP.NET ISAPI Extension to restore the URL,&amp;nbsp;and so "/eurl.axd/{GUID}"&amp;nbsp;remains in the URL.&amp;nbsp; &lt;/p&gt;
&lt;p&gt;The ASP.NET v4.0 Extensionless URL (EURL) feature does its work&amp;nbsp;during the&amp;nbsp;&lt;a target="_blank" href="http://msdn.microsoft.com/en-us/library/ms525878(VS.90).aspx"&gt;SF_NOTIFY_PREPROC_HEADERS&lt;/a&gt; notification on IIS 6.0.&amp;nbsp;&amp;nbsp;For those of you having problems with the &lt;a target="_blank" href="http://iirf.codeplex.com/"&gt;Ionic&amp;rsquo;s ISAPI Rewrite Filter&lt;/a&gt;, I looked at the Ionic's source code and understand why ASP.NET's EURL feature breaks.&amp;nbsp;&amp;nbsp;Ionic's ISAPI Rewriter Filter used to do rewrite and redirect during SF_NOTIFY_PREPROC_HEADERS, but the latest sources do it during SF_NOTIFY_AUTH_COMPLETE, which happens later.&amp;nbsp; So an older version of the Ionic&amp;rsquo;s ISAPI Rewrite Filter would be compatible with the ASP.NET EURL feature as long as it's ISAPI Filter&amp;nbsp;comes first in the ISAPI Filter list (so it can't get between the ASP.NET ISAPI Filter and the ASP.NET ISAPI Extension), and the new version of Ionic&amp;rsquo;s ISAPI Rewrite Filter is not compatible at all with the ASP.NET EURL feature, no matter how the ISAPI Filters are ordered, because Ionic's does work in SF_NOTIFY_AUTH_COMPLETE&lt;strong&gt; &lt;/strong&gt;which always comes in between the ASP.NET ISAPI Filter and the ASP.NET ISAPI Extension.&amp;nbsp; Note that the ASP.NET ISAPI Filter (aspnet_filter.dll) does more than implement ASP.NET EURL, and by no means should you uninstall the ASP.NET ISAPI Filter.&amp;nbsp; All you should do is &lt;a href="http://blogs.msdn.com/b/tmarq/archive/2010/06/18/how-to-disable-the-asp-net-v4-0-extensionless-url-feature-on-iis-6-0.aspx"&gt;disable the ASP.NET extensionless URL feature&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=10073402" width="1" height="1"&gt;</content><author><name>tmarq</name><uri>http://blogs.msdn.com/tmarq/ProfileUrlRedirect.ashx</uri></author></entry><entry><title>How to Disable the ASP.NET v4.0 Extensionless URL feature on IIS 6.0</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/b/tmarq/archive/2010/06/18/how-to-disable-the-asp-net-v4-0-extensionless-url-feature-on-iis-6-0.aspx" /><id>http://blogs.msdn.com/b/tmarq/archive/2010/06/18/how-to-disable-the-asp-net-v4-0-extensionless-url-feature-on-iis-6-0.aspx</id><published>2010-06-18T17:44:00Z</published><updated>2010-06-18T17:44:00Z</updated><content type="html">&lt;p&gt;You can disable the v4.0 ASP.NET extensionless URL feature on IIS6 by setting a DWORD at HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\ASP.NET\4.0.30319.0\EnableExtensionlessUrls = 0.&amp;nbsp; After changing the value, you will need to restart IIS in order for us to pick up the change, because it is only read once when IIS starts.&amp;nbsp; Note that&amp;nbsp;for Wow64 (i.e.,&amp;nbsp;32-bit worker process running on&amp;nbsp;64-bit OS), this registry key must be set at HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\ASP.NET\4.0.30319.0\EnableExtensionlessUrls.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s how the v4.0 ASP.NET extensionless URL features works on IIS 6.&amp;nbsp; We have an ISAPI Filter named aspnet_filter.dll that appends &amp;ldquo;/eurl.axd/GUID&amp;rdquo; to extensionless URLs.&amp;nbsp; This happens early on in the request processing.&amp;nbsp; We also have a script mapping so that &amp;ldquo;*.axd&amp;rdquo; requests are handled by our ISAPI, aspnet_isapi.dll.&amp;nbsp; When we append &amp;ldquo;/eurl.axd/GUID&amp;rdquo; to extensionless URLs, it causes them to be mapped to our aspnet_isapi.dll, as long as the script map exists as expected.&amp;nbsp; These requests then enter ASP.NET where we remove &amp;ldquo;/eurl.axd/GUID&amp;rdquo; from the URL, that is, we restore the original URL.&amp;nbsp; The restoration of the original URL happens very early.&amp;nbsp; Now the URL is extensionless again, and if no further changes are made and you&amp;rsquo;re using the default &amp;lt;httpHandlers&amp;gt; section in web.config, this will be assigned to the DefaultHttpHandler, since it has path=&amp;rdquo;*&amp;rdquo; and is the first handler to match the extensionless URL.&amp;nbsp; The DefaultHttpHandler will then redirect this request back to IIS, but this time our filter will not append &amp;ldquo;/eurl.axd/GUID&amp;rdquo; and the request will be handled as it would be normally.&amp;nbsp; To do anything interesting with the v4.0 ASP.NET extensionless URL feature, you must change the handler from DefaultHttpHandler to something else, before the MapRequestHandler event fires.&lt;/p&gt;
&lt;p&gt;Note that v4.0 aspnet_filter.dll will only enable the extensionless URL feature (by appending eurl.axd to the URL) if the following are true:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;EnableExtensionlessUrls is not defined or has a value of 1.&lt;/li&gt;
&lt;li&gt;v4.0 aspnet_filter.dll is registered as an ISAPI Filter.&lt;/li&gt;
&lt;li&gt;v4.0 aspnet_isapi.dll is script mapped to ".axd" at the web site level (e.g. "/LM/W3SVC/N/ROOT", where N is the siteID).&lt;/li&gt;
&lt;li&gt;v4.0 aspnet_isapi.dll is marked as "Allowed" in the ISAPI Restriction list.&lt;/li&gt;
&lt;li&gt;The web site has read and script permission enabled.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I've heard that it is not compatible with &lt;strong&gt;Ionics ISAPI Rewrite Filter&lt;/strong&gt;, and it may not be compatible with other URL rewriting components.&amp;nbsp; Fortunately&amp;nbsp;the ASP.NET feature&amp;nbsp;is easy to disable.&amp;nbsp; By the way, the most common&amp;nbsp;way for the ASP.NET extensionless URL feature to fail is for it to leave "/eurl.axd/GUID" on the URL.&amp;nbsp; This happens when our aspnet_filter appends &amp;ldquo;/eurl.axd/GUID&amp;rdquo;, but we fail to find the &amp;ldquo;*.axd&amp;rdquo; script map, and so the request is not mapped to aspnet_isapi.dll, and therefore never enters ASP.NET and we never restore the original URL.&amp;nbsp; As a result, you will receive a 404 response and the URL will have "/eurl.axd/GUID" appended.&lt;/p&gt;
&lt;p&gt;Thanks,&lt;br /&gt;Thomas&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=10027208" width="1" height="1"&gt;</content><author><name>tmarq</name><uri>http://blogs.msdn.com/tmarq/ProfileUrlRedirect.ashx</uri></author><category term="Extensionless URLs" scheme="http://blogs.msdn.com/b/tmarq/archive/tags/Extensionless+URLs/" /></entry><entry><title>How Extensionless URLs Are Handled By ASP.NET v4</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/b/tmarq/archive/2010/05/26/how-extensionless-urls-are-handled-by-asp-net-v4.aspx" /><id>http://blogs.msdn.com/b/tmarq/archive/2010/05/26/how-extensionless-urls-are-handled-by-asp-net-v4.aspx</id><published>2010-05-26T18:48:00Z</published><updated>2010-05-26T18:48:00Z</updated><content type="html">&lt;p&gt;ASP.NET v4.0 has a new feature, when hosted on IIS 7, that enables the execution of extensionless URLs.&amp;nbsp; This feature has a dependency on a QFE from IIS that enables extensionless handler mappings.&amp;nbsp; See &lt;a target="_blank" href="http://support.microsoft.com/kb/980368" title="KB 980368"&gt;KB 980368&lt;/a&gt; for&amp;nbsp;more information and a link to download the fix.&lt;/p&gt;
&lt;p&gt;ASP.NET v4.0 adds extensionless handler mappings to&amp;nbsp;IIS's configuration file&amp;nbsp;(applicationHost.config).&amp;nbsp; There are handler mappings for classic mode and integrated mode.&amp;nbsp; The classic mode handler mappings map to aspnet_isapi.dll.&amp;nbsp; The integrated mode handler mappings map to a new type named System.Web.Handlers.TransferRequestHandler.&amp;nbsp; An example of the handler mappings is below:&lt;/p&gt;
&lt;div class="code_sample"&gt;
&lt;pre&gt;&amp;lt;add&lt;br /&gt;&amp;nbsp; name="ExtensionlessUrl-ISAPI-4.0_32bit"&lt;br /&gt;&amp;nbsp; path="*."&lt;br /&gt;&amp;nbsp; verb="GET,HEAD,POST,DEBUG"&lt;br /&gt;&amp;nbsp; modules="IsapiModule"&lt;br /&gt;&amp;nbsp; scriptProcessor="%WINDIR%\Microsoft.NET\Framework\v4.0.30319\aspnet_isapi.dll"&lt;br /&gt;&amp;nbsp; preCondition="classicMode,runtimeVersionv4.0,bitness32" &lt;br /&gt;&amp;nbsp; responseBufferLimit="0" /&amp;gt;&lt;/pre&gt;
&lt;pre&gt;&amp;lt;add&lt;br /&gt;&amp;nbsp; name="ExtensionlessUrl-ISAPI-4.0_64bit"&lt;br /&gt;&amp;nbsp; path="*." &lt;br /&gt;&amp;nbsp; verb="GET,HEAD,POST,DEBUG"&lt;br /&gt;&amp;nbsp; modules="IsapiModule"&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp; scriptProcessor="C:\Windows\Microsoft.NET\Framework64\v4.0.30319\aspnet_isapi.dll"&lt;br /&gt;&amp;nbsp; preCondition="classicMode,runtimeVersionv4.0,bitness64"&lt;br /&gt;&amp;nbsp; responseBufferLimit="0" /&amp;gt;&lt;/pre&gt;
&lt;pre&gt;&amp;lt;add &lt;br /&gt;&amp;nbsp; name="ExtensionlessUrl-Integrated-4.0" &lt;br /&gt;&amp;nbsp; path="*." verb="GET,HEAD,POST,DEBUG" &lt;br /&gt;&amp;nbsp; type="System.Web.Handlers.TransferRequestHandler" &lt;br /&gt;&amp;nbsp; preCondition="integratedMode,runtimeVersionv4.0" /&amp;gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;These handler mappings are installed when v4.0 is installed, but without the IIS 7 QFE, they are ignored for extensionless URLs.&amp;nbsp; As soon as you install the QFE, extensionless URLs will enter ASP.NET.&amp;nbsp; The QFE will eventually be included in a service pack.&lt;/p&gt;
&lt;p&gt;In classic mode,&amp;nbsp;extensionless URLs are first mapped to&amp;nbsp;aspnet_isapi.dll by IIS, and then using the &amp;lt;httpHandlers&amp;gt; configuration section, ASP.NET maps them to the System.Web.DefaultHttpHandler.&amp;nbsp; By default, the&amp;nbsp;root web.config file contains a path="*" mapping to DefaultHttpHandler.&amp;nbsp; In integrated mode they are mapped to TransferRequestHandler.&amp;nbsp; DefaultHttpHandler and TransferRequestHandler are similar in what they do.&amp;nbsp; If you do not remap the handler, they will both issue a "child request" to the original URL, but ask IIS to ignore their handler mappings for this second request.&amp;nbsp; By default, the IIS StaticFile handler is what will be mapped to these URLs during the second request, and that will typically result in a 404 or 403, assuming that no such file or directory exists.&lt;/p&gt;
&lt;p&gt;In order to do something interesting with extensionless URLs, you must remap the handler before the &lt;a target="_blank" href="http://msdn.microsoft.com/en-us/library/system.web.httpapplication.maprequesthandler.aspx" title="MapRequestHandler"&gt;MapRequestHandler&lt;/a&gt; event.&amp;nbsp; To do this, you can call &lt;a target="_blank" href="http://msdn.microsoft.com/en-us/library/system.web.httpcontext.remaphandler.aspx" title="HttpContext.RemapHandler"&gt;HttpContext.RemapHandler&lt;/a&gt; and pass in an instance of IHttpHandler.&amp;nbsp; At the lowest level, this is how you programmatically remap the handler.&amp;nbsp; Alternatively use can a feature like &lt;a target="_blank" href="http://msdn.microsoft.com/en-us/library/cc668201.aspx" title="URL Routing"&gt;URL Routing&lt;/a&gt;&amp;nbsp;to remap the handler.&amp;nbsp;&amp;nbsp;URL Routing&amp;nbsp;allows you to define routes that send&amp;nbsp;a URL to a specific handler.&amp;nbsp; It simply calls RemapHandler for you.&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=10015822" width="1" height="1"&gt;</content><author><name>tmarq</name><uri>http://blogs.msdn.com/tmarq/ProfileUrlRedirect.ashx</uri></author><category term="Extensionless URLs" scheme="http://blogs.msdn.com/b/tmarq/archive/tags/Extensionless+URLs/" /></entry><entry><title>Performing Asynchronous Work, or Tasks, in ASP.NET Applications</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/b/tmarq/archive/2010/04/14/performing-asynchronous-work-or-tasks-in-asp-net-applications.aspx" /><id>http://blogs.msdn.com/b/tmarq/archive/2010/04/14/performing-asynchronous-work-or-tasks-in-asp-net-applications.aspx</id><published>2010-04-14T14:27:00Z</published><updated>2010-04-14T14:27:00Z</updated><content type="html">&lt;p class="MsoNormal"&gt;&lt;span style="font-family: Calibri; font-size: small;"&gt;In this post I hope to clear up some misconceptions about the use of threads in ASP.NET applications so that you know the best way to perform asynchronous work in your ASP.NET applications.&lt;span style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/span&gt;If you&amp;rsquo;re in a hurry and don&amp;rsquo;t want to read the rest of this, then I suggest that you use &lt;/span&gt;&lt;a href="http://msdn.microsoft.com/en-us/library/system.web.ui.pageasynctask.aspx"&gt;&lt;span style="font-family: Calibri; font-size: small;"&gt;PageAsyncTask&lt;/span&gt;&lt;/a&gt;&lt;span style="font-family: Calibri; font-size: small;"&gt; to execute work asynchronously, and enjoy the rest of your day.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class="MsoNormal"&gt;&lt;span style="font-family: Calibri; font-size: small;"&gt;I explained how ASP.NET and IIS use threads in an earlier post titled &lt;/span&gt;&lt;a href="http://blogs.msdn.com/tmarq/archive/2007/07/21/asp-net-thread-usage-on-iis-7-0-and-6-0.aspx"&gt;&lt;span style="font-family: Calibri; font-size: small;"&gt;&amp;ldquo;ASP.NET Thread Usage on IIS 7.0 and 6.0&amp;rdquo;&lt;/span&gt;&lt;/a&gt;&lt;span style="font-family: Calibri; font-size: small;"&gt;.&lt;span style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/span&gt;If you haven't read that, please do, but just so you're not left wondering,&amp;nbsp;I will summarize the "flow" of an ASP.NET&amp;nbsp;request again here:&amp;nbsp; New requests are received by HTTP.sys, a kernel driver.&lt;span style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/span&gt;HTTP.sys posts the request to an I/O completion port on which IIS listens.&lt;span style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/span&gt;IIS picks up the request on one of its thread pool threads and calls into ASP.NET where ASP.NET immediately&amp;nbsp;posts the request to the CLR ThreadPool and returns a pending status to IIS.&amp;nbsp; Next the request is typically executed, although it can be placed in a queue until there are enough resources to execute it.&amp;nbsp;&amp;nbsp;To execute it, we raise all of the pipeline events and the modules and handlers in the pipeline work on the request,&amp;nbsp;typically&amp;nbsp;while remaining on the same thread, but they can alternatively handle these&amp;nbsp;events asynchronously.&amp;nbsp; When the last pipeline event completes, the response bytes are sent to the client asynchronously during our&amp;nbsp;final send, and then the request is done and all the context for that request is released.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class="MsoNormal"&gt;&lt;span style="font-family: Calibri; font-size: small;"&gt;In case you're curious, the IIS thread pool has a maximum thread count of 256.&lt;span style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/span&gt;This thread pool is designed in such a way that it does not handle long running tasks well.&lt;span style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/span&gt;The recommendation from the IIS team is to switch to another thread if you&amp;rsquo;re going to do substantial work, such as done by the ASP.NET ISAPI and/or ASP.NET when running in integrated mode on IIS 7.&lt;span style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/span&gt;Otherwise you will tie up IIS threads and prevent IIS from picking up completions from HTTP.sys.&lt;span style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/span&gt;&lt;span style="mso-spacerun: yes"&gt;&amp;nbsp;&lt;/span&gt;So for this reason, ASP.NET always returns a pending status to IIS, and calls &lt;/span&gt;&lt;a href="http://msdn.microsoft.com/en-us/library/kbf0f1ct.aspx"&gt;&lt;span style="font-family: Calibri; font-size: small;"&gt;QueueUserWorkItem&lt;/span&gt;&lt;/a&gt;&lt;span style="font-family: Calibri; font-size: small;"&gt; to post the request to the CLR ThreadPool.&lt;span style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/span&gt;&lt;span style="mso-spacerun: yes"&gt;&amp;nbsp;&lt;/span&gt;In v2.0, 3.5, and 4.0, ASP.NET initializes the CLR ThreadPool with 100 threads per processor (that&amp;rsquo;s the default, this is configurable).&lt;span style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/span&gt;So on a dual-core server, there will be a maximum of 200 threads in the pool.&lt;span style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;When a CLR ThreadPool thread becomes available (typically happens immediately), the request is picked up by ASP.NET.&lt;span style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/span&gt;Normally the request is executed at this point, but it is possible for it to be inserted into one of the ASP.NET queues (described in earlier post).&lt;span style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/span&gt;If all the modules and handlers in the pipeline are synchronous, the request will execute on a single CLR ThreadPool thread.&lt;span style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/span&gt;If one or more modules or the handler are asynchronous, the request will not execute on a single thread.&lt;span style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/span&gt;This is where your code comes into play, but first I have a little more information for you.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class="MsoNormal"&gt;&lt;span style="font-family: Calibri; font-size: small;"&gt;If you read the paragraphs above, you&amp;nbsp;probably noticed that a lot of thread switches take place in order to execute an ASP.NET request.&lt;span style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/span&gt;There are always at least&amp;nbsp;3 thread switches.&lt;span style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/span&gt;The first is primarily a transition from kernel mode (HTTP.sys) to user mode (IIS).&lt;span style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/span&gt;It also frees up HTTP.sys to pick up more incoming requests and hand them off to their respective listeners&amp;mdash;not always IIS.&lt;span style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/span&gt;If we tried to execute the entire request on that thread, HTTP.sys wouldn&amp;rsquo;t be resilient to poorly performing listeners&amp;mdash;in fact, a single process could shutdown HTTP on the entire server if&amp;nbsp;that process&amp;nbsp;were to deadlock and hold the existing request threads as well as any new incoming request threads.&lt;span style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/span&gt;At the same time, there is a penalty paid for any thread switch.&lt;span style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/span&gt;It&amp;rsquo;s called a context switch, and they&amp;rsquo;re expensive.&lt;span style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/span&gt;However, in this case, the benefit from performing the thread switch (reliability of a kernel mode driver) out weights the cost of the context switch.&lt;span style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/span&gt;The second thread switch is the one from IIS to ASP.NET, where ASP.NET calls &lt;/span&gt;&lt;a href="http://msdn.microsoft.com/en-us/library/kbf0f1ct.aspx"&gt;&lt;span style="font-family: Calibri; font-size: small;"&gt;QueueUserWorkItem&lt;/span&gt;&lt;/a&gt;&lt;span style="font-family: Calibri; font-size: small;"&gt; for the CLR ThreadPool.&lt;span style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/span&gt;This one is not as critical.&lt;span style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/span&gt;As mentioned in an earlier blog post, this is done primarily as a performance improvement for large corporate workloads that have a mixture of static and dynamic requests.&lt;span style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&lt;/span&gt;The thread switch&amp;nbsp;for dynamic requests helps improve CPU cache locality for static requests (served by the IIS&amp;nbsp;static file handler)&amp;nbsp;which don't peform a thread switch and instead just execute until completion on the IIS thread.&lt;span style="mso-spacerun: yes"&gt;&amp;nbsp; Your specific workload may be quite different&amp;mdash;the &lt;/span&gt;context switch&amp;nbsp;may be more expensive than any performance benefit from performing the thread switch.&lt;span style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/span&gt;I think the performance benefit is very subtle regardless, and a more important reason to perform the thread switch is to free IIS threads so IIS can pick up more completions and hand requests off to other applications&amp;mdash;not always ASP.NET.&lt;span style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/span&gt;This is similar to what we described in the Http.sys case.&lt;span style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/span&gt;We decided it was better to perform the thread switch than not, and so that's what we do.&lt;span style="mso-spacerun: yes"&gt;&amp;nbsp; The third thread switch is the one that occurs when we perform the final send, when we switch from a CLR ThreadPool thread to the thread that send the bytes to the client.&amp;nbsp; This switch is definitely worth the cost of the context switch, because clients with low bandwidth are slow and we don't want our CLR ThreadPool thread to be blocked while the bytes are sent, we want it to return to the pool and execute incoming work items.&amp;nbsp; &lt;/span&gt;Ok, back to your code..., lets discuss your code now.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class="MsoNormal"&gt;&lt;span style="font-family: Calibri; font-size: small;"&gt;So, in your ASP.NET application, when should you perform work asynchronously instead of synchronously?&lt;span style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/span&gt;Well, only 1 thread per CPU can execute at a time.&amp;nbsp;&amp;nbsp;Did&amp;nbsp;you catch that?&amp;nbsp;&amp;nbsp;A lot of people seem to miss this point...only one thread executes at a time&amp;nbsp;on a CPU.&lt;span style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/span&gt;When you have more than this, you pay an expensive penalty--a context switch. &lt;span style="mso-spacerun: yes"&gt;&amp;nbsp;&lt;/span&gt;However, if a thread is blocked waiting on work...then it makes sense to switch to another thread, one that can execute now.&amp;nbsp; It also makes sense to&amp;nbsp;switch threads if you want work to be done in parallel as opposed to in series, but up until a certain point it actually makes much more sense to execute work in series, again, because of the expensive context switch.&lt;span style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/span&gt;Pop quiz:&lt;span style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/span&gt;If you have a thread that is doing a lot of computational work and using the CPU heavily, and this takes a while, should you switch to another thread? &lt;span style="mso-spacerun: yes"&gt;&amp;nbsp;&lt;/span&gt;No! The current thread is efficiently using the CPU, so switching will only incur the cost of a context switch.&lt;span style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/span&gt;Ok, well, what if you have a thread that makes an HTTP or SOAP request to another server and takes a long time, should you switch threads? Yes! You can perform the HTTP or SOAP request asynchronously, so that once the "send" has occurred, you can unwind the current thread and not use any threads until there is an I/O completion for the "receive". Between the "send" and the "receive", the remote server is busy, so locally you don't need to be blocking on a thread, but instead make use of the asynchronous APIs provided in .NET Framework so that you can unwind and be notified upon completion.&lt;span style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/span&gt;Again, it only makes sense to switch threads if the benefit from doing so out weights the cost of the switch.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class="MsoNormal"&gt;&lt;span style="font-family: Calibri; font-size: small;"&gt;Ok, so you have a good reason to perform some work asynchronously, how should you do it?&lt;span style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/span&gt;First of all, all of the code that you are able to run &lt;b style="mso-bidi-font-weight: normal"&gt;during the execution of a request&lt;/b&gt; must run within a module or handler.&lt;span style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/span&gt;There is no other option.&lt;span style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp; &lt;/span&gt;If you want work to be performed asynchronously&amp;mdash;truly asynchronously, as in the current thread unwinds and execution of the request resumes only if and when your work completes&amp;mdash;then you must run inside a module or handler that is asynchronous. &lt;span style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&lt;/span&gt;If you don&amp;rsquo;t want to implement your own asynchronous module or handler, you&amp;rsquo;re in luck, because ASP.NET 2.0 introduced &lt;/span&gt;&lt;a href="http://msdn.microsoft.com/en-us/magazine/cc163725.aspx"&gt;&lt;span style="font-family: Calibri; font-size: small;"&gt;async pages&lt;/span&gt;&lt;/a&gt;&lt;span style="font-family: Calibri; font-size: small;"&gt; , a feature which builds upon &lt;/span&gt;&lt;a href="http://msdn.microsoft.com/en-us/library/system.web.ihttpasynchandler.aspx"&gt;&lt;span style="font-family: Calibri; font-size: small;"&gt;IHttpAsyncHandler&lt;/span&gt;&lt;/a&gt;&lt;span style="font-family: Calibri; font-size: small;"&gt; and makes it easy to run asynchronous tasks known as &lt;/span&gt;&lt;a href="http://msdn.microsoft.com/en-us/library/system.web.ui.pageasynctask.aspx"&gt;&lt;span style="font-family: Calibri; font-size: small;"&gt;PageAsyncTasks&lt;/span&gt;&lt;/a&gt;&lt;span style="font-family: Calibri; font-size: small;"&gt;.&lt;span style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/span&gt;There is a nice introduction to asynchronous pages in &lt;/span&gt;&lt;a href="http://blogs.msdn.com/dmitryr/archive/2005/07/05/435647.aspx"&gt;&lt;span style="font-family: Calibri; font-size: small;"&gt;Dmitry&amp;rsquo;s TechEd presentation&lt;/span&gt;&lt;/a&gt;&lt;span style="font-family: Calibri; font-size: small;"&gt;.&lt;span style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/span&gt;I would think most ASP.NET developers would prefer to use async pages with one or more PageAsyncTasks in order to perform work asynchronously.&lt;span style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/span&gt;So I encourage you to read Dmitry&amp;rsquo;s TechEd presentation, and focus on &lt;/span&gt;&lt;a href="http://blogs.msdn.com/dmitryr/attachment/435647.ashx"&gt;&lt;span style="font-family: Calibri; color: #0000ff; font-size: small;"&gt;slides 31 and 32&lt;/span&gt;&lt;/a&gt;&lt;span style="font-family: Calibri; font-size: small;"&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class="MsoNormal"&gt;&lt;span style="font-family: Calibri; font-size: small;"&gt;If instead you would prefer to write your own asynchronous module or handler, I&amp;rsquo;ve included sample code for an asynchronous IHttpModule below along with the same code for a synchronous version of that IHttpModule.&lt;span style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/span&gt;Implementing an IHttpAsyncHandler is very similar, so I didn't bother to provide an example.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;"AsyncModule.cs":&lt;/p&gt;
&lt;div class="code_sample"&gt;
&lt;pre&gt;using System;
using System.Web;
using System.Threading;

public class AsyncModule: IHttpModule {

    // IHttpModule implementation
    void IHttpModule.Dispose() {}

    void IHttpModule.Init( HttpApplication app ) {
        app.AddOnPreRequestHandlerExecuteAsync(new BeginEventHandler(OnBegin),
                                               new EndEventHandler(OnEnd)); 
    }

    // Post a work item to the CLR ThreadPool and return an async result 
    // with IAsyncResult.CompletedSynchronously set to false so that the 
    // calling thread thread can unwind.
    private IAsyncResult OnBegin(Object sender, 
                                 EventArgs e, 
                                 AsyncCallback cb,
                                 Object state) {
        IAsyncResult ar = new MyAsyncResult(cb, 
                                            ((HttpApplication)sender).Context);
        ThreadPool.QueueUserWorkItem(DoAsyncWork, ar);
        return ar;
    }

    // Invoked after completion of the event.  This is frequently a no-op, but
    // if you need to perform any cleanup you could do it here.
    private void OnEnd(IAsyncResult asyncResult) {
    }

    // Called by CLR ThreadPool when a thread becomes available.  We must call
    // Complete on the async result when we are done.  When this happens, the&lt;br /&gt;    //  callback passed to OnEnter is invoked to notify ASP.NET that we are done 
    // handling the event. ASP.NET will then resume execution of the request.
    private void DoAsyncWork(Object state) {
        MyAsyncResult ar = state as MyAsyncResult;
        try {
            throw new NotImplementedException(
                "REMOVE THIS AND ADD YOUR CODE HERE");
        }
        catch(Exception e) {
            ar.Context.AddError(e);
        }
        finally {
            // we must notify ASP.NET upon completion
            ar.Complete(false);
        }
    }

    public class MyAsyncResult: IAsyncResult {
        private AsyncCallback  _callback;
        private HttpContext    _context;
        private bool           _completed;
        private bool           _completedSynchronously;

        // IAsyncResult implementation
        bool IAsyncResult.IsCompleted { get { return _completed;} }
        bool IAsyncResult.CompletedSynchronously { 
            get { return _completedSynchronously; } 
        }
        Object IAsyncResult.AsyncState { get { return null;} }
        //wait not supported
        WaitHandle IAsyncResult.AsyncWaitHandle { get { return null;} }

        // store HttpContext so we can access ASP.NET intrinsics 
        // from our DoAsyncWork thread
        public HttpContext  Context { 
            get { 
                if (_completed || _context == null) {
                    // HttpContext must not be accessed after we 
                    // invoke the completion callback
                    throw new InvalidOperationException();
                }
                return _context; 
            }
        }

        public MyAsyncResult(AsyncCallback cb, HttpContext context) {
            _callback = cb;
            _context = context;
        }

        public void Complete(bool synchronous) {
            _completed = true;
            _completedSynchronously = synchronous;
            // HttpContext must not be accessed after we invoke the 
            // completion callback
            _context = null;
            // let ASP.NET know we've completed the event
            if (_callback != null) {
                _callback(this);
            }
        }
    }
}&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;"SyncModule.cs":&lt;/p&gt;
&lt;div class="code_sample"&gt;
&lt;pre&gt;using System;
using System.Web;

public class SyncModule: IHttpModule {

    // IHttpModule implementation
    void IHttpModule.Dispose() {}

    void IHttpModule.Init( HttpApplication app ) {
        // There are many events, but here we're using 
        // PreRequestHandlerExecute
        app.PreRequestHandlerExecute += new EventHandler( 
                                            OnPreRequestHandlerExecute );
    }

    private void OnPreRequestHandlerExecute(object sender, EventArgs e) {
        HttpApplication app = sender as HttpApplication;
        throw new NotImplementedException(
                     "REMOVE THIS AND ADD YOUR CODE HERE");
    }
}&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class="MsoNormal"&gt;&lt;b style="mso-bidi-font-weight: normal"&gt;&lt;span style="font-size: small;"&gt;&lt;span style="font-family: Calibri;"&gt;FAQ&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class="MsoNormal"&gt;&lt;span style="font-size: small;"&gt;&lt;span style="font-family: Calibri;"&gt;&lt;b style="mso-bidi-font-weight: normal"&gt;Q1) &lt;span style="mso-spacerun: yes"&gt;&amp;nbsp;&lt;/span&gt;&lt;/b&gt;How many thread pools are there?&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class="MsoNormal"&gt;&lt;span style="font-family: Calibri; font-size: small;"&gt;A1)&lt;span style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/span&gt;There is only one managed thread pool:&lt;span style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/span&gt;the CLR &lt;/span&gt;&lt;a href="http://msdn.microsoft.com/en-us/library/system.threading.threadpool.aspx"&gt;&lt;span style="font-family: Calibri; font-size: small;"&gt;ThreadPool&lt;/span&gt;&lt;/a&gt;&lt;span style="font-family: Calibri; font-size: small;"&gt;.&lt;span style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/span&gt;This is the thread pool that is used by the .NET Framework.&lt;span style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/span&gt;However, using Win32 APIs you are free to implement as many thread pools as you like.&lt;span style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/span&gt;Almost nobody does this because it is very difficult to get it right.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class="MsoNormal"&gt;&lt;span style="font-size: small;"&gt;&lt;span style="font-family: Calibri;"&gt;&lt;b style="mso-bidi-font-weight: normal"&gt;Q2) &lt;/b&gt;&lt;span style="mso-spacerun: yes"&gt;&amp;nbsp;&lt;/span&gt;When should I perform work asynchronously?&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class="MsoNormal"&gt;&lt;span style="font-family: Calibri; font-size: small;"&gt;A2)&lt;span style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/span&gt;When the benefit of switching threads out weights the cost of the context switch.&lt;span style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/span&gt;In an attempt to simplify this decision, you should only switch if you would otherwise block the ASP.NET request thread while you do nothing.&amp;nbsp; This is an oversimplification, but I'm trying to make the decision simple.&amp;nbsp; For example, if you make an asynchronous web service request to a remote server then it would be better to let the ASP.NET request thread unwind, and when the "receive" for the web service request fires your callback, you would call into ASP.NET allowing it to resume exection of the pipeline.&amp;nbsp; That way the ASP.NET request thread is not blocked doing nothing while you wait for the web service request to complete.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class="MsoNormal"&gt;&lt;span style="font-size: small;"&gt;&lt;span style="font-family: Calibri;"&gt;&lt;b style="mso-bidi-font-weight: normal"&gt;Q3) &lt;/b&gt;&lt;span style="mso-spacerun: yes"&gt;&amp;nbsp;&lt;/span&gt;If my ASP.NET Application uses CLR &lt;/span&gt;&lt;/span&gt;&lt;a href="http://msdn.microsoft.com/en-us/library/system.threading.threadpool.aspx"&gt;&lt;span style="font-family: Calibri; font-size: small;"&gt;ThreadPool&lt;/span&gt;&lt;/a&gt;&lt;span style="font-family: Calibri; font-size: small;"&gt; threads, won&amp;rsquo;t I starve ASP.NET, which also uses the CLR ThreadPool to execute requests?&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class="MsoNormal"&gt;&lt;span style="font-family: Calibri; font-size: small;"&gt;A3) &lt;span style="mso-spacerun: yes"&gt;&amp;nbsp;&lt;/span&gt;Think about it this way:&lt;span style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/span&gt;if you stay on the same thread, you&amp;rsquo;re using one of ASP.NET&amp;rsquo;s threads.&lt;span style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/span&gt;If you switch to another ThreadPool thread and let ASP.NET&amp;rsquo;s thread unwind, you&amp;rsquo;re still only using one thread.&lt;span style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/span&gt;While it is true that ASP.NET in classic mode will queue requests if there are more than 12 &lt;b style="mso-bidi-font-weight: normal"&gt;threads per CPU&lt;/b&gt; currently in-use (by default, see &lt;/span&gt;&lt;a href="http://support.microsoft.com/kb/821268"&gt;&lt;span style="font-family: Calibri; font-size: small;"&gt;KB 821268&lt;/span&gt;&lt;/a&gt;&lt;span style="font-family: Calibri; font-size: small;"&gt;), for most scenarios you don&amp;rsquo;t want more than 12 threads to be executing at any given time.&lt;span style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/span&gt;When you&amp;rsquo;re doing asynchronous work, you&amp;rsquo;re not always using a thread.&lt;span style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/span&gt;For example, if you made an async web service request, your client will not be using any threads between the &amp;ldquo;send&amp;rdquo; and &amp;ldquo;receive&amp;rdquo;.&lt;span style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/span&gt;You unwind after the &amp;ldquo;send&amp;rdquo;, and the &amp;ldquo;receive&amp;rdquo; occurs on an I/O completion port, at which time your callback is invoked and you will then be using a thread again.&lt;span style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/span&gt;(Note that for this scenario your callback is executed on an i/o thread&amp;mdash;ASP.NET only uses worker threads, and only includes worker threads when it counts the threads in-use.)&lt;span style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/span&gt;This is a juggling game, and you will see that when the requests are asynchronous, there can be more requests "executing" at any given time then the number of threads currently in-use.&lt;span style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/span&gt;IIS 7 integrated mode is different, in that ASP.NET does not limit the number of threads in-use by default&amp;mdash;but you do need to set MaxConcurrentRequestsPerCPU = 5000 in v2.0/3.5, as I mentioned in another post.&lt;span style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/span&gt;So to summarize, don&amp;rsquo;t worry about starving ASP.NET of threads, and if you think there&amp;rsquo;s a problem here let me know and we&amp;rsquo;ll take care of it.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class="MsoNormal"&gt;&lt;span style="font-size: small;"&gt;&lt;span style="font-family: Calibri;"&gt;&lt;b style="mso-bidi-font-weight: normal"&gt;Q4)&lt;/b&gt; Should I create my own threads (new &lt;/span&gt;&lt;/span&gt;&lt;a href="http://msdn.microsoft.com/en-us/library/system.threading.thread.aspx"&gt;&lt;span style="font-family: Calibri; font-size: small;"&gt;Thread&lt;/span&gt;&lt;/a&gt;&lt;span style="font-family: Calibri; font-size: small;"&gt;)?&lt;span style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/span&gt;Won&amp;rsquo;t this be better for ASP.NET, since it uses the CLR &lt;/span&gt;&lt;a href="http://msdn.microsoft.com/en-us/library/system.threading.threadpool.aspx"&gt;&lt;span style="font-family: Calibri; font-size: small;"&gt;ThreadPool&lt;/span&gt;&lt;/a&gt;&lt;span style="font-family: Calibri; font-size: small;"&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class="MsoNormal"&gt;&lt;span style="font-size: small;"&gt;&lt;span style="font-family: Calibri;"&gt;A4) &lt;span style="mso-spacerun: yes"&gt;&amp;nbsp;&lt;/span&gt;Please don&amp;rsquo;t.&lt;span style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/span&gt;Or to put it a different way, no!!!&lt;span style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/span&gt;If you&amp;rsquo;re really smart&amp;mdash;much smarter than me&amp;mdash;then you can create your own threads; otherwise, don&amp;rsquo;t even think about it.&lt;span style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/span&gt;Here are some reasons why you should not frequently create new threads: &lt;span style="mso-spacerun: yes"&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class="MsoNormal"&gt;&lt;span style="font-size: small;"&gt;&lt;span style="font-family: Calibri;"&gt;1) &lt;span style="mso-spacerun: yes"&gt;&amp;nbsp;&lt;/span&gt;It is&amp;nbsp;very expensive, compared to QueueUserWorkItem.&lt;span style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class="MsoNormal"&gt;&lt;span style="font-family: Calibri; font-size: small;"&gt;2) Before you allow your thread to exit, you must &lt;/span&gt;&lt;a href="http://msdn.microsoft.com/en-us/library/ms683234(VS.85).aspx"&gt;&lt;span style="font-family: Calibri; font-size: small;"&gt;check for pending I/O&lt;/span&gt;&lt;/a&gt;&lt;span style="font-family: Calibri; font-size: small;"&gt; because your new thread might call into a .NET Framework API that initiates an asynchronous operation, and there&amp;rsquo;s really no way for you to know whether or not this happened unless you don&amp;rsquo;t call into framework APIs. &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class="MsoNormal"&gt;&lt;span style="font-family: Calibri; font-size: small;"&gt;3) The performance of the system hinges on the fact that only a reasonable number of threads are executing at any given time, and if you start creating your own threads it will then become your responsibility to maintain performance of the system.&lt;span style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/span&gt;By the way, if you can write a better ThreadPool than the CLR&amp;rsquo;s, I encourage you to apply for a job at Microsoft, because we&amp;rsquo;re definitely looking for people like you!&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class="MsoNormal"&gt;&lt;span style="font-size: small;"&gt;&lt;span style="font-family: Calibri;"&gt;&lt;b style="mso-bidi-font-weight: normal"&gt;Q5)&lt;/b&gt; What if I want to perform work in the background, independent of any requests?&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class="MsoNormal"&gt;&lt;span style="font-family: Calibri; font-size: small;"&gt;A5) Do you really need it to be independent of a request?&lt;span style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/span&gt;It really doesn&amp;rsquo;t matter how long it runs, it can still be run in the context of a request.&lt;span style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/span&gt;The problem with performing work independent of a request is that the AppDomain can be unloaded, and any threads executing at that time will be rudely aborted, which could cause you quite a bit of grief depending on what you&amp;rsquo;re doing.&lt;span style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/span&gt;In v4.0 ASP.NET, we won&amp;rsquo;t unload the AppDomain until all requests have completed.&lt;span style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/span&gt;In v2.0/3.5 ASP.NET in IIS 7 integrated mode, we also won&amp;rsquo;t unload the AppDomain until all the requests have completed.&lt;span style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/span&gt;In v2.0/3.5 classic mode, we will only wait until httpRuntime/shutdownTimeout has been exceeded before unloading the AppDomain, so long running async requests are vulnerable to being rudely aborted.&lt;span style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/span&gt;To work around this, you&amp;rsquo;d want the shutdownTimeout to be sufficiently long.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class="MsoNormal"&gt;&lt;span style="font-family: Calibri; font-size: small;"&gt;If you really want this asynchronous work to be independent of any requests, then I recommend that you register for a notification that will tell you when the domain is about to shutdown so that you can safely complete/timeout the work. &lt;span style="mso-spacerun: yes"&gt;&amp;nbsp;&lt;/span&gt;There are three ways to do this: &lt;/span&gt;&lt;/p&gt;
&lt;p class="MsoListParagraphCxSpFirst"&gt;&lt;span style="mso-bidi-font-family: Calibri; mso-bidi-theme-font: minor-latin"&gt;&lt;span style="mso-list: Ignore"&gt;&lt;span style="font-family: Calibri; font-size: small;"&gt;1)&lt;/span&gt;&lt;span style="FONT: 7pt 'Times New Roman'"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size: small;"&gt;&lt;span style="font-family: Calibri;"&gt;Use the &lt;a href="http://msdn.microsoft.com/en-us/library/ms178473.aspx"&gt;ApplicationEnd&lt;/a&gt; event.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p class="MsoListParagraphCxSpFirst"&gt;&lt;span style="font-size: small;"&gt;&lt;span style="font-family: Calibri;"&gt;2)&amp;nbsp;&amp;nbsp; T&lt;/span&gt;&lt;span style="font-family: Calibri;"&gt;here is an &lt;/span&gt;&lt;/span&gt;&lt;a href="http://msdn.microsoft.com/en-us/library/system.appdomain.domainunload.aspx"&gt;&lt;span style="font-family: Calibri; font-size: small;"&gt;AppDomain.DomainUnload&lt;/span&gt;&lt;/a&gt;&lt;span style="font-family: Calibri; font-size: small;"&gt; event which is fired immediately before the AppDomain is unloaded.&lt;/span&gt;&lt;/p&gt;
&lt;p class="MsoListParagraphCxSpLast"&gt;&lt;span style="mso-bidi-font-family: Calibri; mso-bidi-theme-font: minor-latin"&gt;&lt;span style="mso-list: Ignore"&gt;&lt;span style="font-family: Calibri; font-size: small;"&gt;3)&lt;/span&gt;&lt;span style="FONT: 7pt 'Times New Roman'"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-family: Calibri; font-size: small;"&gt;You can create an object that implements the &lt;/span&gt;&lt;a href="http://msdn.microsoft.com/en-us/library/system.web.hosting.iregisteredobject.aspx"&gt;&lt;span style="font-family: Calibri; font-size: small;"&gt;IRegisteredObject&lt;/span&gt;&lt;/a&gt;&lt;span style="font-family: Calibri; font-size: small;"&gt; interface and call &lt;/span&gt;&lt;a href="http://msdn.microsoft.com/en-us/library/system.web.hosting.hostingenvironment.registerobject.aspx"&gt;&lt;span style="font-family: Calibri; font-size: small;"&gt;HostingEnvironment.RegisterObject&lt;/span&gt;&lt;/a&gt;&lt;span style="font-family: Calibri; font-size: small;"&gt; to &amp;ldquo;register&amp;rdquo; it with ASP.NET.&lt;span style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/span&gt;When the AppDomain is about to be unloaded, we will call your implementation of IRegisteredObject.Stop(bool immediate).&lt;span style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/span&gt;The Stop method is called twice by ASP.NET, once with the immediate parameter set to false and once again with that argument set to true.&lt;span style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/span&gt;You are supposed to call &lt;/span&gt;&lt;a href="http://msdn.microsoft.com/en-us/library/system.web.hosting.hostingenvironment.unregisterobject.aspx"&gt;&lt;span style="font-family: Calibri; font-size: small;"&gt;HostingEnvironment.UnregisterObject&lt;/span&gt;&lt;/a&gt;&lt;span style="font-family: Calibri; font-size: small;"&gt; as soon as your registered object has stopped, so ASP.NET knows it doesn&amp;rsquo;t need to call your Stop method.&lt;span style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/span&gt;You can call it at anytime, but you definitely should call it before returning from Stop when it is called with immediate set to true, because that&amp;rsquo;s your final chance and if you&amp;rsquo;re still running after that you will be rudely aborted.&lt;span style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/span&gt;This functionality was not implemented specifically for the ability to execute long running tasks independent of any requests, but it will enable&lt;span style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/span&gt;you to do this and safely shutdown/complete your work.&lt;span style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/span&gt;Note that the point of calling Stop twice is to allow you to asynchronously kick off a job to stop your long running task when Stop is called the first time, giving you a bit more time before it is called the second time and you have to shutdown.&lt;span style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/span&gt;If you need to, you can hold up the unload as long as you like, because we won&amp;rsquo;t unload until your Stop method returns the second time.&lt;span style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/span&gt;However, you should be well behaved, and return in a reasonable amount of time, and preferably immediately the first time Stop is called.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class="MsoNormal"&gt;&lt;o:p&gt;&lt;span style="font-family: Calibri; font-size: small;"&gt;&lt;strong&gt;Q6)&lt;/strong&gt; You didn't answer my question.&lt;/span&gt;&lt;/o:p&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class="MsoNormal"&gt;&lt;o:p&gt;&lt;span style="font-family: Calibri; font-size: small;"&gt;A6) To be fair, I don't know what your question is.&amp;nbsp; I don't have anonymous comments enabled (too much spam), but if you take a few seconds to register you can leave comments below and I will answer them.&lt;/span&gt;&lt;/o:p&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class="MsoNormal"&gt;&lt;o:p&gt;&lt;span style="font-family: Calibri; font-size: small;"&gt;Thank you,&lt;br /&gt;&lt;/span&gt;&lt;/o:p&gt;&lt;o:p&gt;&lt;span style="font-family: Calibri; font-size: small;"&gt;Thomas&lt;/span&gt;&lt;/o:p&gt;&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=9996034" width="1" height="1"&gt;</content><author><name>tmarq</name><uri>http://blogs.msdn.com/tmarq/ProfileUrlRedirect.ashx</uri></author></entry><entry><title>How ASP.NET MVC Routing Works and its Impact on the Performance of Static Requests</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/b/tmarq/archive/2010/04/01/asp-net-4-0-enables-routing-of-extensionless-urls-without-impacting-static-requests.aspx" /><id>http://blogs.msdn.com/b/tmarq/archive/2010/04/01/asp-net-4-0-enables-routing-of-extensionless-urls-without-impacting-static-requests.aspx</id><published>2010-04-01T17:12:00Z</published><updated>2010-04-01T17:12:00Z</updated><content type="html">&lt;p&gt;&lt;span style="FONT-FAMILY: 'Arial','sans-serif'; FONT-SIZE: 10pt"&gt;Recently a number of people have asked how MVC and ASP.NET routing impacts the performance of static requests (HTML, JPG, GIF, CSS, JS, etc).&amp;nbsp; I'll answer that question below while explaining how&amp;nbsp;routing is implemented in the ASP.NET pipeline on IIS 6 and IIS 7.&amp;nbsp; This applies to both ASP.NET 3.5 and 4.0, unless otherwise stated.&amp;nbsp; I'll also talk about the new extensionless URL routing feature in ASP.NET 4.0.&lt;/span&gt;&lt;/p&gt;
&lt;h3&gt;How ASP.NET Routing Works&lt;/h3&gt;
&lt;p&gt;&lt;span style="FONT-FAMILY: 'Arial','sans-serif'; FONT-SIZE: 10pt"&gt;URLs are mapped to handlers by IIS when the request is initially received.&amp;nbsp; I'll refer to the ASP.NET handlers as managed handlers, and everything else as&amp;nbsp;unmanaged handlers.&amp;nbsp; A request with a managed handler will enter ASP.NET and be processed by the various managed modules in the pipeline.&amp;nbsp; On IIS 6,&amp;nbsp;a request with an unmanaged handler won't be processed by managed code.&amp;nbsp; On IIS 7, a request with an unmanaged handler, also, won't be processed by managed code, at least not by default.&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style="FONT-FAMILY: 'Arial','sans-serif'; FONT-SIZE: 10pt"&gt;The first step to make routing work for requests with unmanaged handlers is to direct these requests into managed code.&amp;nbsp; On IIS 6 this is done with a wildcard mapping to aspnet_isapi.dll.&amp;nbsp; On IIS 7&amp;nbsp;this is done either by setting &lt;strong&gt;&lt;span style="FONT-FAMILY: 'Arial','sans-serif'"&gt;runAllManagedModulesForAllRequests=&amp;rdquo;true&amp;rdquo;&lt;/span&gt;&lt;/strong&gt; or removing the &lt;strong&gt;&lt;span style="FONT-FAMILY: 'Arial','sans-serif'"&gt;"managedHandler" preCondition&lt;/span&gt;&lt;/strong&gt; for the &lt;strong&gt;&lt;span style="FONT-FAMILY: 'Arial','sans-serif'"&gt;UrlRoutingModule&lt;/span&gt;&lt;/strong&gt;.&amp;nbsp; Ok, so now the requests are coming into managed code, what happens next?&amp;nbsp; Well, the &lt;strong&gt;&lt;span style="FONT-FAMILY: 'Arial','sans-serif'"&gt;UrlRoutingModule&lt;/span&gt;&lt;/strong&gt; inspects the requests and will change the handler mapping according to the route table.&amp;nbsp; If it doesn't change the handler mapping, then the original handler mapping will be used--the one set by IIS.&amp;nbsp; It's actually a bit more complicated on IIS 6 because of the wildcard mapping.&amp;nbsp; On IIS 6, if the &lt;strong&gt;&lt;span style="FONT-FAMILY: 'Arial','sans-serif'"&gt;UrlRoutingModule&lt;/span&gt;&lt;/strong&gt; doesn't change the handler,&amp;nbsp;ASP.NET will issue a child request that allows the request to execute with the handler mapping that would have been applied originally if the wildcard mapping didn't exist.&amp;nbsp; Make sense?&lt;/span&gt;&lt;/p&gt;
&lt;h3&gt;Impact on Performance of Static Requests&lt;/h3&gt;
&lt;p&gt;&lt;span style="FONT-FAMILY: 'Arial','sans-serif'; FONT-SIZE: 10pt"&gt;Okay, so what about the performance of static requests (HTML, JPG, GIF, CSS, JS, etc)?&amp;nbsp; If a request enters managed code, the throughput for that request will be reduced to *approximately* 1/3 of what it would have been, assuming that no caching is involved.&amp;nbsp;&amp;nbsp;Note that I said *approximately*, because the actual result will depend upon hardware, software, the size of the file, etc.&amp;nbsp; Fortunately kernel caching comes to the rescue on IIS 7.&amp;nbsp; If you have an application that uses routing in such a way that *all* requests enter managed code, as long as you don&amp;rsquo;t change the handler for static requests, they will be kernel cached on IIS 7&amp;nbsp;(if they&amp;rsquo;re hot, and meet kernel cache requirements).&amp;nbsp; &lt;/span&gt;&lt;span style="FONT-FAMILY: 'Arial','sans-serif'; FONT-SIZE: 10pt"&gt;What about kernel caching on IIS 6?&amp;nbsp; Unfortunately kernel caching is disabled for child requests on IIS 6, which means enabling ASP.NET routing on IIS 6 effectively disables kernel caching of static requests.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style="FONT-FAMILY: 'Arial','sans-serif'; FONT-SIZE: 10pt"&gt;&lt;span style="FONT-FAMILY: 'Arial','sans-serif'; FONT-SIZE: 10pt"&gt;So does that mean I should put my static&amp;nbsp;content in an application that isn&amp;rsquo;t using routing,&amp;nbsp;if I'm concerned about the performance of static requests?&amp;nbsp; On IIS 6, the kernel cache is effectively disabled for static content when ASP.NET routing is enabled; so yes, you proably should partition your site.&amp;nbsp; On IIS 7 it's only the first couple requests to the static file&amp;nbsp;that miss the kernel cache, assuming the request meets kernel caching requirements.&amp;nbsp; However, there is a better&amp;nbsp;way to enable ASP.NET routing&amp;nbsp;on IIS 7, and ASP.NET 4.0 makes use of it by default.&amp;nbsp; Keep reading and I'll also tell you how to enable this on ASP.NET 3.5.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h3&gt;ASP.NET 4.0 Enables Routing of Extensionless URLs&lt;/h3&gt;
&lt;p&gt;&lt;span style="FONT-FAMILY: 'Arial','sans-serif'; FONT-SIZE: 10pt"&gt;In ASP.NET v4.0, there is a better&amp;nbsp;way to&amp;nbsp;enable routing.&amp;nbsp; Normally you're only interested in routing extensionless URLs, and have no need to route static requests (HTML, JPG, GIF, CSS, JS, etc).&amp;nbsp; In v4.0 there is a new feature that allows extensionless URLs to be&amp;nbsp;directed into managed code, without&amp;nbsp;impacting static requests (HTML, JPG, GIF, CSS, JS, etc).&amp;nbsp;&amp;nbsp;Because of this feature, on IIS 6 you no longer need a wildcard mapping and on IIS 7 you no longer need to set &lt;strong&gt;runAllManagedModulesForAllRequests=&amp;rdquo;true&amp;rdquo;&lt;/strong&gt; or remove the &lt;strong&gt;"managedHandler" precondition&lt;/strong&gt; for the &lt;strong&gt;UrlRoutingModule&lt;/strong&gt;.&amp;nbsp; It works by default on both IIS 6 and IIS 7, except that&amp;nbsp;you need a QFE from the IIS team to make this work on Windows Vista SP2, Windows Server 2008 SP2, Windows Server 2008 R2, and Windows 7.&amp;nbsp; Once you obtain the IIS 7 QFE and install v4.0 ASP.NET, you&amp;rsquo;ll be able to route extensionless URLs without impacting static requests.&amp;nbsp; The QFE enables a new &amp;ldquo;*.&amp;rdquo; handler mapping&amp;mdash;the notation may seem weird, but all you care about is the fact that this maps to URLs without an extension.&amp;nbsp; ASP.NET registers a &amp;ldquo;*.&amp;rdquo; handler mapping when v4.0 is installed.&amp;nbsp; If you don&amp;rsquo;t have the IIS 7 QFE, that handler mapping does nothing.&amp;nbsp; If you have the IIS 7 QFE, extensionless URLs are mapped to our handler, which enables&amp;nbsp;them to be routed by the UrlRoutingModule.&amp;nbsp;&amp;nbsp;Information about the IIS 7 QFE and steps to download it can be found at &lt;a href="http://support.microsoft.com/kb/980368"&gt;http://support.microsoft.com/kb/980368&lt;/a&gt;.&amp;nbsp; For the record, the implementation of this feature on IIS 6 is done quite differently, and I won't go into that here.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style="FONT-FAMILY: 'Arial','sans-serif'; FONT-SIZE: 10pt"&gt;Just in case you need to disable the feature, I'll tell you how.&amp;nbsp; &lt;/span&gt;&lt;span style="FONT-FAMILY: 'Arial','sans-serif'; FONT-SIZE: 10pt"&gt;On IIS 6, you can disable the feature by setting a DWORD registry key&amp;nbsp;at HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\ASP.NET\4.0.30319\EnableExtensionlessUrls = 0 (the default is 1, even when the key does not exist).&amp;nbsp; &lt;/span&gt;&lt;span style="FONT-FAMILY: 'Arial','sans-serif'; FONT-SIZE: 10pt"&gt;On IIS 7, you can disable the feature by removing the "*." handler mappings.&amp;nbsp; There are three of them (1 for 32-bit classic mode, 1 for 64-bit classic mode, and 1 for integrated mode) and the names of these handler mappings in applicationHost.config are:&amp;nbsp; "ExtensionlessUrl-ISAPI-4.0_32bit", "ExtensionlessUrl-ISAPI-4.0_64bit", and "ExtensionlessUrl-Integrated-4.0".&lt;/span&gt;&lt;/p&gt;
&lt;h3&gt;How to Enable Extensionless URL Routing on ASP.NET 3.5 for MVC&lt;/h3&gt;
&lt;p&gt;Similarly to the way it is done in ASP.NET 4.0, you can enable routing of extensionless URLs on ASP.NET 3.5 (and even ASP.NET 2.0)&amp;nbsp;without&amp;nbsp;setting runAllManagedModulesForAllRequests="true" or configuring a wildcard script handler.&amp;nbsp; To do this, you need one of the following operating systems:&amp;nbsp; Windows Vista SP2, Windows Server 2008 SP2, Windows Server 2008 R2, or Windows 7.&amp;nbsp; And you need to patch it with the hotfix available at &lt;a href="http://support.microsoft.com/kb/980368"&gt;http://support.microsoft.com/kb/980368&lt;/a&gt;&amp;nbsp;(this hotfix will eventually be rolled into a future service pack, of course).&amp;nbsp; Once you have the hotfix installed, simply add the following web.config to your application and take the source code for MyTransferRequestHandler (also below) and put it in a CS file located in the App_Code folder of your application.&amp;nbsp; Alternatively you can compile MyTransferRequestHandler into a DLL and place it in the bin, or better yet, give it a strong name, install it in the GAC, and NGEN it.&amp;nbsp; After doing this, extensionless URLs will be directed into managed code, and you can then route them to a different handler.&amp;nbsp; If you choose not to route them to a different handler, they will be directed back to IIS so that they can be executed by the handler that would have served them if our extensionless URL handler was not installed.&lt;/p&gt;
&lt;p&gt;"web.config":&lt;/p&gt;
&lt;div class="code_sample"&gt;
&lt;pre&gt;&amp;lt;configuration&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;lt;system.webServer&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;handlers&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;!-- THESE HANDLER MAPPINGS SHOULD GO BEFORE &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ANY OTHER HANDLER MAPPINGS --&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;add name="EURL-ISAPI-2.0_32bit" &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; path="*." &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; verb="GET,HEAD,POST,DEBUG" &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; modules="IsapiModule"&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; scriptProcessor="%SystemRoot%\Microsoft.NET\Framework\v2.0.50727\aspnet_isapi.dll" &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; preCondition="classicMode,runtimeVersionv2.0,bitness32" &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; responseBufferLimit="0"/&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;add name="EURL-ISAPI-2.0_64bit" &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; path="*." verb="GET,HEAD,POST,DEBUG"&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; modules="IsapiModule" &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; scriptProcessor="%SystemRoot%\Microsoft.NET\Framework64\v2.0.50727\aspnet_isapi.dll" &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; preCondition="classicMode,runtimeVersionv2.0,bitness64" &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; responseBufferLimit="0"/&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;add name="EURL-integrated-2.0" &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; path="*." &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; verb="GET,HEAD,POST,DEBUG" &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; type="MySample.MyTransferRequestHandler" &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; preCondition="integratedMode,runtimeVersionv2.0"/&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;!-- PLACE ADDITIONAL HANDLER MAPPINGS BELOW HERE --&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/handlers&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;modules runAllManagedModulesForAllRequests="false"&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;!-- BE CERTAIN TO SET runAllManagedModulesForAllRequests="false" --&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;!-- AND IF YOU ADDED A WILDCARD SCRIPT MAPPING, REMOVE THAT TOO --&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/modules&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;lt;/system.webServer&amp;gt;&lt;br /&gt;&amp;lt;/configuration&amp;gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;"App_Code/MyTransferRequestHandler":&lt;/p&gt;
&lt;div class="code_sample"&gt;
&lt;pre&gt;namespace MySample {
    using System;
    using System.Web;
    // This handler only works on IIS 7 in integrated mode and is
    // designed to be used as a "*." handler mapping.  We will refer
    // to "*." as the extensionless handler mapping.  There is a bug
    // in Windows Vista, Windows Server 2008, and Windows Server 2008 R2
    // that prevents the IIS 7 extensionless handler mapping from working
    // correctly, and if you have not already patched your machine you 
    // will need to install this hotfix: http://support.microsoft.com/kb/980368.
    // 
    // WARNING: You may be wondering how this handler works, since it passes
    // the original URL to the TransferRequest method.  Why doesn't it do the
    // same thing the second time the URL is requested?  Well, TransferRequest
    // invokes the IIS 7 API IHttpContext::ExecuteRequest, and includes the
    // EXECUTE_FLAG_IGNORE_CURRENT_INTERCEPTOR flag in the third argument to
    // ExecuteRequest.  This causes the "*.", or extensionless, handler mapping
    // to be skipped, and allows the handler that would have executed originally
    // to serve this request. If you attempt to use this handler without an
    // extensionless handler mapping, it will probably result in recursion.
    // This recurssion will eventually be stopped by IIS once the loop iterates
    // about 12 times, and then IIS will respond with a 500 status, a message 
    // that says "HTTP Error 500.0 - Internal Server Error", and an HRESULT  
    // value of 0x800703e9.  The system error message for this HRESULT is
    // "Recursion too deep; the stack overflowed.".
    public class MyTransferRequestHandler : IHttpHandler {        
        public void ProcessRequest(HttpContext context) {
            context.Server.TransferRequest(context.Request.RawUrl, 
                                           true /*preserveForm*/);
        }       
        public bool IsReusable {
            get {
                return true;
            }
        }
    }
}&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style="FONT-FAMILY: 'Arial','sans-serif'; FONT-SIZE: 10pt"&gt;&lt;span style="FONT-FAMILY: 'Arial','sans-serif'; FONT-SIZE: 10pt"&gt;&lt;o:p&gt;&lt;span style="FONT-FAMILY: 'Arial','sans-serif'; FONT-SIZE: 10pt"&gt;&lt;span style="FONT-FAMILY: 'Arial','sans-serif'; FONT-SIZE: 10pt"&gt;Wondering how the extensionless URL handler mappings work?&amp;nbsp;&amp;nbsp;Wondering why it works for both classic and integrated mode?&amp;nbsp;In classic mode, the "*." handler mapping is to our aspnet_isapi.dll.&amp;nbsp;&amp;nbsp;Extensionless URLs&amp;nbsp;will be mapped to the DefaultHttpHandler in classic mode.&amp;nbsp; The DefaultHttpHandler is listed in the &amp;lt;httpHandlers&amp;gt; section in the root web.config file with path="*", so it catches everything that is not caught by something else.&amp;nbsp; If you don't change the handler before the MapRequestHandler event, the DefaultHttpHandler will issue a child request to the original URL with the HSE_EXEC_URL_IGNORE_CURRENT_INTERCEPTOR flag.&amp;nbsp; This tells IIS to skip our extensionless URL handler and map the request to the handler that would normally serve this type of request.&amp;nbsp; If on the hand you want ASP.NET to handle it, you can change the handler by calling HttpContext.RemapHandler before the MapRequestHandler event.&amp;nbsp; This is what ASP.NET routing does in v4.0--in v3.5, routing essentially does&amp;nbsp;the same, but&amp;nbsp;in a much more round about manner.&amp;nbsp; So that's how the extensionless URL handler works in classic mode.&amp;nbsp; In integrated mode, it's essentially the same, but instead of going through the ISAPI path we're able to call the IIS API IHttpContext::ExecuteRequest directly, and similarly we pass a flag to ignore the current interceptor, but the flag is called EXECUTE_FLAG_IGNORE_CURRENT_INTERCEPTOR.&amp;nbsp; The ISAPI code path actually uses these same APIs, but not directly.&lt;/span&gt;&lt;/span&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h3&gt;Troubleshooting&lt;/h3&gt;
&lt;p&gt;&lt;span style="FONT-FAMILY: 'Arial','sans-serif'; FONT-SIZE: 10pt"&gt;&lt;span style="FONT-FAMILY: 'Arial','sans-serif'; FONT-SIZE: 10pt"&gt;&lt;o:p&gt;&lt;span style="FONT-FAMILY: 'Arial','sans-serif'; FONT-SIZE: 10pt"&gt;&lt;span style="FONT-FAMILY: 'Arial','sans-serif'; FONT-SIZE: 10pt"&gt;&lt;/span&gt;&lt;/span&gt;&lt;/o:p&gt;&lt;/span&gt;Having trouble with the MyTransferRequestHandler shown above?&amp;nbsp; That code only directs extensionless URLs into managed code.&amp;nbsp; You need to use routing or something else in order to handle them.&amp;nbsp; If you just want to see if extensionless requests are entering managed code, you can add the following to your web.config and put the MyInterceptorModule source code in&amp;nbsp;a file named MyInterceptorModule.cs within your App_Code directory.&amp;nbsp; The MyInterceptorModule intercepts all requests that enter managed code and displays information about the URL and the values of several server variables.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;"web.config additions":&lt;/p&gt;
&lt;div class="code_sample"&gt;
&lt;pre&gt;&amp;lt;system.web&amp;gt;
    &amp;lt;httpModules&amp;gt;
      &amp;lt;add 
        name="MyInterceptorModule" 
        type="MySample.MyInterceptorModule"/&amp;gt;
    &amp;lt;/httpModules&amp;gt;
  &amp;lt;/system.web&amp;gt;
  &amp;lt;system.webServer&amp;gt;
    &amp;lt;validation validateIntegratedModeConfiguration="false"/&amp;gt;
    &amp;lt;modules runAllManagedModulesForAllRequests="false"&amp;gt;
      &amp;lt;add name="MyInterceptorModule" 
        type="MySample.MyInterceptorModule" 
        preCondition="managedHandler"/&amp;gt;&lt;/pre&gt;
&lt;pre&gt;  &amp;lt;/modules&amp;gt;
  &amp;lt;/system.webServer&amp;gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;"App_Code/MyInterceptor":&lt;/p&gt;
&lt;div class="code_sample"&gt;
&lt;pre&gt;namespace MySample {
    using System;
    using System.Web;
    public class MyInterceptorModule : IHttpModule {
        public void Dispose() {}
        public void Init( HttpApplication app ) {
            app.BeginRequest += new EventHandler( OnBeginRequest );
        }
        private void OnBeginRequest(object sender, EventArgs e) {
            HttpApplication app = sender as HttpApplication;
            HttpResponse response = app.Context.Response;
            HttpRequest request = app.Context.Request;
            response.Output.WriteLine("&amp;lt;html&amp;gt;&amp;lt;head&amp;gt;&amp;lt;title&amp;gt;intercepted&amp;lt;/title&amp;gt;&amp;lt;/head&amp;gt;&amp;lt;body&amp;gt;&amp;lt;pre&amp;gt;");
            response.Output.WriteLine("Path={0}", request.Path);
            response.Output.WriteLine("PathInfo={0}", request.PathInfo);
            response.Output.WriteLine("QueryString={0}", request.QueryString);
            response.Output.WriteLine("RawUrl={0}", request.RawUrl);
            foreach(string key in request.ServerVariables.AllKeys) {
                response.Output.WriteLine(
                       "{0} = {1}", key, request.ServerVariables[key]);
            }
            response.Output.WriteLine("&amp;lt;/pre&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;");
            app.CompleteRequest();
        } 
    }
}&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style="FONT-FAMILY: 'Arial','sans-serif'; FONT-SIZE: 10pt"&gt;&lt;o:p&gt;&lt;span style="FONT-FAMILY: 'Arial','sans-serif'; FONT-SIZE: 10pt"&gt;&lt;span style="FONT-FAMILY: 'Arial','sans-serif'; FONT-SIZE: 10pt"&gt;&lt;/span&gt;&lt;/span&gt;&lt;/o:p&gt;&lt;/span&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style="FONT-FAMILY: 'Arial','sans-serif'; FONT-SIZE: 10pt"&gt;&lt;o:p&gt;&lt;span style="FONT-FAMILY: 'Arial','sans-serif'; FONT-SIZE: 10pt"&gt;&lt;span style="FONT-FAMILY: 'Arial','sans-serif'; FONT-SIZE: 10pt"&gt;Note that if you're experimenting and want to see what is kernel cached, there are a number of things that prevent&amp;nbsp;a resource from being kernel cached.&amp;nbsp; Be sure to include an Accept-Encoding header, for example most browsers include "Accept-Encoding: gzip,deflate".&amp;nbsp; Also don't&amp;nbsp;issue a conditional GET, for example, make sure your browser is not using "If-Modified-Since".&amp;nbsp;Request the page a couple times in order to kernel cache it.&amp;nbsp; To see if it is served from the kernel cache, start&amp;nbsp;Performance&amp;nbsp;Monitor&amp;nbsp;(Start|Run|type perfmon.msc) and add the following performance counters:&amp;nbsp;"Web Service Cache\Kernel: URI Cache Hits", "Web Service Cache\Kernel: URI Cache Misses", and "Web Service Cache\Kernel: Current URIs Cached".&lt;/span&gt;&lt;/span&gt;&lt;span style="FONT-FAMILY: 'Arial','sans-serif'; FONT-SIZE: 10pt"&gt;&lt;span style="FONT-FAMILY: 'Arial','sans-serif'; FONT-SIZE: 10pt"&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style="FONT-FAMILY: 'Arial','sans-serif'; FONT-SIZE: 10pt"&gt;Regards,&lt;br /&gt;Thomas&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p class="MsoNormal"&gt;&lt;span style="FONT-FAMILY: 'Arial','sans-serif'; FONT-SIZE: 9pt; mso-bidi-font-family: 'Times New Roman'; mso-bidi-theme-font: minor-bidi; mso-bidi-font-size: 11.0pt"&gt;&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;/span&gt;&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=9989032" width="1" height="1"&gt;</content><author><name>tmarq</name><uri>http://blogs.msdn.com/tmarq/ProfileUrlRedirect.ashx</uri></author><category term="Extensionless URLs" scheme="http://blogs.msdn.com/b/tmarq/archive/tags/Extensionless+URLs/" /></entry><entry><title>Correct use of System.Web.HttpResponse.Redirect</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/b/tmarq/archive/2009/06/25/correct-use-of-system-web-httpresponse-redirect.aspx" /><link rel="enclosure" type="application/x-zip-compressed" length="739" href="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-components-postattachments/00-09-80-39-56/sample.zip" /><id>http://blogs.msdn.com/b/tmarq/archive/2009/06/25/correct-use-of-system-web-httpresponse-redirect.aspx</id><published>2009-06-25T20:10:00Z</published><updated>2009-06-25T20:10:00Z</updated><content type="html">&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;SPAN style="FONT-FAMILY: 'Tahoma','sans-serif'; FONT-SIZE: 10pt"&gt;Try very, very&amp;nbsp;hard to avoid using Response.Redirect(url), instead, use Response.Redirect(url, &lt;STRONG&gt;false&lt;/STRONG&gt;).&amp;nbsp; Response.Redirect(url), after writing a 302 redirect response to the response buffers, calls Response.End.&amp;nbsp; This is very expensive.&amp;nbsp; The alternative, Response.Redirect(url, &lt;STRONG&gt;false&lt;/STRONG&gt;) is fast, but unlike Response.Redirect(url), the lines of code which&amp;nbsp;follow the call to Response.Redirect(url, &lt;STRONG&gt;false&lt;/STRONG&gt;) will be executed.&amp;nbsp; More on this later, but first, let me tell you about the horrors of Response.End.&lt;/SPAN&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;SPAN style="FONT-FAMILY: 'Tahoma','sans-serif'; FONT-SIZE: 10pt"&gt;&lt;/SPAN&gt;&lt;SPAN style="FONT-FAMILY: 'Tahoma','sans-serif'; FONT-SIZE: 10pt"&gt;&lt;/SPAN&gt;&amp;nbsp;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;SPAN style="FONT-FAMILY: 'Tahoma','sans-serif'; FONT-SIZE: 10pt"&gt;Response.End is a terrible method.&amp;nbsp; It was added in ASP.NET 1.0 for compatibility with classic ASP.&amp;nbsp;&amp;nbsp;In classic ASP, this method terminated script processing, so that the lines of script that followed this call never executed.&amp;nbsp; How do you simulate that in managed code?&amp;nbsp; The only way is to abort the thread, which raises a ThreadAbortException and&amp;nbsp;is&amp;nbsp;outrageously expensive.&amp;nbsp; Normally, that's exactly what Response.End does in ASP.NET, however, if it is called from within an asynchronous handler/module (a.k.a. an asynchronous pipeline event), it will instead perform a synchronous flush of the response buffers (can you say expensive?) and then complete the request by calling Context.ApplicationInstance.CompleteRequest().&amp;nbsp; Either way, whether you're calling it from a synchronous handler/module or an asynchronous handler/module, Response.End is horribly expensive, and you should avoid it.&lt;/SPAN&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;SPAN style="FONT-FAMILY: 'Tahoma','sans-serif'; FONT-SIZE: 10pt"&gt;&lt;/SPAN&gt;&amp;nbsp;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;SPAN style="FONT-FAMILY: 'Tahoma','sans-serif'; FONT-SIZE: 10pt"&gt;Ok, so what if&amp;nbsp;you don't want the lines of code to execute after&amp;nbsp;you redirect?&amp;nbsp; Well, one way to accomplish this is to call HttpApplication.CompleteRequest(), which is accessible from the HttpContext. e.g., call calling Context.ApplicationInstance.CompleteRequest().&amp;nbsp; It's not the same as aborting the thread, which truly does prevent all subsequent lines of code form running.&amp;nbsp; The lines of code that follow the call to CompleteRequest() will execute, but as soon as the current page or module that calls this completes, the pipeline will jump ahead to the EndRequest event, thereby short circuiting the pipeline.&amp;nbsp; This is usually all you need.&lt;/SPAN&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;SPAN style="FONT-FAMILY: 'Tahoma','sans-serif'; FONT-SIZE: 10pt"&gt;&lt;/SPAN&gt;&amp;nbsp;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;SPAN style="FONT-FAMILY: 'Tahoma','sans-serif'; FONT-SIZE: 10pt"&gt;So to summarize...&lt;/SPAN&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;SPAN style="FONT-FAMILY: 'Tahoma','sans-serif'; FONT-SIZE: 10pt"&gt;&lt;/SPAN&gt;&amp;nbsp;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;SPAN style="FONT-FAMILY: 'Tahoma','sans-serif'; FONT-SIZE: 10pt"&gt;&lt;STRONG&gt;BAD&lt;/STRONG&gt;:&lt;/SPAN&gt;&lt;/P&gt;&lt;SPAN style="FONT-FAMILY: 'Tahoma','sans-serif'; FONT-SIZE: 10pt"&gt;
&lt;P style="MARGIN: 0in 0in 0pt 0.5in" class=MsoNormal&gt;&lt;FONT size=3 face=Calibri&gt;Response.Redirect(url);&lt;/FONT&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;SPAN style="FONT-FAMILY: 'Tahoma','sans-serif'; FONT-SIZE: 10pt"&gt;&lt;?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" /&gt;&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;SPAN style="FONT-FAMILY: 'Tahoma','sans-serif'; FONT-SIZE: 10pt"&gt;&lt;o:p&gt;&lt;STRONG&gt;GOOD&lt;/STRONG&gt;:&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;&lt;SPAN style="FONT-FAMILY: 'Tahoma','sans-serif'; FONT-SIZE: 10pt"&gt;&lt;o:p&gt;
&lt;P style="MARGIN: 0in 0in 0pt 0.5in" class=MsoNormal&gt;&lt;FONT size=3 face=Calibri&gt;Response.Redirect(url, &lt;B&gt;false&lt;/B&gt;);&lt;/FONT&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt 0.5in" class=MsoNormal&gt;&lt;FONT size=3 face=Calibri&gt;Context.ApplicationInstance.&lt;B&gt;CompleteRequest()&lt;/B&gt;;&lt;/FONT&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;SPAN style="FONT-FAMILY: 'Tahoma','sans-serif'; FONT-SIZE: 10pt"&gt;&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;SPAN style="FONT-FAMILY: 'Tahoma','sans-serif'; FONT-SIZE: 10pt"&gt;&lt;/SPAN&gt;&lt;SPAN style="FONT-FAMILY: 'Tahoma','sans-serif'; FONT-SIZE: 10pt"&gt;But before I put my pen away, there's one more common practice I've seen that people should be aware of .&amp;nbsp; &lt;/SPAN&gt;&lt;SPAN style="FONT-FAMILY: 'Tahoma','sans-serif'; FONT-SIZE: 10pt"&gt;In classic mode, calling &lt;STRONG&gt;Response.Redirect(url)&lt;/STRONG&gt; from Application_Error &lt;STRONG&gt;without calling Server.ClearError&lt;/STRONG&gt; is doubly expensive.&amp;nbsp; It causes three exceptions to be thrown, and then either raises a ThreadAbortException or does a synchronous flush and completes the response.&amp;nbsp; And in integrated mode, calling Response.Redirect(url) from Application_Error &lt;STRONG&gt;without calling Server.ClearError&lt;/STRONG&gt; does not even work.&amp;nbsp; Instead, you should use the following code, which&amp;nbsp;performs&amp;nbsp;well in both classic mode and integrated mode.&amp;nbsp; If you’re going to redirect from Application_Error, you should do it the following way:&lt;/SPAN&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;SPAN style="FONT-FAMILY: 'Tahoma','sans-serif'; FONT-SIZE: 10pt"&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&amp;nbsp;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;SPAN style="FONT-FAMILY: 'Tahoma','sans-serif'; FONT-SIZE: 10pt"&gt;&lt;SPAN style="FONT-FAMILY: 'Tahoma','sans-serif'; FONT-SIZE: 10pt"&gt;&lt;o:p&gt;&lt;STRONG&gt;GOOD&lt;/STRONG&gt;:&lt;/o:p&gt;&lt;/SPAN&gt;&amp;nbsp;&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt 0.5in" class=MsoNormal&gt;&lt;FONT size=3 face=Calibri&gt;void Application_Error(object sender, EventArgs e)&lt;/FONT&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt 0.5in" class=MsoNormal&gt;&lt;FONT size=3 face=Calibri&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;/FONT&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt 0.5in" class=MsoNormal&gt;&lt;FONT size=3 face=Calibri&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Server.&lt;B&gt;ClearError()&lt;/B&gt;;&lt;/FONT&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt 0.5in" class=MsoNormal&gt;&lt;FONT size=3 face=Calibri&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Response.Redirect(url, &lt;B&gt;false&lt;/B&gt;);&lt;/FONT&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt 0.5in" class=MsoNormal&gt;&lt;FONT size=3 face=Calibri&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Context.ApplicationInstance.&lt;B&gt;CompleteRequest()&lt;/B&gt;;&lt;/FONT&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt 0.5in" class=MsoNormal&gt;&lt;FONT size=3 face=Calibri&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/FONT&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;SPAN style="FONT-FAMILY: 'Tahoma','sans-serif'; FONT-SIZE: 10pt"&gt;&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;SPAN style="FONT-FAMILY: 'Tahoma','sans-serif'; FONT-SIZE: 10pt"&gt;The behavior difference between classic and integrated mode with respect to calling Redirect from Application_Error without calling Server.ClearError is just due to the different environments.&amp;nbsp; You might notice that with a default 3.5 install, if you remove the ASP.NET ScriptModule from the IIS &amp;lt;modules&amp;gt; section, you're suddenly able to call Redirect from Application_Error without calling Server.ClearError.&amp;nbsp; ScriptModule registers a PreSendRequestHeaders handler.&amp;nbsp; In integrated mode, the PreSendRequestHeaders event is implemented differently.&amp;nbsp; As a result, in integrated mode, the exception will be rendered if you try to redirect without clearing the error from Application_Error.&amp;nbsp; I’ve attached a sample that demonstrates the difference between the two pipelines.&amp;nbsp; This sample will demonstrate the difference regardless of whether or not ScriptModule is installed.&amp;nbsp; Just request default.aspx.&amp;nbsp; Then change the value of demonstratePipelineDifference in global.asax, and request default.aspx again.&amp;nbsp; Do this in both classic mode and integrated mode, and observe the behavior.&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;SPAN style="FONT-FAMILY: 'Tahoma','sans-serif'; FONT-SIZE: 10pt"&gt;&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;SPAN style="FONT-FAMILY: 'Tahoma','sans-serif'; FONT-SIZE: 10pt"&gt;Thanks,&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;SPAN style="FONT-FAMILY: 'Tahoma','sans-serif'; FONT-SIZE: 10pt"&gt;Thomas&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=9803956" width="1" height="1"&gt;</content><author><name>tmarq</name><uri>http://blogs.msdn.com/tmarq/ProfileUrlRedirect.ashx</uri></author></entry><entry><title>Obtaining more information from an HttpException</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/b/tmarq/archive/2009/03/10/obtaining-more-information-from-an-httpexception.aspx" /><id>http://blogs.msdn.com/b/tmarq/archive/2009/03/10/obtaining-more-information-from-an-httpexception.aspx</id><published>2009-03-10T23:44:00Z</published><updated>2009-03-10T23:44:00Z</updated><content type="html">&lt;P&gt;ASP.NET throws HttpException for numerous types of errors, making it difficult to determine exactly what went wrong programmatically. You can parse the error message, but the message can be localized,&amp;nbsp;and therefore&amp;nbsp;parsing&amp;nbsp;it can be&amp;nbsp;non-trivial.&amp;nbsp; There is, however, a way that you can identify a few specific types of errors.&lt;/P&gt;
&lt;P&gt;In v2.0 there is an internal&amp;nbsp;property on HttpException named WebEventCode&amp;nbsp;that returns an int.&amp;nbsp; This value contains additional information which you can use to determine the type of error.&amp;nbsp; Because it is internal you will have to use private reflection to get the value, but the property has been made public in v4.0.&amp;nbsp; Below I've listed the meaning of several of the possible values of this property:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;If the HttpException&amp;nbsp;is because the request timed out ("Request timed out."), the value will be&amp;nbsp;&amp;nbsp;System.Web.Management.&lt;STRONG&gt;WebEventCodes.RuntimeErrorRequestAbort&lt;/STRONG&gt;.&lt;/LI&gt;
&lt;LI&gt;If it's because the client posted too much data ("Maximum request length exceeded."), the value will be &lt;STRONG&gt;WebEventCodes.RuntimeErrorPostTooLarge&lt;/STRONG&gt;.&lt;/LI&gt;
&lt;LI&gt;If it's&amp;nbsp;caused by a ViewState error, the value will be &lt;STRONG&gt;WebEventCodes.RuntimeErrorViewStateFailure&lt;/STRONG&gt;.&lt;/LI&gt;&lt;/UL&gt;
&lt;P&gt;Again, this property has been made public in v4.0, but for v2.0, you'll have to use private reflection to access it.&lt;/P&gt;
&lt;P&gt;-Thomas&lt;/P&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=9469736" width="1" height="1"&gt;</content><author><name>tmarq</name><uri>http://blogs.msdn.com/tmarq/ProfileUrlRedirect.ashx</uri></author></entry><entry><title>Using IIS 7.0 Dynamic Compression with ASP.NET Output Cache </title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/b/tmarq/archive/2008/08/27/using-iis-7-0-dynamic-compression-with-asp-net-output-cache.aspx" /><id>http://blogs.msdn.com/b/tmarq/archive/2008/08/27/using-iis-7-0-dynamic-compression-with-asp-net-output-cache.aspx</id><published>2008-08-27T22:31:00Z</published><updated>2008-08-27T22:31:00Z</updated><content type="html">&lt;P&gt;This post&amp;nbsp;discusses an efficient way to compress content served by the ASP.NET output cache.&amp;nbsp;&amp;nbsp;In .NET Framework v2.0 SP1, included with Windows Server 2008, the ASP.NET Output Cache includes a new feature that allows cached responses to vary by the response's &lt;A class="" href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.11" mce_href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.11"&gt;Content-Encoding&lt;/A&gt; header.&amp;nbsp; And IIS 7.0 has efficient compression algorithms for gzip and deflate.&amp;nbsp; Put the two together, and you can efficiently compress responses once, and serve them from the cache many times.&lt;/P&gt;
&lt;P&gt;Here's how you can do this on Windows Server 2008:&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;1.&lt;/STRONG&gt;&amp;nbsp; If you haven't already, install the Dynamic Content Compression role for IIS 7.0 using the Server Manager.&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;2.&lt;/STRONG&gt;&amp;nbsp; Step 1 will add the &lt;STRONG&gt;&amp;lt;httpCompression&amp;gt;&lt;/STRONG&gt; configuration section to applicationHost.config.&amp;nbsp; Note that only gzip compression is supported by default.&amp;nbsp; However, deflate is also implemented in gzip.dll, so if you add the following to the &lt;STRONG&gt;&amp;lt;httpCompression&amp;gt;&lt;/STRONG&gt; configuration section, you'll also have deflate enabled:&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;STRONG&gt;&amp;lt;scheme name="deflate" dll="%Windir%\system32\inetsrv\gzip.dll" /&amp;gt;&lt;/STRONG&gt; &lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;3.&lt;/STRONG&gt; Create an application with an ASPX page like the one below.&amp;nbsp; This example page will cache at most 3 responses, one that is gzip compressed, one deflate compressed, and one that is a normal (uncompressed) response.&lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;&amp;lt;%@ Page language="C#" %&amp;gt;&lt;BR&gt;&amp;lt;%@ OutputCache duration="600" varyByParam="None" varyByContentEncoding=&lt;STRONG&gt;"gzip;deflate"&lt;/STRONG&gt; %&amp;gt;&lt;/P&gt;
&lt;P&gt;&amp;lt;script runat="server"&amp;gt;&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; void Page_Load() {&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Request.ServerVariables[&lt;STRONG&gt;"IIS_EnableDynamicCompression"&lt;/STRONG&gt;] = &lt;STRONG&gt;"1"&lt;/STRONG&gt;; &lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Response.Write(DateTime.Now);&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;BR&gt;&amp;lt;/script&amp;gt;&amp;nbsp;&lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;&lt;STRONG&gt;4.&lt;/STRONG&gt;&amp;nbsp; You must enable the IIS 7.0 "dynamic compression before cache" feature.&amp;nbsp; You can do this by adding the following to your application's web.config file.&amp;nbsp; &lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;&amp;lt;system.webServer&amp;gt;&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;urlCompression doDynamicCompression=&lt;STRONG&gt;"false"&lt;/STRONG&gt; dynamicCompressionBeforeCache=&lt;STRONG&gt;"true"&lt;/STRONG&gt; /&amp;gt;&lt;BR&gt;&amp;lt;/system.webServer&amp;gt;&lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P mce_keep="true"&gt;Note that in Step 4, doDynamicCompression=&lt;STRONG&gt;"false"&lt;/STRONG&gt;.&amp;nbsp; If you set this to &lt;STRONG&gt;"true"&lt;/STRONG&gt;, all responses will be compressed.&amp;nbsp; That is, pages that are not using the output cache, as well as pages that are using the output cache (but may not be using varyByContentEncoding) will be compressed.&amp;nbsp;&amp;nbsp;Our example sets doDynamicCompression to &lt;STRONG&gt;"false"&lt;/STRONG&gt; in configuration, but then uses the server variable &lt;STRONG&gt;"IIS_EnableDynamicCompression"&lt;/STRONG&gt; to enable it for a specific page.&amp;nbsp; Alternatively, you could use configuration and &amp;lt;location path=""&amp;gt; to limit the scope of compression.&lt;/P&gt;
&lt;P mce_keep="true"&gt;So how does this work?&amp;nbsp; When a request is sent to the server, it may or may not include an &lt;A class="" href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.3" mce_href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.3"&gt;Accept-Encoding&lt;/A&gt; header, indicating one or more acceptable encodings.&amp;nbsp; For example, Internet Explorer sends "Accept-Encoding: gzip, deflate".&amp;nbsp; When the ASP.NET Output Cache receives the request, if the page is using the VaryByContentEncoding feature,&amp;nbsp;ASP.NET will check if any of the encodings specified by VaryByContentEncoding match the request's Accept-Encoding header.&amp;nbsp; If there is a match, ASP.NET will see if that response is in the cache and serve it.&amp;nbsp; If it's not cached, ASP.NET will render the response and the IIS 7.0 DynamicCompressionModule, which runs during &lt;A class="" href="http://blogs.msdn.com/tmarq/archive/2007/08/30/iis-7-0-asp-net-pipelines-modules-handlers-and-preconditions.aspx" mce_href="http://blogs.msdn.com/tmarq/archive/2007/08/30/iis-7-0-asp-net-pipelines-modules-handlers-and-preconditions.aspx"&gt;RQ_RELEASE_REQUEST_STATE&lt;/A&gt;, will encode the response according to the request's Accept-Encoding header, and it will set the response's Content-Encoding header.&amp;nbsp; For the example request, the response header will be set to "Content-Encoding: gzip".&amp;nbsp; When the ASP.NET OutputCacheModule runs during &lt;A class="" href="http://blogs.msdn.com/tmarq/archive/2007/08/30/iis-7-0-asp-net-pipelines-modules-handlers-and-preconditions.aspx" mce_href="http://blogs.msdn.com/tmarq/archive/2007/08/30/iis-7-0-asp-net-pipelines-modules-handlers-and-preconditions.aspx"&gt;RQ_UPDATE_REQUEST_CACHE&lt;/A&gt;, it will check to see if the Content-Encoding header has been set, and&amp;nbsp;if specified in VaryByContentEncoding, it will cache the response according to that encoding.&amp;nbsp; If the Content-Encoding is not set, the response can be cached as the identity.&lt;/P&gt;
&lt;P&gt;This ASP.NET Output Cache feature can be used via the &amp;lt;%@ OutputCache varyByContentEncoding= %&amp;gt; page directive, programmatically via HttpCachePolicy.VaryByContentEncodings, or via configuration outputCacheProfiles&amp;gt;&amp;lt;add varyByContentEncoding=””&amp;gt;&amp;lt;/outputCacheProfiles&amp;gt;.&amp;nbsp; &lt;/P&gt;
&lt;P&gt;Here are a few of the implementation details:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;
&lt;DIV mce_keep="true"&gt;The value of the VaryByContentEncoding property is the name of an encoding, or a semi-colon separated list of encodings (e.g. "gzip", "gzip; deflate; compress").&amp;nbsp; &lt;/DIV&gt;&lt;/LI&gt;
&lt;LI&gt;
&lt;DIV mce_keep="true"&gt;The identity, or plain text, is a special case.&amp;nbsp; You can consider it to be implicitly included in the value of VaryByContentEncoding.&amp;nbsp; E.g., if you specify varyByContentEncoding="gzip", ASP.NET will cache at most two responses, gzip and the identity.&lt;/DIV&gt;&lt;/LI&gt;
&lt;LI&gt;
&lt;DIV mce_keep="true"&gt;A response&amp;nbsp;can be cached only if&amp;nbsp;the value of its&amp;nbsp;Content-Encoding header is&amp;nbsp;included in VaryByContentEncoding; except for the identity, which is a special case.&amp;nbsp; If the response does not have a Content-Encoding header, it can be cached as the identity.&lt;/DIV&gt;&lt;/LI&gt;
&lt;LI&gt;
&lt;DIV mce_keep="true"&gt;A response will be served from the cache if it&amp;nbsp;has an&amp;nbsp;acceptable encoding according to &lt;A class="" href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.3" mce_href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.3"&gt;RFC 2616, Section 14.3&lt;/A&gt;&amp;nbsp;(qvalues are supported).&amp;nbsp;However, even if the identity is available in the cache, if an acceptable encoding is found in VaryByContentEncoding, but that encoding has not yet been cached, ASP.NET will allow the page to render so that a response for that encoding may be cached.&lt;/DIV&gt;&lt;/LI&gt;
&lt;LI&gt;
&lt;DIV mce_keep="true"&gt;If the value of the Content-Encoding response header is not in the list, the response is not cacheable. If the Content-Encoding response header is in the list, the response is cacheable (assuming nothing else in its cache policy prevents it from being cached). &lt;/DIV&gt;&lt;/LI&gt;
&lt;LI&gt;
&lt;DIV mce_keep="true"&gt;If the Content-Encoding response header is not set, the response may be cached as the identity (again, assuming nothing else in its cache policy prevents it from being cached).&lt;/DIV&gt;&lt;/LI&gt;
&lt;LI&gt;
&lt;DIV mce_keep="true"&gt;If the request's Accept-Encoding doesn't match any cached responses, and is not listed in VaryByContentEncoding,&amp;nbsp;and the identity is available in the cache, then the response served will be the identity.&amp;nbsp;&lt;/DIV&gt;&lt;/LI&gt;&lt;/UL&gt;
&lt;P mce_keep="true"&gt;Regards,&lt;BR&gt;Thomas&lt;/P&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=8901188" width="1" height="1"&gt;</content><author><name>tmarq</name><uri>http://blogs.msdn.com/tmarq/ProfileUrlRedirect.ashx</uri></author></entry><entry><title>ASP.NET Cache can notify you before an entry is removed</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/b/tmarq/archive/2008/07/22/asp-net-cache-can-notify-you-before-an-entry-is-removed.aspx" /><id>http://blogs.msdn.com/b/tmarq/archive/2008/07/22/asp-net-cache-can-notify-you-before-an-entry-is-removed.aspx</id><published>2008-07-22T19:32:00Z</published><updated>2008-07-22T19:32:00Z</updated><content type="html">&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal"&gt;&lt;SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Verdana','sans-serif'"&gt;.NET Framework v2.0 SP2, which will be included with .NET Framework v3.5 SP1, is scheduled to release this summer.&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;This release contains a long awaited ASP.NET Cache feature, which will notify you when an entry becomes invalid and leave the stale entry in the cache while you generate a replacement in the background.&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;Therefore, requests are not blocked attempting to access the cache entry while it is being updated, and the update is done by a single thread.&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;Here's the specification:&lt;?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" /&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal"&gt;&lt;SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Verdana','sans-serif'"&gt;&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal"&gt;&lt;SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Verdana','sans-serif'"&gt;&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal"&gt;&lt;U&gt;&lt;SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Verdana','sans-serif'"&gt;A new Cache.Insert overload&lt;/SPAN&gt;&lt;/U&gt;&lt;SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Verdana','sans-serif'"&gt;:&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal"&gt;&lt;SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Verdana','sans-serif'"&gt;&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt 0.5in; LINE-HEIGHT: normal"&gt;&lt;SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Courier New'"&gt;void Cache.Insert( String key,&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt 0.5in; LINE-HEIGHT: normal"&gt;&lt;SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Courier New'"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/SPAN&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/SPAN&gt;Object expensiveObject,&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt 0.5in; LINE-HEIGHT: normal"&gt;&lt;SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Courier New'"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/SPAN&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/SPAN&gt;CacheDependency dependencies,&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt 0.5in; LINE-HEIGHT: normal"&gt;&lt;SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Courier New'"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/SPAN&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/SPAN&gt;DateTime absoluteExpiration,&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt 0.5in; LINE-HEIGHT: normal"&gt;&lt;SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Courier New'"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/SPAN&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/SPAN&gt;TimeSpan slidingExpiration,&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt 0.5in; LINE-HEIGHT: normal"&gt;&lt;SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Courier New'"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/SPAN&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/SPAN&gt;CacheItemUpdateCallback onUpdateCallback &lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt 0.5in; LINE-HEIGHT: normal"&gt;&lt;SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Courier New'"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;);&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Verdana','sans-serif'"&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt 0.5in; LINE-HEIGHT: normal"&gt;&lt;SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Verdana','sans-serif'"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt 0.5in; LINE-HEIGHT: normal"&gt;&lt;SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Verdana','sans-serif'"&gt;This method inserts an object into the cache, with optional dependencies and expiration policy, and a callback that is invoked before the entry is removed.&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;Objects inserted into the cache with this method should be expensive to generate.&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;You should not use this method for objects that are inexpensive to generate, because there is overhead in the implementation.&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;When the callback is invoked, the application developer should generate a new object, with new dependencies and expiration policy, and return it via the callback’s out parameters.&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;The stale cache entry will be accessible while the callback is executing and the replacement object is generated.&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt 0.5in; LINE-HEIGHT: normal"&gt;&lt;SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Verdana','sans-serif'"&gt;&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt 0.5in; LINE-HEIGHT: normal"&gt;&lt;SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Verdana','sans-serif'"&gt;Cache entries inserted by this method are not subject to eviction by the cache memory manager.&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;These objects only become invalid when they expire or a dependency changes, and therefore these objects &lt;B style="mso-bidi-font-weight: normal"&gt;MUST&lt;/B&gt; have some form of invalidation (i.e., it is not possible to insert the entry with a null dependency, NoAbsoluteExpiration, and NoSlidingExpriation).&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal"&gt;&lt;SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Verdana','sans-serif'"&gt;&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal"&gt;&lt;SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Verdana','sans-serif'"&gt;&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal"&gt;&lt;U&gt;&lt;SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Verdana','sans-serif'"&gt;A new update callback&lt;/SPAN&gt;&lt;/U&gt;&lt;SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Verdana','sans-serif'"&gt;:&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal"&gt;&lt;SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Verdana','sans-serif'"&gt;&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt 0.5in; LINE-HEIGHT: normal"&gt;&lt;SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Courier New'"&gt;void CacheItemUpdateCallback( String key,&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt 0.5in; LINE-HEIGHT: normal"&gt;&lt;SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Courier New'"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;CacheItemUpdateReason reason,&lt;BR&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/SPAN&gt;out Object expensiveObject,&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt 0.5in; LINE-HEIGHT: normal"&gt;&lt;SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Courier New'"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;out CacheDependency dependencies,&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt 0.5in; LINE-HEIGHT: normal"&gt;&lt;SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Courier New'"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/SPAN&gt;out DateTime absoluteExpiration,&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt 0.5in; LINE-HEIGHT: normal"&gt;&lt;SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Courier New'"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;out TimeSpan slidingExpiration &lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt 0.5in; LINE-HEIGHT: normal"&gt;&lt;SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Courier New'"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;);&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Verdana','sans-serif'"&gt; &lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt 0.5in; LINE-HEIGHT: normal"&gt;&lt;SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Verdana','sans-serif'"&gt;&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt 0.5in; LINE-HEIGHT: normal"&gt;&lt;SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Verdana','sans-serif'"&gt;When the cache entry becomes invalid, ASP.NET invokes this callback, allowing the application developer to generate a replacement cache entry.&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;The developer is passed only two pieces of information:&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;the key for the cache entry, and an enum value indicating why the entry is invalid.&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;The rest of the arguments to the callback are output-only parameters, allowing references to the newly generated object, dependencies, and expiration policy to be passed back and subsequently inserted into the cache by ASP.NET.&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt 0.5in; LINE-HEIGHT: normal"&gt;&lt;SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Verdana','sans-serif'"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt 0.5in; LINE-HEIGHT: normal"&gt;&lt;SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Verdana','sans-serif'"&gt;Do not modify the existing cache entry.&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;The stale cache entry is still alive and potentially being used by other threads.&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;It is not included in the callback arguments in order to minimize the likelihood of a developer accidentally updating portions of the cache entry.&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;Although this pitfall exists today in the cache API (e.g. a cache entry could be modified in a non-thread-safe manner by an ASP.NET developer), we think leaving the cached value out of the callback arguments will reduce the likelihood of a developer stumbling into this. &lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt 0.5in; LINE-HEIGHT: normal"&gt;&lt;SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Verdana','sans-serif'"&gt;&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt 0.5in; LINE-HEIGHT: normal"&gt;&lt;SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Verdana','sans-serif'"&gt;To remove the cache entry, simply set the argument &lt;I style="mso-bidi-font-style: normal"&gt;expensiveObject&lt;/I&gt; to null.&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;Otherwise, set it to the newly generated replacement object.&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;The developer &lt;B style="mso-bidi-font-weight: normal"&gt;MUST&lt;/B&gt; also specify expiration and/or dependency information using the other out parameters.&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;As stated above, the cache entry is not subject to eviction by the cache memory manager, and therefore must have some form of invalidation.&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;It is an error to pass back a non-null value for &lt;I style="mso-bidi-font-style: normal"&gt;expensiveObject&lt;/I&gt; while setting &lt;I style="mso-bidi-font-style: normal"&gt;dependencies&lt;/I&gt; to null, &lt;I style="mso-bidi-font-style: normal"&gt;absoluteExpiration&lt;/I&gt; to NoAbsolteExpiration, and &lt;I style="mso-bidi-font-style: normal"&gt;slidingExpriation&lt;/I&gt; to NoSlidingExpiration.&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;The cache entry will be removed if this occurs.&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt 0.5in; LINE-HEIGHT: normal"&gt;&lt;SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Verdana','sans-serif'"&gt;&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt 0.5in; LINE-HEIGHT: normal"&gt;&lt;SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Verdana','sans-serif'"&gt;If the developer’s callback throws an exception, ASP.NET will suppress the exception and remove the cache entry.&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;The exception needs to be suppressed because cache item updates occur in the background where there is no call path for routing the exception back to user code.&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;The cache entry&amp;nbsp;is removed because without a new cache value, ASP.NET has no way of knowing what the correct behavior is for updating the cached item.&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt 0.5in; LINE-HEIGHT: normal"&gt;&lt;SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Verdana','sans-serif'"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt 0.5in; LINE-HEIGHT: normal"&gt;&lt;SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Verdana','sans-serif'"&gt;The update callback will not be called if the cached item was explicitly removed via a call to Cache.Remove, or a call to Cache.Insert (Insert removes a pre-existing entry).&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;These operations indicate the developer’s intent to remove the item, so the update callback will not be triggered. &lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal"&gt;&lt;SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Verdana','sans-serif'"&gt;&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal"&gt;&lt;SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Verdana','sans-serif'"&gt;&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal"&gt;&lt;U&gt;&lt;SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Verdana','sans-serif'"&gt;A new update reason enumeration&lt;/SPAN&gt;&lt;/U&gt;&lt;SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Verdana','sans-serif'"&gt;:&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal"&gt;&lt;SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Verdana','sans-serif'"&gt;&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt 0.5in; LINE-HEIGHT: normal"&gt;&lt;SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Courier New'"&gt;public enum CacheItemUpdateReason {&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt 0.5in; LINE-HEIGHT: normal"&gt;&lt;SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Courier New'"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;// The item was removed from the cache because either &lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt 0.5in; LINE-HEIGHT: normal"&gt;&lt;SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Courier New'"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;// the absolute or sliding expiration interval expired.&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt 0.5in; LINE-HEIGHT: normal"&gt;&lt;SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Courier New'"&gt;&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt 0.5in; LINE-HEIGHT: normal"&gt;&lt;SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Courier New'"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;Expired = 1,&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt 0.5in; LINE-HEIGHT: normal"&gt;&lt;SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Courier New'"&gt;&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt 0.5in; LINE-HEIGHT: normal"&gt;&lt;SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Courier New'"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;// The item was removed from the cache because an &lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt 0.5in; LINE-HEIGHT: normal"&gt;&lt;SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Courier New'"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;// associated cache dependency was changed.&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt 0.5in; LINE-HEIGHT: normal"&gt;&lt;SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Courier New'"&gt;&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt 0.5in; LINE-HEIGHT: normal"&gt;&lt;SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Courier New'"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;DependencyChanged = 2&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt 0.5in; LINE-HEIGHT: normal"&gt;&lt;SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Courier New'"&gt;}&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt 0.5in; LINE-HEIGHT: normal"&gt;&lt;SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Verdana','sans-serif'"&gt;&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt 0.5in; LINE-HEIGHT: normal"&gt;&lt;SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Verdana','sans-serif'"&gt;Unlike the CacheItemRemovedReason enumeration, the update reason enumeration does not include values for Removed (see explanation above) or Underused (as mentioned above, the cache entry is not subject to eviction by the cache memory manager).&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal"&gt;&lt;SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Verdana','sans-serif'"&gt;&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal"&gt;&lt;SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Verdana','sans-serif'"&gt;Regards,&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal"&gt;&lt;SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Verdana','sans-serif'"&gt;Thomas&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=8764523" width="1" height="1"&gt;</content><author><name>tmarq</name><uri>http://blogs.msdn.com/tmarq/ProfileUrlRedirect.ashx</uri></author></entry><entry><title>If you allocate from the Large Object Heap each time an ASPX page is requested, your performance will be dismal.</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/b/tmarq/archive/2008/04/29/if-you-allocate-from-the-large-object-heap-each-time-an-aspx-page-is-requested-your-performance-will-be-dismal.aspx" /><id>http://blogs.msdn.com/b/tmarq/archive/2008/04/29/if-you-allocate-from-the-large-object-heap-each-time-an-aspx-page-is-requested-your-performance-will-be-dismal.aspx</id><published>2008-04-29T20:12:00Z</published><updated>2008-04-29T20:12:00Z</updated><content type="html">&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;FONT size=3 face="Times New Roman"&gt;I've been giving this advice for years internally, and the problem pops up way too often.&amp;nbsp; I'm sure it will be addressed eventually, in a future version of the CLR, but for now, every released version of the CLR has this problem.&amp;nbsp;&amp;nbsp;If you allocate objects larger than 85000 bytes, they will be allocated in the Large Object Heap (LOH).&amp;nbsp; The LOH is not compacted and collection only occurs after a full collection (so gen 2 must be collected in order to&amp;nbsp;collect an object in the LOH).&amp;nbsp; Short-lived and frequent LOH allocations will kill the performance of your application.&amp;nbsp;&amp;nbsp;&lt;/FONT&gt;&lt;FONT size=3 face="Times New Roman"&gt;As an example, if you allocate from the LOH each time an ASPX page is requested, your performance will be dismal.&lt;/FONT&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;FONT size=3 face="Times New Roman"&gt;&lt;/FONT&gt;&amp;nbsp;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;FONT size=3 face="Times New Roman"&gt;There are a few ways to determine if you're allocating from the Large Object Heap (LOH).&amp;nbsp; The easiest is via the performance counter ".NET CLR Memory\Large Object Heap size".&amp;nbsp; You can more or less determine if your allocating from the LOH on a per-request basis by checking to see if the value of this performance counter fluctuates while you make requests.&lt;/FONT&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;FONT size=3 face="Times New Roman"&gt;&lt;/FONT&gt;&amp;nbsp;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;FONT size=3 face="Times New Roman"&gt;You can also deterimine if you're allocating from the LOH by profiling a request with the CLR Profiler, which is discussed in an earlier blog post.&amp;nbsp; The Histogram view shows you a histogram of the allocations broken down by size, and you can quickly check to see if there's anything larger than 85000 bytes.&amp;nbsp; If you right click on columns in the histogram, you can select "Show who Allocated" to see a stack trace for the allocation(s).&lt;/FONT&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;FONT size=3 face="Times New Roman"&gt;&lt;/FONT&gt;&amp;nbsp;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;FONT size=3 face="Times New Roman"&gt;Another way to determine&amp;nbsp;&lt;/FONT&gt;&lt;FONT size=3 face="Times New Roman"&gt;if you’re allocating from the Large Object Heap (LOH) is by setting break points.&amp;nbsp; For example, you can use this break point on v2.0 on a multi-proc box, where the server GC is enabled by default: &lt;/FONT&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" /&gt;&lt;o:p&gt;&lt;FONT size=3 face="Times New Roman"&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P style="TEXT-INDENT: 0.5in; MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;FONT size=3 face="Times New Roman"&gt;bp mscorwks!SVR::gc_heap::allocate_large_object ".echo size=; ? poi(@esp+4); k12; gc"&lt;/FONT&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;o:p&gt;&lt;FONT size=3 face="Times New Roman"&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;FONT size=3 face="Times New Roman"&gt;If you want to get fancy, the following will attach ntsd to w3wp.exe, disable break on first chance exceptions, and trace the calls to allocate_large_object to %SYSTEMDRIVE%\NTSD.log&lt;/FONT&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;o:p&gt;&lt;FONT size=3 face="Times New Roman"&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P style="TEXT-INDENT: 0.5in; MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;FONT size=3 face="Times New Roman"&gt;ntsd.exe -y %WINDIR%\symbols;srv*%WINDIR%\Symbols*http://msdl.microsoft.com/download/symbols -xd * -c "bp mscorwks!SVR::gc_heap::allocate_large_object \".echo size=; ? poi(@esp+4); k12; gc\"; g" -G -logo %SYSTEMDRIVE%\NTSD.log -pn w3wp.exe&lt;/FONT&gt;&lt;/P&gt;
&lt;P style="TEXT-INDENT: 0.5in; MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;FONT size=3 face="Times New Roman"&gt;&lt;/FONT&gt;&amp;nbsp;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;FONT size=3 face="Times New Roman"&gt;Aside from short-lived LOH allocations, the most common cause of memory related issues in ASP.NET apps is pinning managed memory during long async I/O operations.&amp;nbsp; ASP.NET itself does not do this, but perhaps&amp;nbsp;your application does?&lt;/FONT&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;o:p&gt;&lt;FONT size=3 face="Times New Roman"&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;FONT size=3 face="Times New Roman"&gt;Thanks,&lt;/FONT&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;FONT size=3 face="Times New Roman"&gt;Thomas&lt;/FONT&gt;&lt;/P&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=8439140" width="1" height="1"&gt;</content><author><name>tmarq</name><uri>http://blogs.msdn.com/tmarq/ProfileUrlRedirect.ashx</uri></author></entry><entry><title>ASP.NET File Change Notifications, exactly which files and directories are monitored?</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/b/tmarq/archive/2007/11/02/asp-net-file-change-notifications-exactly-which-files-and-directories-are-monitored.aspx" /><id>http://blogs.msdn.com/b/tmarq/archive/2007/11/02/asp-net-file-change-notifications-exactly-which-files-and-directories-are-monitored.aspx</id><published>2007-11-02T06:15:00Z</published><updated>2007-11-02T06:15:00Z</updated><content type="html">&lt;p&gt;Perhaps one of the most loved and hated features of ASP.NET is the ability to detect file changes and automatically recompile or reconfigure the application.&amp;nbsp; In development scenarios it&amp;rsquo;s very nice because you can access the application in a browser at the same time you&amp;rsquo;re modifying ASPX, ASAX, DLL, CONFIG, and other application files, and you don&amp;rsquo;t have to stop the web server process in order to update DLLs or configuration.&amp;nbsp; The changes are picked up immediately, but in order to make this possible we have to recycle the AppDomain.&amp;nbsp; In deployment scenarios, this feature can be a problem.&amp;nbsp; There are many files and folders (e.g. global.asax, web.config, BIN, App_Code, etc) that trigger an AppDomain unload when changed.&amp;nbsp; A subsequent request to the application will cause the AppDomain to be loaded again and the new files will be compiled, if necessary.&amp;nbsp; Restarting the application is expensive due to the disk access and recompilation of files.&amp;nbsp; Many applications also have expensive initialization steps that occur when the AppDomain is loaded, such as accessing SQL to populate local stores with frequently accessed data.&amp;nbsp; So what exactly does ASP.NET monitor and how?&lt;/p&gt;
&lt;p&gt;ASP.NET uses the Win32 function ReadDirectoryChangesW to monitor directories and files.&amp;nbsp; Given a directory handle, this function will let you know lots of information about changes to the directory or the files and directories contained within it.&amp;nbsp; By passing different flags, you tell it exactly what information you&amp;rsquo;re interested in and it informs you when there are changes.&amp;nbsp; ASP.NET monitors directories with the following flags (they are fairly self-explanatory, but you can refer to ReadDirectoryChangesW documentation for details):&lt;/p&gt;
&lt;p&gt;FILE_NOTIFY_CHANGE_FILE_NAME&lt;br /&gt;FILE_NOTIFY_CHANGE_DIR_NAME&lt;br /&gt;FILE_NOTIFY_CHANGE_CREATION&lt;br /&gt;FILE_NOTIFY_CHANGE_SIZE&lt;br /&gt;FILE_NOTIFY_CHANGE_LAST_WRITE&lt;br /&gt;FILE_NOTIFY_CHANGE_SECURITY&lt;/p&gt;
&lt;p&gt;The above flags are the ones that ASP.NET uses, but not all of them are used for each directory handle that is monitored.&amp;nbsp; So which directories are monitored?&amp;nbsp; By default in ASP.NET 2.0, there are six directory monitors for each application path, and two additional directory monitors for each virtual subdirectory path.&amp;nbsp; This is best explained with an example.&amp;nbsp; Suppose that we have an application with a virtual path of "/vpath" and a physical path of "c:\ppath".&amp;nbsp; If you make a request to a file located at "/vpath/f.aspx", the following directory monitors will be created:&amp;nbsp;&lt;/p&gt;
&lt;p&gt;#1) The application physical root path and all of its subdirectories are&amp;nbsp;monitored for subdirectory name changes&amp;nbsp;or deletions.&amp;nbsp; To be more precise, if FILE_NOTIFY_INFORMATION.Action is FILE_ACTION_RENAMED_OLD_NAME or FILE_ACTION_REMOVED, the AppDomain is unloaded.&amp;nbsp; This means that if you rename or delete a subdirectory beneath the application physical root, the application will be restarted.&amp;nbsp; This is discussed in &lt;a href="http://blogs.msdn.com/toddca/archive/2005/12/01/499144.aspx" title="Todd Carter's blog."&gt;Todd Carter's blog post&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;ReadDirectoryChangesW is called with these arguments:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Directory= "c:\ppath"&lt;br /&gt;WatchSubtree=&amp;nbsp;True&lt;br /&gt;NotifyFilter=&amp;nbsp;FILE_NOTIFY_CHANGE_DIR_NAME&lt;/p&gt;
&lt;p&gt;The&amp;nbsp;stack trace for the call is:&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Web.DirMonCompletion..ctor(DirectoryMonitor dirMon, String dir, Boolean watchSubtree, UInt32 notifyFilter)&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Web.DirectoryMonitor.StartMonitoring()&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Web.DirectoryMonitor.StartMonitoringFile(String file, FileChangeEventHandler callback, String alias)&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Web.FileChangesMonitor.StartMonitoringDirectoryRenamesAndBinDirectory(String dir, FileChangeEventHandler callback)&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Web.HttpRuntime.HostingInit(HostingEnvironmentFlags hostingFlags)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;#2)&amp;nbsp; The "bin", "App_Code", "App_WebReferences", "App_GlobalResources", and "App_Browsers"&amp;nbsp;subdirectories of the application root folder are monitored&amp;nbsp;for creation, deletion, renaming,&amp;nbsp;ACL changes, changes to the last-write time, and changes to the size.&amp;nbsp; If any of these things change, the&amp;nbsp;AppDomain is unloaded.&amp;nbsp; Note that one directory monitor is created to monitor all five of these subfolders when they do not exist so that ASP.NET can catch their creation.&amp;nbsp; If they do exist, then a unique directory monitor is created for that subdirectory.&amp;nbsp; So if "bin" exists and the other four do not exist, you will have a total of two directory monitors, one for "bin" and one that monitors for the creation of the other four.&amp;nbsp; Or if "bin" and "App_Code" exist but the others do not, you will have three directory monitors, one for "bin", one for "App_Code", and one that monitors for the creation of the other three.&lt;/p&gt;
&lt;p&gt;ReadDirectoryChangesW is called with these arguments:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Directory= "c:\ppath"&lt;br /&gt;WatchSubtree=&amp;nbsp;False&lt;br /&gt;NotifyFilter=&amp;nbsp;FILE_NOTIFY_CHANGE_FILE_NAME &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; | FILE_NOTIFY_CHANGE_DIR_NAME&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;| FILE_NOTIFY_CHANGE_CREATION&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;| FILE_NOTIFY_CHANGE_SIZE&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;| FILE_NOTIFY_CHANGE_LAST_WRITE&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;| FILE_NOTIFY_CHANGE_SECURITY&lt;/p&gt;
&lt;p&gt;The&amp;nbsp;stack trace for the call is:&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Web.DirMonCompletion..ctor(DirectoryMonitor dirMon, String dir, Boolean watchSubtree, UInt32 notifyFilter)&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Web.DirectoryMonitor.StartMonitoring()&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Web.DirectoryMonitor.StartMonitoringFile(String file, FileChangeEventHandler callback, String alias)&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Web.FileChangesMonitor.ListenToSubdirectoryChanges(String dirRoot, String dirToListenTo)&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Web.FileChangesMonitor.StartMonitoringDirectoryRenamesAndBinDirectory(String dir, FileChangeEventHandler callback)&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Web.HttpRuntime.HostingInit(HostingEnvironmentFlags hostingFlags)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;#3) The machine.config and&amp;nbsp;root web.config file are monitored for changes.&amp;nbsp; A change to either of these files will unload the AppDomain.&lt;/p&gt;
&lt;p&gt;ReadDirectoryChangesW is called with these arguments:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Directory= "%WINDIR%\Microsoft.NET\Framework\v2.0.50727\Config"&lt;br /&gt;WatchSubtree=&amp;nbsp;FALSE&lt;br /&gt;NotifyFilter=&amp;nbsp;FILE_NOTIFY_CHANGE_FILE_NAME &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; | FILE_NOTIFY_CHANGE_DIR_NAME&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;| FILE_NOTIFY_CHANGE_CREATION&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;| FILE_NOTIFY_CHANGE_SIZE&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;| FILE_NOTIFY_CHANGE_LAST_WRITE&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;| FILE_NOTIFY_CHANGE_SECURITY&lt;/p&gt;
&lt;p&gt;The&amp;nbsp;stack trace for the call is:&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Web.DirMonCompletion..ctor(DirectoryMonitor dirMon, String dir, Boolean watchSubtree, UInt32 notifyFilter)&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Web.DirectoryMonitor.StartMonitoring()&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Web.DirectoryMonitor.StartMonitoringFile(String file, FileChangeEventHandler callback, String alias)&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Web.FileChangesMonitor.StartMonitoringFile(String alias, FileChangeEventHandler callback)&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Web.Configuration.WebConfigurationHost.StartMonitoringStreamForChanges(String streamName, StreamChangeCallback callback)&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Configuration.BaseConfigurationRecord.MonitorStream(String configKey, String configSource, String streamname)&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Configuration.BaseConfigurationRecord.InitConfigFromFile()&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Configuration.BaseConfigurationRecord.Init(IInternalConfigRoot configRoot, BaseConfigurationRecord parent, String configPath, String locationSubPath)&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Configuration.RuntimeConfigurationRecord.Create(InternalConfigRoot configRoot, IInternalConfigRecord parent, String configPath)&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Configuration.Internal.InternalConfigRoot.GetConfigRecord(String configPath)&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Configuration.Internal.InternalConfigRoot.GetUniqueConfigRecord(String configPath)&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Web.Configuration.HttpConfigurationSystem.GetUniqueConfigRecord(String configPath)&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Web.CachedPathData.Init(CachedPathData parentData)&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Web.CachedPathData.GetConfigPathData(String configPath)&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Web.CachedPathData.GetConfigPathData(String configPath)&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Web.CachedPathData.GetConfigPathData(String configPath)&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Web.CachedPathData.GetConfigPathData(String configPath)&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Web.Configuration.RuntimeConfig.GetAppLKGConfig()&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Web.HttpRuntime.GetInitConfigSections&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Web.HttpRuntime.HostingInit(HostingEnvironmentFlags hostingFlags)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;#4)&amp;nbsp; The web.config of parent applications are monitored.&amp;nbsp; In this example, the root application "/" is the only parent.&amp;nbsp; A change to&amp;nbsp;this file will unload the AppDomain.&lt;/p&gt;
&lt;p&gt;ReadDirectoryChangesW is called with these arguments:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Directory= "c:\inetpub\wwwroot"&lt;br /&gt;WatchSubtree=&amp;nbsp;False&lt;br /&gt;NotifyFilter=&amp;nbsp;FILE_NOTIFY_CHANGE_FILE_NAME &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; | FILE_NOTIFY_CHANGE_DIR_NAME&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;| FILE_NOTIFY_CHANGE_CREATION&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;| FILE_NOTIFY_CHANGE_SIZE&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;| FILE_NOTIFY_CHANGE_LAST_WRITE&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;| FILE_NOTIFY_CHANGE_SECURITY&lt;/p&gt;
&lt;p&gt;The&amp;nbsp;stack trace for the call is:&lt;br /&gt;&amp;nbsp;&amp;nbsp; &lt;/p&gt;
&lt;p&gt;&amp;nbsp;&amp;nbsp; at System.Web.DirMonCompletion..ctor(DirectoryMonitor dirMon, String dir, Boolean watchSubtree, UInt32 notifyFilter)&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Web.DirectoryMonitor.StartMonitoring()&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Web.DirectoryMonitor.StartMonitoringFile(String file, FileChangeEventHandler callback, String alias)&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Web.FileChangesMonitor.StartMonitoringFile(String alias, FileChangeEventHandler callback)&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Web.Configuration.WebConfigurationHost.StartMonitoringStreamForChanges(String streamName, StreamChangeCallback callback)&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Configuration.BaseConfigurationRecord.MonitorStream(String configKey, String configSource, String streamname)&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Configuration.BaseConfigurationRecord.InitConfigFromFile()&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Configuration.BaseConfigurationRecord.Init(IInternalConfigRoot configRoot, BaseConfigurationRecord parent, String configPath, String locationSubPath)&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Configuration.RuntimeConfigurationRecord.Create(InternalConfigRoot configRoot, IInternalConfigRecord parent, String configPath)&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Configuration.Internal.InternalConfigRoot.GetConfigRecord(String configPath)&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Configuration.Internal.InternalConfigRoot.GetUniqueConfigRecord(String configPath)&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Web.Configuration.HttpConfigurationSystem.GetUniqueConfigRecord(String configPath)&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Web.CachedPathData.Init(CachedPathData parentData)&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Web.CachedPathData.GetConfigPathData(String configPath)&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Web.CachedPathData.GetConfigPathData(String configPath)&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Web.Configuration.RuntimeConfig.GetAppLKGConfig()&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Web.HttpRuntime.GetInitConfigSections&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Web.HttpRuntime.HostingInit(HostingEnvironmentFlags hostingFlags)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;#5)&amp;nbsp;&amp;nbsp; The web.config&amp;nbsp;in the&amp;nbsp;application root is monitored.&amp;nbsp;&amp;nbsp;A change to&amp;nbsp;this file will unload the AppDomain.&lt;/p&gt;
&lt;p&gt;ReadDirectoryChangesW is called with these arguments:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Directory= "c:\ppath"&lt;br /&gt;WatchSubtree=&amp;nbsp;False&lt;br /&gt;NotifyFilter=&amp;nbsp;FILE_NOTIFY_CHANGE_FILE_NAME &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; | FILE_NOTIFY_CHANGE_DIR_NAME&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;| FILE_NOTIFY_CHANGE_CREATION&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;| FILE_NOTIFY_CHANGE_SIZE&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;| FILE_NOTIFY_CHANGE_LAST_WRITE&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;| FILE_NOTIFY_CHANGE_SECURITY&lt;/p&gt;
&lt;p&gt;The&amp;nbsp;stack trace for the call is:&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Web.DirMonCompletion..ctor(DirectoryMonitor dirMon, String dir, Boolean watchSubtree, UInt32 notifyFilter)&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Web.DirectoryMonitor.StartMonitoring()&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Web.DirectoryMonitor.StartMonitoringFile(String file, FileChangeEventHandler callback, String alias)&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Web.FileChangesMonitor.StartMonitoringFile(String alias, FileChangeEventHandler callback)&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Web.Configuration.WebConfigurationHost.StartMonitoringStreamForChanges(String streamName, StreamChangeCallback callback)&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Configuration.BaseConfigurationRecord.MonitorStream(String configKey, String configSource, String streamname)&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Configuration.BaseConfigurationRecord.InitConfigFromFile()&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Configuration.BaseConfigurationRecord.Init(IInternalConfigRoot configRoot, BaseConfigurationRecord parent, String configPath, String locationSubPath)&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Configuration.RuntimeConfigurationRecord.Create(InternalConfigRoot configRoot, IInternalConfigRecord parent, String configPath)&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Configuration.Internal.InternalConfigRoot.GetConfigRecord(String configPath)&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Configuration.Internal.InternalConfigRoot.GetUniqueConfigRecord(String configPath)&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Web.Configuration.HttpConfigurationSystem.GetUniqueConfigRecord(String configPath)&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Web.CachedPathData.Init(CachedPathData parentData)&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Web.CachedPathData.GetConfigPathData(String configPath)&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Web.Configuration.RuntimeConfig.GetAppLKGConfig()&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Web.HttpRuntime.GetInitConfigSections&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Web.HttpRuntime.HostingInit(HostingEnvironmentFlags hostingFlags)&amp;nbsp;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;#6)&amp;nbsp; The hash.web file located in the "hash" subdirectory of the Temporary ASP.NET Files&amp;nbsp;folder is monitored.&amp;nbsp; This monitor was added to support development scenarios, in which both the ClientBuildManager and the runtime BuildManager are updating the application files.&amp;nbsp; Basically, since there are two build managers, there was a need to keep them in sync, and the hash.web file is used for that purpose.&lt;/p&gt;
&lt;p&gt;ReadDirectoryChangesW is called with these arguments:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Directory= "%WINDIR%\Microsoft.NET\Framework\v2.0.50727\Temporary ASP.NET Files\repro\19a9eafa\c159e372\hash"&lt;br /&gt;WatchSubtree=&amp;nbsp;False&lt;br /&gt;NotifyFilter=&amp;nbsp;FILE_NOTIFY_CHANGE_FILE_NAME &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; | FILE_NOTIFY_CHANGE_DIR_NAME&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;| FILE_NOTIFY_CHANGE_CREATION&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;| FILE_NOTIFY_CHANGE_SIZE&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;| FILE_NOTIFY_CHANGE_LAST_WRITE&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;| FILE_NOTIFY_CHANGE_SECURITY&lt;/p&gt;
&lt;p&gt;The&amp;nbsp;stack trace for the call is:&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Web.DirMonCompletion..ctor(DirectoryMonitor dirMon, String dir, Boolean watchSubtree, UInt32 notifyFilter)&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Web.DirectoryMonitor.StartMonitoring()&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Web.DirectoryMonitor.StartMonitoringFile(String file, FileChangeEventHandler callback, String alias)&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Web.FileChangesMonitor.StartMonitoringFile(String alias, FileChangeEventHandler callback)&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Web.Compilation.BuildManager.CheckTopLevelFilesUpToDate2(StandardDiskBuildResultCache diskCache)&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Web.Compilation.BuildManager.CheckTopLevelFilesUpToDate(StandardDiskBuildResultCache diskCache)&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Web.Compilation.BuildManager.RegularAppRuntimeModeInitialize()&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Web.Compilation.BuildManager.Initialize()&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Web.Compilation.BuildManager.InitializeBuildManager()&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Web.HttpRuntime.HostingInit(HostingEnvironmentFlags hostingFlags)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;As mentioned earlier, the six directory monitors mentioned above are created when a request is made to "/vpath/f.aspx".&amp;nbsp; If there is a subdirectory and a request is made to "/vpath/subdir/f.aspx", then two additional directory monitors will be created:&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;#7)&amp;nbsp;&amp;nbsp;The web.config&amp;nbsp;in any subdirectory of the application is monitored.&amp;nbsp;&amp;nbsp;A change to&amp;nbsp;this file will unload the AppDomain.&lt;/p&gt;
&lt;p&gt;ReadDirectoryChangesW is called with these arguments:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Directory= "c:\ppath\subdir"&lt;br /&gt;WatchSubtree=&amp;nbsp;False&lt;br /&gt;NotifyFilter=&amp;nbsp;FILE_NOTIFY_CHANGE_FILE_NAME &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; | FILE_NOTIFY_CHANGE_DIR_NAME&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;| FILE_NOTIFY_CHANGE_CREATION&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;| FILE_NOTIFY_CHANGE_SIZE&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;| FILE_NOTIFY_CHANGE_LAST_WRITE&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;| FILE_NOTIFY_CHANGE_SECURITY&lt;/p&gt;
&lt;p&gt;The&amp;nbsp;stack trace for the call is:&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Web.DirMonCompletion..ctor(DirectoryMonitor dirMon, String dir, Boolean watchSubtree, UInt32 notifyFilter)&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Web.DirectoryMonitor.StartMonitoring()&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Web.DirectoryMonitor.StartMonitoringFile(String file, FileChangeEventHandler callback, String alias)&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Web.FileChangesMonitor.StartMonitoringFile(String alias, FileChangeEventHandler callback)&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Web.Configuration.WebConfigurationHost.StartMonitoringStreamForChanges(String streamName, StreamChangeCallback callback)&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Configuration.BaseConfigurationRecord.MonitorStream(String configKey, String configSource, String streamname)&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Configuration.BaseConfigurationRecord.InitConfigFromFile()&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Configuration.BaseConfigurationRecord.Init(IInternalConfigRoot configRoot, BaseConfigurationRecord parent, String configPath, String locationSubPath)&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Configuration.RuntimeConfigurationRecord.Create(InternalConfigRoot configRoot, IInternalConfigRecord parent, String configPath)&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Configuration.Internal.InternalConfigRoot.GetConfigRecord(String configPath)&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Configuration.Internal.InternalConfigRoot.GetUniqueConfigRecord(String configPath)&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Web.Configuration.HttpConfigurationSystem.GetUniqueConfigRecord(String configPath)&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Web.CachedPathData.Init(CachedPathData parentData)&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Web.CachedPathData.GetConfigPathData(String configPath)&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Web.CachedPathData.GetConfigPathData(String configPath)&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Web.CachedPathData.GetVirtualPathData(VirtualPath virtualPath, Boolean permitPathsOutsideApp)&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Web.HttpContext.GetFilePathData()&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Web.HttpContext.GetConfigurationPathData()&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Web.ClientImpersonationContext.Start(HttpContext context, Boolean throwOnError)&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Web.HttpApplication.OnThreadEnter()&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;#8)&amp;nbsp; The "App_LocalResources" subdirectory of each virtual subdirectory&amp;nbsp;is monitored&amp;nbsp;for creation, deletion, renaming,&amp;nbsp;ACL changes, changes to the last-write time, and changes to the size.&amp;nbsp; If any of these things change, the&amp;nbsp;AppDomain is unloaded.&amp;nbsp; This monitor is created on the virtual directory itself if the "App_LocalResources" folder does not exist; otherwise, the monitor is created on the "App_LocalResources" directory.&lt;/p&gt;
&lt;p&gt;ReadDirectoryChangesW is called with these arguments:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Directory= "c:\ppath\subdir"&lt;br /&gt;WatchSubtree=&amp;nbsp;False&lt;br /&gt;NotifyFilter=&amp;nbsp;FILE_NOTIFY_CHANGE_FILE_NAME &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; | FILE_NOTIFY_CHANGE_DIR_NAME&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;| FILE_NOTIFY_CHANGE_CREATION&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;| FILE_NOTIFY_CHANGE_SIZE&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;| FILE_NOTIFY_CHANGE_LAST_WRITE&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;| FILE_NOTIFY_CHANGE_SECURITY&lt;/p&gt;
&lt;p&gt;The&amp;nbsp;stack trace for the call is:&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Web.DirMonCompletion..ctor(DirectoryMonitor dirMon, String dir, Boolean watchSubtree, UInt32 notifyFilter)&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Web.DirectoryMonitor.StartMonitoring()&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Web.DirectoryMonitor.StartMonitoringFile(String file, FileChangeEventHandler callback, String alias)&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Web.FileChangesMonitor.ListenToSubdirectoryChanges(String dirRoot, String dirToListenTo)&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Web.FileChangesMonitor.StartListeningToVirtualSubdirectory(VirtualPath virtualDir)&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Web.Compilation.BuildManager.EnsureFirstTimeDirectoryInit(VirtualPath virtualDir)&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Web.Compilation.BuildManager.GetBuildResultFromCacheInternal(String cacheKey, Boolean keyFromVPP, VirtualPath virtualPath, Int64 hashCode)&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Web.Compilation.BuildManager.GetVPathBuildResultFromCacheInternal(VirtualPath virtualPath)&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Web.Compilation.BuildManager.GetVPathBuildResultInternal&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Web.Compilation.BuildManager.GetVPathBuildResultWithNoAssert&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Web.Compilation.BuildManager.GetVirtualPathObjectFactory(VirtualPath virtualPath, HttpContext context, Boolean allowCrossApp, Boolean noAssert)&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Web.Compilation.BuildManager.CreateInstanceFromVirtualPath&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Web.UI.PageHandlerFactory.GetHandlerHelper(HttpContext context, String requestType, VirtualPath virtualPath, String physicalPath)&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Web.UI.PageHandlerFactory.System.Web.IHttpHandlerFactory2.GetHandler&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Web.HttpApplication.MapHttpHandler(HttpContext context, String requestType, VirtualPath path, String pathTranslated, Boolean useAppConfig)&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Web.HttpApplication.MapHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()&lt;br /&gt;&amp;nbsp;&amp;nbsp; at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean&amp;amp; completedSynchronously)&amp;nbsp;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;So what about global.asax, or the f.aspx page that we used in the example?&amp;nbsp; Aren't they monitored?&amp;nbsp; Yes, they are, but they piggy back on one of the directory monitors listed above.&amp;nbsp; In the example, both global.asax and f.aspx would be added to the directory monitor described in #5.&amp;nbsp; Each directory monitor is capable of monitoring many different files, each with a unique callback for notification upon any changes.&amp;nbsp; I hope this helps your understanding.&amp;nbsp;I've also included an ASPX page code sample below that will display the number of requests, number of AppDomain restarts, and number of active directory monitors.&amp;nbsp; I'm not sure why you would want to know how many directory monitors are active in your application, but if someone asks you, you can always requests this page and&amp;nbsp;answer them.&amp;nbsp; :)&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;div&gt;
&lt;pre&gt;&amp;lt;%@ import namespace="System.Diagnostics"%&amp;gt;&lt;br /&gt;&amp;lt;%@ import namespace="System.Reflection"%&amp;gt;&lt;/pre&gt;
&lt;pre&gt;&amp;lt;script runat="server" language="c#"&amp;gt;&lt;br /&gt;void Page_Load() {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; PerformanceCounter pc1 = new PerformanceCounter("ASP.NET Applications", &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; "Requests Total", &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; "__Total__");&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; PerformanceCounter pc2 = new PerformanceCounter("ASP.NET", &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; "Application Restarts");&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Type t = typeof(HttpRuntime).Assembly.GetType(&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; "System.Web.DirMonCompletion");&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; int dirMonCount = (int) t.InvokeMember("_activeDirMonCompletions",&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; BindingFlags.NonPublic &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; | BindingFlags.Static&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; | BindingFlags.GetField, &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; null, &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; null, &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; null); &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; // The perf client polls the server every 400 milliseconds&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; System.Threading.Thread.Sleep(800);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Response.Output.WriteLine(&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; "Requests={0},Restarts={1},DirMonCompletions={2}",&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; pc1.NextValue(), &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; pc2.NextValue(), &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; dirMonCount);&lt;br /&gt;}&lt;br /&gt;&amp;lt;/script&amp;gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;-Thomas&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=5826773" width="1" height="1"&gt;</content><author><name>tmarq</name><uri>http://blogs.msdn.com/tmarq/ProfileUrlRedirect.ashx</uri></author></entry><entry><title>Things you didn't know (and perhaps don't want to know) about ASP.NET Session State</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/b/tmarq/archive/2007/10/10/things-you-didn-t-know-and-probably-don-t-want-to-know-about-asp-net-session-state.aspx" /><link rel="enclosure" type="application/pdf" length="581653" href="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-components-postattachments/00-05-38-88-93/AspNetSessionState.pdf" /><id>http://blogs.msdn.com/b/tmarq/archive/2007/10/10/things-you-didn-t-know-and-probably-don-t-want-to-know-about-asp-net-session-state.aspx</id><published>2007-10-10T07:21:00Z</published><updated>2007-10-10T07:21:00Z</updated><content type="html">&lt;P&gt;I've uploaded a brief summary on the extensibility of ASP.NET Session State (see link to&amp;nbsp;AspNetSessionState.pdf below).&amp;nbsp; Many people aren't aware of how easy it is to implement&amp;nbsp;a custom&amp;nbsp;session state provider.&amp;nbsp; Having your very own disk based (file based) session state provider is just a few lines of code away.&lt;/P&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=5388893" width="1" height="1"&gt;</content><author><name>tmarq</name><uri>http://blogs.msdn.com/tmarq/ProfileUrlRedirect.ashx</uri></author></entry><entry><title>Server.TransferRequest hangs or takes a long time to execute</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/b/tmarq/archive/2007/10/10/server-transferrequest-hangs-or-takes-a-long-time-to-execute.aspx" /><id>http://blogs.msdn.com/b/tmarq/archive/2007/10/10/server-transferrequest-hangs-or-takes-a-long-time-to-execute.aspx</id><published>2007-10-10T06:31:00Z</published><updated>2007-10-10T06:31:00Z</updated><content type="html">&lt;P&gt;Server.TransferRequest is a new API in the IIS 7.0 release of ASP.NET.&amp;nbsp; It is essentially a server-side redirect.&amp;nbsp; When invoked, a new request context is created, called the child request, and it goes through the entire IIS 7.0/ASP.NET integrated pipeline.&amp;nbsp; The child request can be mapped to any of the handlers configured in IIS, and it's execution is no different than it would be if it was received via the HTTP stack.&amp;nbsp; The parent request jumps ahead in the pipeline to the end request notification, and waits for the child request to complete.&amp;nbsp; When the child request completes, the parent request executes the end request notifications and completes itself.&lt;/P&gt;
&lt;P&gt;Recently an issue was brought to my attention by &lt;A class="" title="Luis Abreu" href="http://msmvps.com/blogs/luisabreu/archive/2007/10/09/are-you-using-the-new-transferrequest.aspx" mce_href="http://msmvps.com/blogs/luisabreu/archive/2007/10/09/are-you-using-the-new-transferrequest.aspx"&gt;Luis Abreu&lt;/A&gt;.&amp;nbsp; Attempts to use Server.TransferRequest between the AcquireRequestState and ReleaseRequestState notifications will hang when session state is in use.&amp;nbsp; Luis does a great job of describing the problem, so let me just add that we will fix this as soon as possible.&lt;/P&gt;
&lt;P&gt;-Thomas&lt;/P&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=5388801" width="1" height="1"&gt;</content><author><name>tmarq</name><uri>http://blogs.msdn.com/tmarq/ProfileUrlRedirect.ashx</uri></author></entry><entry><title>IIS 7.0, ASP.NET, pipelines, modules, handlers, and preconditions</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/b/tmarq/archive/2007/08/30/iis-7-0-asp-net-pipelines-modules-handlers-and-preconditions.aspx" /><id>http://blogs.msdn.com/b/tmarq/archive/2007/08/30/iis-7-0-asp-net-pipelines-modules-handlers-and-preconditions.aspx</id><published>2007-08-30T09:36:00Z</published><updated>2007-08-30T09:36:00Z</updated><content type="html">&lt;P&gt;&lt;B&gt;&lt;I&gt;1.0 What is the IIS Pipeline&lt;/I&gt;&lt;/B&gt;&lt;/P&gt;
&lt;P&gt;Conceptually, the IIS pipeline is a state machine with the following states:&lt;/P&gt;
&lt;P&gt;BEGIN_REQUEST&lt;BR&gt;AUTHENTICATE_REQUEST&lt;BR&gt;AUTHORIZE_REQUEST&lt;BR&gt;RESOLVE_REQUEST_CACHE&lt;BR&gt;MAP_REQUEST_HANDLER&lt;BR&gt;ACQUIRE_REQUEST_STATE&lt;BR&gt;PRE_EXECUTE_REQUEST_HANDLER&lt;BR&gt;&lt;B&gt;EXECUTE_REQUEST_HANDLER&lt;/B&gt;&lt;BR&gt;RELEASE_REQUEST_STATE&lt;BR&gt;UPDATE_REQUEST_CACHE&lt;BR&gt;LOG_REQUEST&lt;BR&gt;END_REQUEST&lt;/P&gt;
&lt;P&gt;When a request is received, it moves through the state machine step-by-step until completion.&amp;nbsp; Beginning with IIS 7.0, users can register their own code to be executed within any or all of the steps.&amp;nbsp; This code is referred to as a module.&amp;nbsp; In other words, users register modules to handle events in the pipeline.&amp;nbsp; (I will refer to the stages of the pipeline as steps, events, notifications, and sometimes combinations of one or more of these.&amp;nbsp; Don't let that confuse you.&amp;nbsp; They all mean the same thing.)&lt;/P&gt;
&lt;P&gt;&lt;B&gt;&lt;I&gt;2.0 Integrated vs. Classic Pipeline Modes&lt;/I&gt;&lt;/B&gt;&lt;/P&gt;
&lt;P&gt;IIS 7.0 has two pipeline modes: integrated and classic.&amp;nbsp; The latter is sometimes referred to as ISAPI mode.&amp;nbsp; &lt;/P&gt;
&lt;P&gt;Integrated mode allows both managed and native modules to register for events in the IIS pipeline.&amp;nbsp; This enables many new scenarios, such as applying ASP.NET forms authentication to non-asp.net requests (static files, classic ASP files, etc).&amp;nbsp; &lt;/P&gt;
&lt;P&gt;Classic mode is identical to IIS 6.0.&amp;nbsp; In classic mode, the ASP.NET pipeline (BeginRequest, AuthenticateRequest,…, EndRequest) runs entirely within the IIS pipeline’s &lt;B&gt;EXECUTE_REQUEST_HANDLER&lt;/B&gt; event.&amp;nbsp; Think of ASP.NET in classic mode as a pipeline within a pipeline.&lt;/P&gt;
&lt;P&gt;&lt;B&gt;&lt;I&gt;3.0 Handlers vs. Modules&lt;/I&gt;&lt;/B&gt;&lt;/P&gt;
&lt;P&gt;Modules are units of code that have registered for one or more pipeline events. They perform authentication, authorization, logging, etc. There is one task that is reserved for a special module, known as a handler.&amp;nbsp; The handler’s unique job is to retrieve the resource that is requested in the URL.&amp;nbsp; All resources served by IIS are mapped to a handler in configuration.&amp;nbsp; If the configuration mapping does not exist, requests for the resource will receive a 404 HTTP status.&amp;nbsp; In addition to the configuration mapping, which takes place before the pipeline begins, the handler mapping can be changed during request execution in the MAP_REQUEST_HANDLER event.&amp;nbsp; This allows scenarios such as URL rewriting to work.&amp;nbsp; Handlers are notified, or executed, in the &lt;B&gt;EXECUTE_REQUEST_HANDLER&lt;/B&gt; step.&amp;nbsp; (Note that only the mapped handler is notified, as you would expect.)&lt;/P&gt;
&lt;P&gt;&lt;B&gt;&lt;I&gt;4.0 Handler and Module Configuration&lt;/I&gt;&lt;/B&gt;&lt;/P&gt;
&lt;P&gt;IIS 7.0 introduces two new configuration sections: &amp;lt;handlers&amp;gt; and &amp;lt;modules&amp;gt;. The ASP.NET &amp;lt;httpHandlers&amp;gt; and &amp;lt;httpModules&amp;gt; sections still exist, but are only used when running in the classic pipeline mode.&amp;nbsp; The new IIS sections add a level of complexity which many users find confusing.&lt;/P&gt;
&lt;P&gt;First, if you're running in classic mode, your application should not require any changes.&amp;nbsp; The standard extensions served by ASP.NET ASPX, ASMX, AXD, etc are mapped to a handler in IIS configuration that invokes aspnet_isapi.dll and executes managed code just like it does on IIS 6.&amp;nbsp; If, however, you made changes to the IIS script mappings on IIS 6, you will need to make corresponding changes in IIS 7.&amp;nbsp; This will involve adding a &amp;lt;handler&amp;gt; mapping.&lt;/P&gt;
&lt;P&gt;On the other hand, if you're running in integrated mode, you will need to migrate your &amp;lt;httpHandler&amp;gt; and &amp;lt;httpModule&amp;gt; sections to the new &amp;lt;handler&amp;gt; and &amp;lt;module&amp;gt; sections.&amp;nbsp; For example, you can use the following command to migrate the &amp;lt;httpModules&amp;gt; section for the default web site:&lt;/P&gt;&lt;B&gt;%WINDIR%\system32\inetsrv\appcmd migrate config "Default Web Site/" -section:httpModules&lt;/B&gt; 
&lt;P&gt;The confusion occurs right about the time you start wondering how to add a managed module to the integrated pipeline that only executes for managed requests.&amp;nbsp; Or when you need to add a handler mapping that only applies to the integrated pipeline.&amp;nbsp; To perform these type actions, IIS uses preconditions on the module or handler to restrict the conditions under which it executes.&lt;/P&gt;
&lt;P&gt;For handlers, if you set &lt;B&gt;preCondition="integratedMode" &lt;/B&gt;in the &amp;lt;handler&amp;gt; mapping, the handler will only run in integrated mode.&amp;nbsp; On the other hand, if you set &lt;B&gt;preCondition="classicMode"&lt;/B&gt; the handler will only run in classic mode.&amp;nbsp; And if you omit both of these, the handler can run in both modes, although this is not possible for a managed handler.&lt;/P&gt;
&lt;P&gt;For modules, if you set &lt;B&gt;preCondition=”managedHandler”&lt;/B&gt; in the &amp;lt;module&amp;gt; entry, the module will only run for managed requests (a managed request is a request that has a managed handler).&amp;nbsp; If you omit this, the module will run for all requests.&amp;nbsp; Managed modules in the &amp;lt;modules&amp;gt; section are only called if you're running in the integrated pipeline.&amp;nbsp; If you're running in classic mode, then &amp;lt;httpModules&amp;gt; is used.&lt;/P&gt;
&lt;P&gt;Note that the “integratedMode” and “classicMode” preconditions only apply to handlers, and the “managedHandler” precondition only applies to modules. Also note that there are other preconditions that we have not discussed here.&amp;nbsp; These can be used to restrict the handler or module to a version of the framework, or specific processor architecture (32-bit or 64-bit), for example.&lt;/P&gt;
&lt;P&gt;The ASP.NET &amp;lt;httpHandlers&amp;gt; section has no knowledge of preconditions, and so you should never use them there.&amp;nbsp; The same is true for &amp;lt;httpModules&amp;gt;.&lt;/P&gt;
&lt;P&gt;&lt;B&gt;&lt;I&gt;5.0 Troubleshooting&lt;/I&gt;&lt;/B&gt;&lt;/P&gt;
&lt;P&gt;If you receive an error similar to the one below, your &amp;lt;handler&amp;gt; section is probably invalid.&amp;nbsp; &lt;/P&gt;
&lt;BLOCKQUOTE&gt;&lt;B&gt;HTTP Error 500.21 - Internal Server Error&lt;/B&gt;&lt;BR&gt;Handler "&amp;lt;HANDLER_NAME&amp;gt;" has a bad module "ManagedPipelineHandler" in its module list&lt;/BLOCKQUOTE&gt;
&lt;P&gt;You probably have a handler mapping that does not have the correct precondition.&amp;nbsp; IIS is not forgiving in regard to typos, and preconditions are case-sensitive.&amp;nbsp; The text must be &lt;B&gt;preCondition=”integratedMode”&lt;/B&gt; or &lt;B&gt;preCondition=”classicMode”&lt;/B&gt;.&lt;/P&gt;&lt;/SPAN&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=4641157" width="1" height="1"&gt;</content><author><name>tmarq</name><uri>http://blogs.msdn.com/tmarq/ProfileUrlRedirect.ashx</uri></author></entry><entry><title>IIS7 kernel-mode authentication</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/b/tmarq/archive/2007/08/29/iis7-kernel-mode-authentication.aspx" /><id>http://blogs.msdn.com/b/tmarq/archive/2007/08/29/iis7-kernel-mode-authentication.aspx</id><published>2007-08-29T22:41:00Z</published><updated>2007-08-29T22:41:00Z</updated><content type="html">&lt;P&gt;This appears to be undocumented.&lt;/P&gt;
&lt;P&gt;By default, IIS7 uses kernel-mode authentication, which happens to have a large performance benefit.&amp;nbsp; There is a bug in kernel-mode authentication where requests that send credentials will fail unless an anonymous request has been made first.&amp;nbsp; Once an anonymous request has been made, requests that send credentials will succeed (if the authentication is successful).&amp;nbsp; This isn’t a problem for browsers that send an anonymous request first and only send credentials when challenged, but it is often a problem for clients like that send credentials on the initial request.&amp;nbsp; If you find that an HTTP client that you wrote fails on IIS7, you can confirm that this is the cause by disabling kernel-mode authentication with the following command:&lt;/P&gt;
&lt;P&gt;%windir%\system32\inetsrv\appcmd set config /section:windowsAuthentication /useKernelMode:false&lt;/P&gt;
&lt;P&gt;-Thomas&lt;/P&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=4633085" width="1" height="1"&gt;</content><author><name>tmarq</name><uri>http://blogs.msdn.com/tmarq/ProfileUrlRedirect.ashx</uri></author></entry><entry><title>An Ounce of Prevention: Using System.Threading.Timer in an ASP.NET Application</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/b/tmarq/archive/2007/07/21/an-ounce-of-prevention-using-system-threading-timer-in-an-asp-net-application.aspx" /><id>http://blogs.msdn.com/b/tmarq/archive/2007/07/21/an-ounce-of-prevention-using-system-threading-timer-in-an-asp-net-application.aspx</id><published>2007-07-21T09:15:00Z</published><updated>2007-07-21T09:15:00Z</updated><content type="html">&lt;p&gt;Using a timer in an ASP.NET application is not as simple as it seems.&amp;nbsp; There are&amp;nbsp;three problems that commonly occur:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;ASP.NET applications shutdown for a number of reasons, and if the timer is not disposed before the AppDomain is unloaded, the timer may fire and crash the process with an unhandled AppDomainUnloadedException. &lt;/li&gt;
&lt;li&gt;The timer executes its callback on CLR Threadpool threads.&amp;nbsp; If the interval is small and the server is under a heavy load, the CLR Threadpool can queue up many work items.&amp;nbsp; This could result in multiple timer callbacks firing at or about the same time.&amp;nbsp; Or, if the timer callback itself takes a long time to execute, this could result in multiple concurrent callers.&amp;nbsp; Developers often incorrectly assume that only one thread will call the timer concurrently. &lt;/li&gt;
&lt;li&gt;If your timer callback invokes native code, via a P/Invoke or COM Interop call, and the AppDomain is unloaded while that code is being called, it may result in an unhandled AppDomainUnloadedException and crash the process. &lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;These problems are easily avoided.&amp;nbsp; There are a number of ways to be notified before the AppDomain is unloaded.&amp;nbsp; Within ASP.NET applications, you can listen to the &lt;a href="http://msdn2.microsoft.com/en-us/library/ms178473.aspx"&gt;Application_End&lt;/a&gt; event.&amp;nbsp; Another way would be to listen to the &lt;a href="http://msdn2.microsoft.com/en-us/library/system.appdomain.domainunload.aspx"&gt;AppDomain.DomainUnload&lt;/a&gt; event.&amp;nbsp; Regardless of how you are notified, you&amp;rsquo;ll want to use code similar to the following to protect yourself against the aforementioned problems:&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;div class="code_sample"&gt;
&lt;pre&gt;// 1 if timer callback is executing; otherwise 0
int _inTimerCallback = 0;

Timer _timer = new Timer(new TimerCallback(this.TimerCallback), 
                         null, 
                         1000, 
                         1000);
//
// if the timer fires frequently or the callback runs for a long period, 
// you may want to prevent two threads from calling it concurrently
//
void TimerCallback(object state) {
    // if the callback is already being executed, just return
    if (Interlocked.Exchange(ref _inTimerCallback, 1) != 0) {
        return;
    }
    try {
        // do work (potentially long running work that 
        // may call into native code)
    }
    finally {
        Interlocked.Exchange(ref _inTimerCallback, 0);
    }
}

//
// Before the AppDomain is shutdown, the timer must be disposed.  Otherwise,
// the underlying native timer may crash the process if it fires and attempts
// to call into the unloaded AppDomain.  In a multi-threaded environment,
// you may need to use synchronization to ensure the timer is disposed at 
// most once.
//
internal void DisposeTimer() {
    Timer timer = _timer;
    if (timer != null
        &amp;amp;&amp;amp; Interlocked.CompareExchange(ref _timer, null, timer) == timer) {
            timer.Dispose();
        }
    }

    // if you don&amp;rsquo;t want the timer callback to be aborted during an 
    // AppDomain unload, or if it calls into native code, then loop until 
    // the callback has completed
    while (_inTimerCallback != 0) {
        Sleep(100);
    }
}&lt;/pre&gt;
&lt;/div&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=3985550" width="1" height="1"&gt;</content><author><name>tmarq</name><uri>http://blogs.msdn.com/tmarq/ProfileUrlRedirect.ashx</uri></author></entry><entry><title>ASP.NET Thread Usage on IIS 7.5, IIS 7.0, and IIS 6.0</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/b/tmarq/archive/2007/07/21/asp-net-thread-usage-on-iis-7-0-and-6-0.aspx" /><id>http://blogs.msdn.com/b/tmarq/archive/2007/07/21/asp-net-thread-usage-on-iis-7-0-and-6-0.aspx</id><published>2007-07-21T04:39:00Z</published><updated>2007-07-21T04:39:00Z</updated><content type="html">&lt;p&gt;I'd like to briefly explain how ASP.NET uses threads when hosted on IIS 7.5, IIS 7.0 and IIS 6.0, as well as the configuration changes that you can make to alter the defaults. Please take a quick look at the &amp;ldquo;Threading Explained&amp;rdquo; section in &lt;a title="Chapter 6" href="http://msdn2.microsoft.com/en-us/library/ms998549.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/ms998549.aspx"&gt;Chapter 6&lt;/a&gt; of &amp;ldquo;Improving .NET Application Performance and Scalability&amp;rdquo;. Prior to v2.0 of the .NET Framework, it was necessary to tweak the processModel/maxWorkerThreads, processModel/maxIoThreads, httpRuntime/minFreeThreads, httpRuntime/minLocalRequestFreeThreads, and connectionManagement/maxconnection configuration. The v2.0 .NET Framework attempted to simplify this by adding a new &lt;a title="processModel/autoConfig" href="http://msdn2.microsoft.com/en-us/library/7w2sway1(vs.80).aspx" mce_href="http://msdn2.microsoft.com/en-us/library/7w2sway1(vs.80).aspx"&gt;processModel/autoConfig&lt;/a&gt; configuration, which made the changes for you at runtime. With the introduction of IIS 7.0 and the ASP.NET integrated pipeline, we've introduced another element to the mix, a registry key named &lt;strong&gt;MaxConcurrentRequestsPerCPU&lt;/strong&gt;. Lets start with a discussion of how things worked on IIS 6.0 before discussing the changes made in IIS 7.0.&lt;/p&gt;
&lt;p&gt;When ASP.NET is hosted on IIS 6.0, the request is handed over to ASP.NET on an IIS I/O thread. ASP.NET immediately posts the request to the CLR ThreadPool and returns HSE_STATUS_PENDING to IIS. This frees up IIS threads, enabling IIS to serve other requests, such as static files. Posting the request to the CLR Threadpool also acts as a queue. The CLR Threadpool automatically adjusts the number of threads according to the workload, so that if the requests are high throughput there will only be 1 or 2 threads per CPU, and if the requests are high latency there will be potentially far more concurrently executing requests than 1 or 2 per CPU. The queuing provided by the CLR Threadpool is very useful, because while the requests are in the queue there is only a very small amount of memory allocated for the request, and it is all native memory. It&amp;rsquo;s not until a thread picks up the request and begins to execute that we enter managed code and allocate managed memory.&lt;/p&gt;
&lt;p&gt;The CLR Threadpool is not the only queue used by ASP.NET when hosted in IIS 6.0. There are also queues at the application level, within each AppDomain. If there is a lot of latency, the CLR Threadpool will grow and inject more active threads. At some point we would either run out of threads, not have enough threads left over for performing other tasks, or the memory associated with all the concurrently executing requests would be too much, so ASP.NET imposes a cap on the number of threads concurrently executing requests. This is controlled by the httpRuntime/minFreeThreads and httpRuntime/minLocalRequestFreeThreads settings. If the cap is exceeded, the request is queued in the application-level queue, and executed later when the concurrency falls back down below the limit. The performance of these application-level queues is really quite miserable. If you observe that the &amp;ldquo;ASP.NET Applications\Requests in Application Queue&amp;rdquo; performance counter is non-zero, you definitely have a performance problem. These queues were implemented to prevent thread exhaustion and contention related to web service requests. The problem was first described in &lt;a title="KB 821268" href="http://support.microsoft.com/kb/821268" mce_href="http://support.microsoft.com/kb/821268"&gt;KB 821268&lt;/a&gt;, which I had published many years ago. The KB article has been re-written a few times since it was originally published, and I hope nothing has been lost during the translations.&lt;/p&gt;
&lt;p&gt;For most usage scenarios, the changes recommended in the KB article are not necessary because v2.0 introduced &lt;a title="processModel/autoConfig" href="http://msdn2.microsoft.com/en-us/library/7w2sway1(vs.80).aspx" mce_href="http://msdn2.microsoft.com/en-us/library/7w2sway1(vs.80).aspx"&gt;processModel/autoConfig&lt;/a&gt;. However, the autoConfig setting may not work for everyone--it limits the number of concurrently executing requests per CPU to 12. An application with high latency may want to allow higher concurrency than this, in which case you can disable autoConfig and make the changes yourself. If you do allow higher concurrency, keep an eye on your working set. I believe the default works for about 90% of the applications out there. I do wish we had the foresight to name that setting maxConcurrentRequestsPerCPU, and allow it to be used to control concurrency, since that would be much easier to configure. I guess this is just another example of when business was just a little bit faster than the speed of thought.&lt;/p&gt;
&lt;p&gt;When ASP.NET is hosted on IIS 7.5 and 7.0&amp;nbsp;in integrated mode, the use of threads is a bit different. First of all, the application-level queues are no more. Their performance was always really bad, there was no hope in fixing this, and so we got rid of them. But perhaps the biggest difference is that in IIS 6.0, or ISAPI mode, ASP.NET restricts the number of &lt;strong&gt;threads&lt;/strong&gt; concurrently executing requests, but in IIS 7.5 and 7.0 integrated mode, ASP.NET restricts the number of concurrently executing &lt;strong&gt;requests&lt;/strong&gt;. The difference only matters when the requests are asynchronous (the request either has an asynchronous handler or a module in the pipeline completes asynchronously). Obviously if the reqeusts are synchronous, then the number of concurrently executing &lt;strong&gt;requests&lt;/strong&gt; is the same as the number of &lt;strong&gt;threads&lt;/strong&gt; concurrently executing requests, but if the requests are asynchronous then these two numbers can be quite different as you could have far more reqeusts than threads. So how do things work, exactly,&amp;nbsp;in integrated mode? Similar to IIS 6.0 (classic mode, a.k.a. ISAPI mode), the request is still handed over to ASP.NET on an IIS I/O thread. And ASP.NET immediately posts the request to the CLR Threadpool and returns pending. We found this thread switch was still necessary to maintain optimal performance for static file requests. So although you will take a performance hit if you&amp;rsquo;re only executing ASP.NET requests, if you have a mix of dynamic and static files, as we see with many large corporate workloads, this thread switch will actually free up threads for retrieving the static files. Finally, once the request is picked up by a thread from the CLR Threadpool, we check to see how many requests are currently executing. If the count is too high, the request is queued in a global (process-wide) queue. This global, native queue performs much better than the application-level queues used when we&amp;rsquo;re running in ISAPI mode (same as on IIS 6.0). There is very little memory associated with a queued request, and we have not entered managed code yet so there is no managed memory associated with it. And we respect the FIFO aspect of a queue, something we didn&amp;rsquo;t do with the application-level queues--if there was more than one application, there was no simple way to globally manage the individual queues. We did however have a difficult time trying to come up with a good configuration story for the IIS 7.0 changes.&lt;/p&gt;
&lt;p&gt;When I discuss how to configure thread usage for ASP.NET/IIS 7.0 integrated mode, please remember that we have a lot of pre-existing code and configuration, and you can&amp;rsquo;t just create something new the way you would like to without introducing backward compatibility issues. In this new mode, the CLR Threadpool is still controlled by the processModel configuration settings (autoConfig, maxWorkerThreads, maxIoThreads, minWorkerThreads, and minIoThreads). And autoConfig is still enabled, but its modifications to httpRuntime/minFreeThreads and httpRuntime/minLocalRequestFreeThreads do nothing, since the application-level queues do not exist. Perhaps we should have tried to use them to configure the global (process-wide) queue limits, but they have application scope (httpRuntime configuration is application specific), not process scope, not to mention being too difficult to understand. And because of some issues with using the configuration system that I won&amp;rsquo;t go into right now, we decided to use a registry key to control concurrency. So for IIS 7.0 integrated mode, a DWORD named &lt;strong&gt;MaxConcurrentRequestsPerCPU&lt;/strong&gt; within HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\ASP.NET\2.0.50727.0 determines the number of concurrent &lt;strong&gt;requests&lt;/strong&gt; per CPU. By default, it does not exist and the number of requests per CPU is limited to 12. If you&amp;rsquo;re curious to see how much faster ASP.NET requests execute without the thread switch, you can set the value to 0. This will cause the request to execute on the IIS I/O thread, without switching to a CLR Threadpool thread. I don&amp;rsquo;t recommend this primarily because dynamic requests take a long time to execute relative to static requests, and I believe the overall performance of the system is better with the thread switch. However, and this is important, if your application consists of primarily or entirely asynchronous requests, the default &lt;strong&gt;MaxConcurrentReqeustsPerCPU&lt;/strong&gt; limit of 12 will be too restrictive for you, especially if the requests are very long running. In this case, I do recommend setting &lt;strong&gt;MaxConcurrentRequestsPerCPU&lt;/strong&gt; to&amp;nbsp;a very high number.&amp;nbsp; In fact, in v4.0, we have changed the default for MaxConcurrentRequestsPerCPU to 5000.&amp;nbsp; There's nothing special about 5000, other than it is a very large number, and will therefore allow plenty of async requests to execute concurrently.&amp;nbsp; One thing to watch out for is that when concurrency increases, your application will use more memory&amp;nbsp;simply because there are more requests executing in managed code.&amp;nbsp; The CLR ThreadPool will still do a great job maintaining the number of threads in the ThreadPool, so there should be no concern about this adversly impacting synchronous requests.&amp;nbsp; I know there are people using ASP.NET 2.0 and developing &lt;a title="Comet" href="http://en.wikipedia.org/wiki/Comet_(programming)" mce_href="http://en.wikipedia.org/wiki/Comet_(programming)"&gt;Comet&lt;/a&gt; or Comet-like applications on WS08 x64 servers, and they set MaxConcurrentRequestsPerCPU to 5000 and increase the HTTP.sys kernel queue limit to 10,000 (it has a default of 1000).&amp;nbsp; The HTTP.sys kernel queue limit is controlled by IIS.&amp;nbsp; You&amp;nbsp;can change it by opening IIS Manager and opening the Advanced Settings&amp;nbsp;for your application pool and changing the value of "Queue Length".&lt;/p&gt;
&lt;p&gt;As a final remark, please note that the processModel/requestQueueLimit configuration limits the maximum number of requests in the ASP.NET system for IIS 6.0, IIS 7.0, and IIS 7.5. This number is exposed by the "ASP.NET/Requests Current" performance counter, and when it exceeds the limit (default is 5000) we reject requests with a 503 status (Server Too Busy).&lt;/p&gt;
&lt;p&gt;-Thomas&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;/strong&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;UPDATE (Aug-18-2008)&lt;/strong&gt;: .NET Framework v3.5 SP1 released earlier this week and it includes an update to the v2.0 binaries that supports configuring IIS application pools via the aspnet.config file.&amp;nbsp; The aspnet.config file is not very well known.&amp;nbsp; It is the &lt;a title="CLR Hosting" href="http://msdn.microsoft.com/en-us/magazine/cc163567.aspx"&gt;CLR Hosting&lt;/a&gt; configuration file, and ASP.NET/IIS pass it to the CLR when the CLR is loaded.&amp;nbsp;&amp;nbsp;The host configuration file&amp;nbsp;(aspnet.config) applies configuration at the process-level, as opposed to the application-level like web.config.&amp;nbsp; There is a new&amp;nbsp;system.web/applicationPool&amp;nbsp;configuration section which applies to integrated mode only (Classic/ISAPI mode ignores these settings).&amp;nbsp;The new config section with default values is:&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;system.web&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;applicationPool maxConcurrentRequestsPerCPU="12" maxConcurrentThreadsPerCPU="0" requestQueueLimit="5000"/&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/system.web&amp;gt;&lt;/p&gt;
&lt;p&gt;There is a corresponding IIS 7.5 change (Windows Server 2008 &lt;strong&gt;R2&lt;/strong&gt; only)&amp;nbsp;which allows different aspnet.config files to be specified for each application pool (this change has not been ported to IIS 7.0). With this, you can configure each application pool differently.&amp;nbsp; The maxConcurrentRequestsPerCPU setting is the same as the registry key described above, except that the setting in aspnet.config will override the registry key value.&amp;nbsp; The maxConcurrentThreadsPerCPU setting is new, and allows concurrency to be gated by the number of threads, similar to the way it was done in Classic/ISAPI mode.&amp;nbsp; By default maxConcurrentThreadsPerCPU is disabled (has a value of 0), in favor of gating concurrency by the number of requests, primarily because&amp;nbsp;maxConcurrentRequestsPerCPU performs better (gating the number of threads is more complicated/costly to implement).&amp;nbsp; Normally you'll use request gating, but you now have the option of disabling it (set maxConccurrentRequestsPerCPU=0) and enabling maxConccurentThreadsPerCPU instead.&amp;nbsp; You can also enable both request and thread gating at the same time, and ASP.NET will ensure both requirements are met.&amp;nbsp; The requestQueueLimit setting is the same as processModel/requestQueueLimit, except that the setting in aspnet.config will override the machine.config setting.&amp;nbsp; All of this may be a little confusing, but for nearly everyone, my recommendation is that for ASP.NET 2.0 you should use the same settings as the defaults in ASP.NET v4.0; that is, set maxConcurrentRequestsPerCPU = "5000" and maxConcurrentThreadsPerCPU="0".&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;UPDATE (Sep-12-2011)&lt;/strong&gt;:&amp;nbsp; The only relevant change to .NET Framework v4.0 (as compared to 3.5 or 2.0) is that the default for maxConcurrentRequestsPerCPU was increased to&amp;nbsp;5000.&amp;nbsp; 5000 is also the value you should use in versions 2.0 and 3.5, which have a default of 12.&amp;nbsp; Also, IIS 7.5 is identical to IIS 7.0 as far as threading is concerned.&amp;nbsp; The only difference between IIS 7.5 and 7.0 that is relevant to this blog post is the support to configure different aspnet.config files for each application pool.&amp;nbsp; You do this by setting the &lt;a title="CLRConfigFile" href="http://msdn.microsoft.com/en-us/library/aa347554(VS.90).aspx"&gt;CLRConfigFile&lt;/a&gt; attribute for the application pool.&amp;nbsp; You can then use the system.web &lt;a title="applicationPool" href="http://msdn.microsoft.com/en-us/library/dd560844.aspx"&gt;applicationPool&lt;/a&gt; configuration mentioned above to set different values for maxConcurrentRequestsPerCPU, maxConcurrentThreadsPerCPU, and requestQueueLimit, if desired.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;In general, running with default configuration works best.&amp;nbsp; However, applications that have measurable latency, say latency of 100 milliseconds when communicating with a backend web service, will perform better with a few configuration changes.&amp;nbsp; Let me tell you what configuration changes you should make on &lt;strong&gt;IIS 7.0 and IIS 7.5 in integrated mode&lt;/strong&gt;&amp;nbsp;in order to handle a large number of concurrent requests to an application that has backend latency.&amp;nbsp;By large number of concurrent reqests, I mean between 12 and 5000 per CPU.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;For v2.0 and v3.5&amp;nbsp;set a DWORD registry value @ HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\ASP.NET\2.0.50727.0\MaxConcurrentRequestsPerCPU = 5000.&amp;nbsp; Restart IIS&lt;/li&gt;
&lt;li&gt;For v3.5, you can&amp;nbsp;alternatively set &amp;lt;system.web&amp;gt;&amp;lt;applicationPool maxConcurrentRequestsPerCPU="5000"/&amp;gt;&amp;lt;/system.web&amp;gt; in the &lt;a title="aspnet.config" href="http://msdn.microsoft.com/en-us/library/dd560844.aspx"&gt;aspnet.config&lt;/a&gt; file.&amp;nbsp; If the value is set in both places, the aspnet.config setting overrides the registry setting.&lt;/li&gt;
&lt;li&gt;For v4.0, the default maxConcurrentRequestsPerCPU is 5000, so you don't need to do anything.&lt;/li&gt;
&lt;li&gt;Increase the HTTP.sys queue limit, which has a default of 1000.&amp;nbsp; If the operating system is x64 and you have 2 GB of RAM or more, setting it to 5000 should be fine.&amp;nbsp; If it is too low, you may see HTTP.sys reject requests with a 503 status.&amp;nbsp; Open IIS Manager and the Advanced Settings for your Application Pool, then change the value of "Queue Length".&lt;/li&gt;
&lt;li&gt;If your ASP.NET application is using web services (WFC or ASMX)&amp;nbsp;or System.Net to communicate with a backend over HTTP you may need to increase &lt;a title="connectionManagement/maxconnection" href="http://msdn.microsoft.com/en-us/library/fb6y0fyc.aspx"&gt;connectionManagement/maxconnection&lt;/a&gt;.&amp;nbsp; For ASP.NET applications, this is limited to 12 * #CPUs by the &lt;a title="autoConfig" href="http://msdn.microsoft.com/en-us/library/7w2sway1.aspx"&gt;autoConfig&lt;/a&gt; feature.&amp;nbsp; This means that on a quad-proc, you can have at most 12 * 4 = 48 concurrent connections to an IP&amp;nbsp;end point.&amp;nbsp; Because this is tied to autoConfig, the easiest way to increase maxconnection in an ASP.NET application is to set System.Net.ServicePointManager.DefaultConnectionLimit programatically, from Application_Start, for example.&amp;nbsp; Set the value to the number of concurrent System.Net connections you expect your application to use.&amp;nbsp; I've set this to Int32.MaxValue and not had any side effects, so you might try that--this is actually the default used in the native HTTP stack, WinHTTP.&amp;nbsp; If you're not able to set System.Net.ServicePointManager.DefaultConnectionLimit programmatically, you'll need to disable &lt;a title="autoConfig" href="http://msdn.microsoft.com/en-us/library/7w2sway1.aspx"&gt;autoConfig&lt;/a&gt; , but that means you also need to set maxWorkerThreads and&amp;nbsp;maxIoThreads.&amp;nbsp; You won't need to set minFreeThreads or minLocalRequestFreeThreads if you're not using classic/ISAPI mode.&lt;/li&gt;
&lt;li&gt;If your application sees a large number of concurrent requests at start-up or has a bursty load, where concurrency increases suddenly, you will need to make the application asynchronous because the CLR ThreadPool does not respond well to these loads.&amp;nbsp; The CLR ThreadPool injects new threads at a rate of about 2 per second.&amp;nbsp; This is true for all versions of the CLR (v1.0 thru v4.0) at the time of this writing.&amp;nbsp; If concurrency is bursty&amp;nbsp;and the request thread&amp;nbsp;blocks (e.g. on a backend with latency), the injection rate of 2 threads per second will make your application respond very poorly to this work load.&amp;nbsp; The fix is to stop blocking on threads by using asynchronous I/O to communicate with the backend with latency.&amp;nbsp; If you cannot make the application asynchronous, you will need to increase &lt;a title="minWorkerThreads" href="http://support.microsoft.com/kb/821268"&gt;minWorkerThreads&lt;/a&gt;.&amp;nbsp; I don't like to increase minWorkerThreads.&amp;nbsp; It has a side effect on high-throughput synchronous requests that don't block on threads, because the thread count is artificially high.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;-Thomas&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=3984584" width="1" height="1"&gt;</content><author><name>tmarq</name><uri>http://blogs.msdn.com/tmarq/ProfileUrlRedirect.ashx</uri></author></entry><entry><title>Some history on the ASP.NET cache memory limits</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/b/tmarq/archive/2007/06/25/some-history-on-the-asp-net-cache-memory-limits.aspx" /><id>http://blogs.msdn.com/b/tmarq/archive/2007/06/25/some-history-on-the-asp-net-cache-memory-limits.aspx</id><published>2007-06-25T20:13:00Z</published><updated>2007-06-25T20:13:00Z</updated><content type="html">&lt;P&gt;When v1.0 released, the only OS that ASP.NET supported was Win2k, the only process model was aspnet_wp, and the only architecture we supported was x86.&amp;nbsp; The aspnet_wp process model had a memory limit that was calculated at runtime during startup.&amp;nbsp; The limit was configurable (&amp;lt;processModel memoryLimit/&amp;gt;) as a percentage of physical RAM, and the default was 60%.&amp;nbsp; This limit prevents the process from consuming too much memory, especially in the face of a memory leak caused by user code, and allows the process to gracefully recycle.&amp;nbsp; We also had a feature known as the ASP.NET cache, which allows you to store objects with various expiration and validation policies.&amp;nbsp; The ASP.NET cache had built in logic that would drop entries when private bytes became too close to the private bytes memory limit for the process.&amp;nbsp; The actual percentage at which the cache began to drop entries is an implementation detail, and it is different on different hardware.&amp;nbsp; Suffice it to say that the cache dropped entries when memory usage approached the process memory limit.&lt;/P&gt;
&lt;P&gt;This default limit of 60% worked okay for machines with small amounts of RAM.&amp;nbsp; The 60% value was chosen to allow plenty of breathing room in the case where the limit was exceeded, since when that happens the new process is created before the old process has completely drained existing requests.&amp;nbsp;&amp;nbsp; Stress runs showed that memory limits higher than this resulted in too much memory paging during process recycles.&amp;nbsp; However, there was still a problem on boxes with large amounts of RAM.&amp;nbsp; For example, a box with 4GB of RAM had a default memory limit of 2.4GB (60%) by default.&amp;nbsp; This obviously doesn’t work given that the user mode address space is only 2GB.&amp;nbsp; Furthermore, ASP.NET apps typically had a very fragmented virtual address space.&amp;nbsp; We often saw apps throwing OutOfMemoryExceptions when virtual bytes reached about 1.5GB.&amp;nbsp; We found through experimentation that on x86 with a 2GB user-mode virtual address space, a conservative private bytes limit of 800 MB worked for most people.&amp;nbsp; We began recommending that people use this as a cap on private bytes.&amp;nbsp; Of course some applications could go beyond this, but if you wanted to play it safe, 800MB was a good limit for private bytes.&lt;/P&gt;
&lt;P&gt;In v1.1, we also supported WS03.&amp;nbsp; WS03 used a different process model (w3wp).&amp;nbsp; This process model gets its private bytes limit from IIS configuration ("Maximum Used Memory" on Recycling tab of Application Pool properties in IIS Manager), not the aforementioned &amp;lt;processModel memoryLimit/&amp;gt;.&amp;nbsp; Unfortunately, this limit had no default (it was not set by default).&amp;nbsp; So if the application used the ASP.NET cache, we would never drop entries and eventually you would start seeing OutOfMemoryExceptions.&amp;nbsp; These are non-recoverable, and required human intervention since the process would typically stay up and serve responses with a nicely formatted OutOfMemoryException error page from that point forward.&lt;/P&gt;
&lt;P&gt;In v2.0, we fixed this by exposing new configuration for the cache &amp;lt;caching&amp;gt;&amp;lt;cache privateBytesLimit/&amp;gt;&amp;lt;caching&amp;gt;.&amp;nbsp; Now the cache could have a memory limit independent of the process memory limit.&amp;nbsp; For backward compatibility, we also applied the process memory limit if it was set.&amp;nbsp;&amp;nbsp; Unfortunately, this complicated things a bit, and the way we calculate the cache memory limit is hidden to the user.&amp;nbsp; If you don’t set a cache or a process memory limit, we calculate one for you.&amp;nbsp;&amp;nbsp; If the user mode address space is 2GB, we use MIN(60% physical RAM, 800MB).&amp;nbsp; If the user mode address space is greater than 2GB and the process is 32-bit,&amp;nbsp;we use MIN(60% physical RAM, 1800MB).&amp;nbsp; And for 64-bit processes, we use MIN(60% physical RAM, 1TB).&amp;nbsp; That’s what happens if you don’t set any limits.&amp;nbsp; However, if you set both a cache memory limit and a process memory limit, we will use the minimum of the two.&amp;nbsp; And if you only set one, we will use the one you set.&amp;nbsp; Confused?&amp;nbsp; You'll be happy to know that the actual limit we use is exposed by the property &lt;A title=Cache.EffectivePrivateBytesLimit href="http://msdn.microsoft.com/en-us/library/system.web.caching.cache.effectiveprivatebyteslimit(VS.80).aspx" target=_blank mce_href="http://msdn.microsoft.com/en-us/library/system.web.caching.cache.effectiveprivatebyteslimit(VS.80).aspx"&gt;Cache.EffectivePrivateBytesLimit&lt;/A&gt;.&lt;/P&gt;
&lt;P&gt;While 60% may not work for boxes with 1TB of RAM, this value is configurable.&lt;/P&gt;
&lt;P&gt;Enough about private bytes.&amp;nbsp; The cache also has a physical memory limit that was introduced in v2.0.&amp;nbsp; It was introduced because the garbage collector (GC) becomes very aggressive in low memory conditions.&amp;nbsp; If the cache is consuming a bunch of memory and inducing the low memory condition, then it needs to release entries to alleviate the pressure on the GC.&amp;nbsp; In 2.0, the cache dropped entries when available memory was &amp;lt;= 11%.&amp;nbsp; We later discovered this was too aggressive, and have backed it off in 2.0 SP1 so that now we can use much more physical memory before dropping entries.&amp;nbsp; The actual limits that we use are an implementation detail, and they are different for different hardware.&lt;/P&gt;
&lt;P&gt;The v2.0 SP1 cache work was requested as a QFE by MS.com.&amp;nbsp; The KB article for this is at &lt;A href="http://support.microsoft.com/kb/938276" mce_href="http://support.microsoft.com/kb/938276"&gt;http://support.microsoft.com/kb/938276&lt;/A&gt;.&amp;nbsp; Anyone using the v2.0 ASP.NET cache should install this QFE.&amp;nbsp; It will of course be included in v2.0 SP1, when it is released.&lt;/P&gt;
&lt;P&gt;The cache memory manager should not be the primary eviction mechanism.&amp;nbsp; It is better to use expiration policies on the entries, so that they expire&amp;nbsp; before encountering memory pressure.&amp;nbsp; Most of the issues surrounding memory stem from the fact that the ASP.NET cache is not able to detect how much memory it is using.&amp;nbsp; It knows the number of entries, but not their sizes.&amp;nbsp; It uses Private Bytes for the process and available physical memory for the machine to determine when to drop entries, even though the cache may not be the cause of the memory pressure.&amp;nbsp; I suggest thinking of the cache memory manager as a safety net or fallback, and using expiration policies or other forms of validation to ensure that your cache entries are removed before encountering memory pressure.&amp;nbsp; If you only have a handful of cache entries this is not really an issue, and you can rely on the cache memory manager.&amp;nbsp; But if you're inserting unique entries on a per-request basis, or if you just simply have a very large number of entries, it makes sense to use expiration and/or validation policies.&lt;BR&gt;&lt;/P&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=3523527" width="1" height="1"&gt;</content><author><name>tmarq</name><uri>http://blogs.msdn.com/tmarq/ProfileUrlRedirect.ashx</uri></author></entry><entry><title>Orcas Beta 1 Visual Studio F5 Debugging on IIS 7 / Windows Vista</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/b/tmarq/archive/2007/06/13/orcas-beta-1-visual-studio-f5-debugging-on-iis-7-windows-vista.aspx" /><id>http://blogs.msdn.com/b/tmarq/archive/2007/06/13/orcas-beta-1-visual-studio-f5-debugging-on-iis-7-windows-vista.aspx</id><published>2007-06-13T18:15:00Z</published><updated>2007-06-13T18:15:00Z</updated><content type="html">&lt;P&gt;In Visual Studio Orcas Beta 1, the AJAX ScriptModule is listed in the web.config template used for version 3.5 web projects.&amp;nbsp; There is a known bug (DevDivBugs 78367) that prevents Visual Studio F5 debugging from working on IIS7 / Windows Vista&amp;nbsp;when a module in the IIS/ASP.NET integrated pipeline has an event handler for PreSendRequestHeaders or PreSendRequestContent.&amp;nbsp; Unfortunately, the ScriptModule listens to these.&lt;/P&gt;
&lt;P&gt;There are two workarounds:&lt;/P&gt;
&lt;OL&gt;
&lt;LI&gt;If you’re not using AJAX, you can remove the ScriptModule from the web.config file.&amp;nbsp; You need to remove it from the “&amp;lt;system.webServer&amp;gt;&amp;lt;modules&amp;gt;” configuration section, since this is the one used by the IIS/ASP.NET integrated pipeline.&amp;nbsp; Note that 2.0 web projects uses a web.config template that does not contain an entry for ScriptModule, and can therefore be debugged using F5 without making any changes.&lt;/LI&gt;
&lt;LI&gt;If you’re using AJAX, you can run the application in the “Classic .NET AppPool” instead of the “DefaultAppPool”.&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/LI&gt;&lt;/OL&gt;
&lt;P&gt;If you need to use the IIS/ASP.NET integrated pipeline and AJAX, you’re best option is to manually attach to w3wp.exe instead of using F5 debugging.&amp;nbsp;&lt;/P&gt;
&lt;P&gt;This issue is fixed post Orcas Beta 1.&lt;BR&gt;&lt;/P&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=3270487" width="1" height="1"&gt;</content><author><name>tmarq</name><uri>http://blogs.msdn.com/tmarq/ProfileUrlRedirect.ashx</uri></author></entry><entry><title>The CLR Profiler</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/b/tmarq/archive/2007/06/09/the-clr-profiler.aspx" /><id>http://blogs.msdn.com/b/tmarq/archive/2007/06/09/the-clr-profiler.aspx</id><published>2007-06-09T04:41:00Z</published><updated>2007-06-09T04:41:00Z</updated><content type="html">&lt;H1 style="MARGIN: 12pt 0in 3pt; TEXT-ALIGN: center" align=center&gt;&lt;SPAN style="FONT-SIZE: 10pt; FONT-FAMILY: 'Verdana','sans-serif'; mso-bidi-font-family: 'Times New Roman'"&gt;The ClrProfiler&lt;?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" /&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/H1&gt;
&lt;H2 style="MARGIN: 12pt 0in 3pt"&gt;&lt;SPAN style="FONT-SIZE: 10pt; FONT-FAMILY: 'Verdana','sans-serif'; mso-bidi-font-family: 'Times New Roman'"&gt;&lt;o:p&gt;&lt;EM&gt;&amp;nbsp;&lt;/EM&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/H2&gt;
&lt;H2 style="MARGIN: 12pt 0in 3pt"&gt;&lt;SPAN style="FONT-SIZE: 10pt; FONT-FAMILY: 'Verdana','sans-serif'; mso-bidi-font-family: 'Times New Roman'"&gt;&lt;EM&gt;1.0 Where can I get it?&lt;o:p&gt;&lt;/o:p&gt;&lt;/EM&gt;&lt;/SPAN&gt;&lt;/H2&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;SPAN style="FONT-SIZE: 10pt; FONT-FAMILY: 'Verdana','sans-serif'"&gt;&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;A href="http://www.microsoft.com/downloads/details.aspx?FamilyId=A362781C-3870-43BE-8926-862B40AA0CD0&amp;amp;displaylang=en" mce_href="http://www.microsoft.com/downloads/details.aspx?FamilyId=A362781C-3870-43BE-8926-862B40AA0CD0&amp;amp;displaylang=en"&gt;http://www.microsoft.com/downloads/details.aspx?FamilyId=A362781C-3870-43BE-8926-862B40AA0CD0&amp;amp;displaylang=en&lt;/A&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;SPAN style="FONT-SIZE: 10pt; FONT-FAMILY: 'Verdana','sans-serif'"&gt;&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;H2 style="MARGIN: 12pt 0in 3pt"&gt;&lt;SPAN style="FONT-SIZE: 10pt; FONT-FAMILY: 'Verdana','sans-serif'; mso-bidi-font-family: 'Times New Roman'"&gt;&lt;EM&gt;2.0 Should I use it?&lt;o:p&gt;&lt;/o:p&gt;&lt;/EM&gt;&lt;/SPAN&gt;&lt;/H2&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;SPAN style="FONT-SIZE: 10pt; FONT-FAMILY: 'Verdana','sans-serif'"&gt;&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;SPAN style="FONT-SIZE: 10pt; FONT-FAMILY: 'Verdana','sans-serif'"&gt;When running performance tests, if the CPU is maximized and % Time in GC is greater than 5%, you can improve throughput by reducing allocations.&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;SPAN style="FONT-SIZE: 10pt; FONT-FAMILY: 'Verdana','sans-serif'"&gt;&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;SPAN style="FONT-SIZE: 10pt; FONT-FAMILY: 'Verdana','sans-serif'"&gt;Excessive garbage collection is often the culprit for low throughput.&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;The “.NET CLR Memory(w3wp)\% Time in GC” performance counter tells you how much time is spent doing collections.&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;The diagram below depicts how this is calculated:&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;SPAN style="FONT-SIZE: 10pt; FONT-FAMILY: 'Verdana','sans-serif'"&gt;&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt 0.5in; TEXT-INDENT: 0.5in"&gt;&lt;SPAN style="FONT-SIZE: 10pt; FONT-FAMILY: 'Verdana','sans-serif'"&gt;| gc1_begin&lt;SPAN style="mso-tab-count: 1"&gt;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;| gc1_end&lt;SPAN style="mso-tab-count: 4"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;| gc2_begin&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;SPAN style="FONT-SIZE: 10pt; FONT-FAMILY: 'Verdana','sans-serif'"&gt;Time &lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE: 10pt; FONT-FAMILY: Wingdings; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-char-type: symbol; mso-symbol-font-family: Wingdings"&gt;&lt;SPAN style="mso-char-type: symbol; mso-symbol-font-family: Wingdings"&gt;à&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE: 10pt; FONT-FAMILY: 'Verdana','sans-serif'"&gt; -------------------------------------------------------------------------------------&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt 0.5in; TEXT-INDENT: 0.5in"&gt;&lt;SPAN style="FONT-SIZE: 10pt; FONT-FAMILY: 'Verdana','sans-serif'"&gt;|&lt;SPAN style="mso-tab-count: 2"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;|&lt;SPAN style="mso-tab-count: 5"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/SPAN&gt;|&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;SPAN style="FONT-SIZE: 10pt; FONT-FAMILY: 'Verdana','sans-serif'"&gt;&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;SPAN style="FONT-SIZE: 10pt; FONT-FAMILY: 'Verdana','sans-serif'"&gt;&lt;SPAN style="mso-tab-count: 1"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;% Time in GC = 100 * (gc1_end – gc1_begin) / (gc2_begin – gc1_begin)&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;SPAN style="FONT-SIZE: 10pt; FONT-FAMILY: 'Verdana','sans-serif'"&gt;&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;SPAN style="FONT-SIZE: 10pt; FONT-FAMILY: 'Verdana','sans-serif'"&gt;&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;SPAN style="FONT-SIZE: 10pt; FONT-FAMILY: 'Verdana','sans-serif'"&gt;&lt;o:p&gt;For ASP.NET applications, it is interesting to know what is allocated on a per request basis.&amp;nbsp; You can calculate &lt;U&gt;allocated bytes per request&lt;/U&gt; by&amp;nbsp;dividing ".NET CLR Memory(w3wp)\Allocated Bytes/Sec" by "ASP.NET Applications(__Total__)\Requests/sec".&amp;nbsp; &lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;H2 style="MARGIN: 12pt 0in 3pt"&gt;&lt;SPAN style="FONT-SIZE: 10pt; FONT-FAMILY: 'Verdana','sans-serif'; mso-bidi-font-family: 'Times New Roman'"&gt;&lt;EM&gt;3.0 How do I create a profile?&lt;o:p&gt;&lt;/o:p&gt;&lt;/EM&gt;&lt;/SPAN&gt;&lt;/H2&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;SPAN style="FONT-SIZE: 10pt; FONT-FAMILY: 'Verdana','sans-serif'"&gt;&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;OL style="MARGIN-TOP: 0in" type=1&gt;
&lt;LI class=MsoNormal style="MARGIN: 0in 0in 0pt; mso-list: l0 level1 lfo2; tab-stops: list .5in"&gt;&lt;SPAN style="FONT-SIZE: 10pt; FONT-FAMILY: 'Verdana','sans-serif'"&gt;Change the worker process identity to System.&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/LI&gt;
&lt;LI class=MsoNormal style="MARGIN: 0in 0in 0pt; mso-list: l0 level1 lfo2; tab-stops: list .5in"&gt;&lt;SPAN style="FONT-SIZE: 10pt; FONT-FAMILY: 'Verdana','sans-serif'"&gt;Launch ClrProfiler.exe.&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/LI&gt;
&lt;LI class=MsoNormal style="MARGIN: 0in 0in 0pt; mso-list: l0 level1 lfo2; tab-stops: list .5in"&gt;&lt;SPAN style="FONT-SIZE: 10pt; FONT-FAMILY: 'Verdana','sans-serif'"&gt;Uncheck “Profiling active”.&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/LI&gt;
&lt;LI class=MsoNormal style="MARGIN: 0in 0in 0pt; mso-list: l0 level1 lfo2; tab-stops: list .5in"&gt;&lt;SPAN style="FONT-SIZE: 10pt; FONT-FAMILY: 'Verdana','sans-serif'"&gt;From the File menu, select Profile ASP.NET.&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/LI&gt;
&lt;LI class=MsoNormal style="MARGIN: 0in 0in 0pt; mso-list: l0 level1 lfo2; tab-stops: list .5in"&gt;&lt;SPAN style="FONT-SIZE: 10pt; FONT-FAMILY: 'Verdana','sans-serif'"&gt;Wait for a dialog to appear that says, “Waiting for ASP.NET to start common language runtime – this is the time to load your test page”.&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/LI&gt;
&lt;LI class=MsoNormal style="MARGIN: 0in 0in 0pt; mso-list: l0 level1 lfo2; tab-stops: list .5in"&gt;&lt;SPAN style="FONT-SIZE: 10pt; FONT-FAMILY: 'Verdana','sans-serif'"&gt;Issue the first request to your page.&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/LI&gt;
&lt;LI class=MsoNormal style="MARGIN: 0in 0in 0pt; mso-list: l0 level1 lfo2; tab-stops: list .5in"&gt;&lt;SPAN style="FONT-SIZE: 10pt; FONT-FAMILY: 'Verdana','sans-serif'"&gt;Check “Profiling active”.&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/LI&gt;
&lt;LI class=MsoNormal style="MARGIN: 0in 0in 0pt; mso-list: l0 level1 lfo2; tab-stops: list .5in"&gt;&lt;SPAN style="FONT-SIZE: 10pt; FONT-FAMILY: 'Verdana','sans-serif'"&gt;Issue the second request to your page.&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/LI&gt;
&lt;LI class=MsoNormal style="MARGIN: 0in 0in 0pt; mso-list: l0 level1 lfo2; tab-stops: list .5in"&gt;&lt;SPAN style="FONT-SIZE: 10pt; FONT-FAMILY: 'Verdana','sans-serif'"&gt;Uncheck “Profiling active”.&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/LI&gt;
&lt;LI class=MsoNormal style="MARGIN: 0in 0in 0pt; mso-list: l0 level1 lfo2; tab-stops: list .5in"&gt;&lt;SPAN style="FONT-SIZE: 10pt; FONT-FAMILY: 'Verdana','sans-serif'"&gt;Click the “Kill ASP.NET” button&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/LI&gt;
&lt;LI class=MsoNormal style="MARGIN: 0in 0in 0pt; mso-list: l0 level1 lfo2; tab-stops: list .5in"&gt;&lt;SPAN style="FONT-SIZE: 10pt; FONT-FAMILY: 'Verdana','sans-serif'"&gt;From the file menu, select “Save Profile as” if you would like to use the profile for comparisons later on.&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/LI&gt;&lt;/OL&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;SPAN style="FONT-SIZE: 10pt; FONT-FAMILY: 'Verdana','sans-serif'"&gt;&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;SPAN style="FONT-SIZE: 10pt; FONT-FAMILY: 'Verdana','sans-serif'"&gt;&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;H2 style="MARGIN: 12pt 0in 3pt"&gt;&lt;SPAN style="FONT-SIZE: 10pt; FONT-FAMILY: 'Verdana','sans-serif'; mso-bidi-font-family: 'Times New Roman'"&gt;&lt;EM&gt;4.0 How do I interpret the profile?&lt;o:p&gt;&lt;/o:p&gt;&lt;/EM&gt;&lt;/SPAN&gt;&lt;/H2&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;SPAN style="FONT-SIZE: 10pt; FONT-FAMILY: 'Verdana','sans-serif'"&gt;&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;SPAN style="FONT-SIZE: 10pt; FONT-FAMILY: 'Verdana','sans-serif'"&gt;There are many interesting ways to use the ClrProfiler.&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;I will discuss the two that I find most useful.&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;SPAN style="FONT-SIZE: 10pt; FONT-FAMILY: 'Verdana','sans-serif'"&gt;&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;SPAN style="FONT-SIZE: 10pt; FONT-FAMILY: 'Verdana','sans-serif'"&gt;1.&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;The “Histogram Allocated Types” view displays a histogram by size.&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;In the left panel, you can select objects with the mouse, right click, and select “Show Who Allocated”.&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;More importantly, in the right panel is listed the total number of objects allocated and their total size.&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;If you right click in this region, you can select “Export Data to File” to save a CSV file that lists the min size, max size, # instances, and total size for each type.&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;You can write a Perl script to quickly create a diff to assist you when comparing profiles.&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;SPAN style="FONT-SIZE: 10pt; FONT-FAMILY: 'Verdana','sans-serif'"&gt;&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;SPAN style="FONT-SIZE: 10pt; FONT-FAMILY: 'Verdana','sans-serif'"&gt;2.&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;The “Allocation Graph” view is a graph depicting function calls and the total amount of memory and types of objects allocated by each call.&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;At the far left you see the total amount of memory allocated by your test.&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;As you move to the right, the function calls branch out and you see how much is allocated by each call.&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;At the far right is a list of the objects that are allocated, and the total size of those allocations.&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;You can right click anywhere in the graph and use various options, such as “Prune to Callers and Callees”, “Find Routine”, “Find interesting nodes”, etc. &lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;SPAN style="FONT-SIZE: 10pt; FONT-FAMILY: 'Verdana','sans-serif'"&gt;&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;H2 style="MARGIN: 12pt 0in 3pt"&gt;&lt;SPAN style="FONT-SIZE: 10pt; FONT-FAMILY: 'Verdana','sans-serif'; mso-bidi-font-family: 'Times New Roman'"&gt;&lt;EM&gt;5.0 Troubleshooting&lt;o:p&gt;&lt;/o:p&gt;&lt;/EM&gt;&lt;/SPAN&gt;&lt;/H2&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;SPAN style="FONT-SIZE: 10pt; FONT-FAMILY: 'Verdana','sans-serif'"&gt;&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;SPAN style="FONT-SIZE: 10pt; FONT-FAMILY: 'Verdana','sans-serif'"&gt;The ClrProfiler sets a custom environment block for both IISADMIN and W3SVC.&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;Normally this is removed when you click the “Kill ASP.NET” button, but if the profiler crashes you might have to remove it manually.&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;To do this, delete the REG_SZ named Environment beneath HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\IISADMIN and HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\W3SVC.&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;SPAN style="FONT-SIZE: 10pt; FONT-FAMILY: 'Verdana','sans-serif'"&gt;&lt;/SPAN&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;SPAN style="FONT-SIZE: 10pt; FONT-FAMILY: 'Verdana','sans-serif'"&gt;A couple points worth noting:&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE: 10pt; FONT-FAMILY: 'Verdana','sans-serif'"&gt;&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;SPAN style="FONT-SIZE: 10pt; FONT-FAMILY: 'Verdana','sans-serif'"&gt;&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;UL style="MARGIN-TOP: 0in" type=disc&gt;
&lt;LI class=MsoNormal style="MARGIN: 0in 0in 0pt; mso-list: l1 level1 lfo1; tab-stops: list .5in"&gt;&lt;SPAN style="FONT-SIZE: 10pt; FONT-FAMILY: 'Verdana','sans-serif'"&gt;You only need to profile a single request.&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;You can profile multiple requests, but the profile will grow quickly in size.&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/LI&gt;
&lt;LI class=MsoNormal style="MARGIN: 0in 0in 0pt; mso-list: l1 level1 lfo1; tab-stops: list .5in"&gt;&lt;SPAN style="FONT-SIZE: 10pt; FONT-FAMILY: 'Verdana','sans-serif'"&gt;You do not want to include AppDomain load or AppDomain unload in the profile.&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;Uncheck “Profiling active” to avoid profiling during these times.&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/LI&gt;&lt;/UL&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;SPAN style="FONT-SIZE: 10pt; FONT-FAMILY: 'Verdana','sans-serif'"&gt;&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;H2 style="MARGIN: 12pt 0in 3pt"&gt;&lt;SPAN style="FONT-SIZE: 10pt; FONT-FAMILY: 'Verdana','sans-serif'; mso-bidi-font-family: 'Times New Roman'"&gt;&lt;EM&gt;6.0 Documentation&lt;o:p&gt;&lt;/o:p&gt;&lt;/EM&gt;&lt;/SPAN&gt;&lt;/H2&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;SPAN style="FONT-SIZE: 10pt; FONT-FAMILY: 'Verdana','sans-serif'"&gt;&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;SPAN style="FONT-SIZE: 10pt; FONT-FAMILY: 'Verdana','sans-serif'"&gt;The author of the ClrProfiler wrote a document that I hear is quite good.&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;It is available with version 1.0 of the tool at &lt;A href="http://download.microsoft.com/download/4/4/2/442d67c7-a1c1-4884-9715-803a7b485b82/clr%20profiler.exe" mce_href="http://download.microsoft.com/download/4/4/2/442d67c7-a1c1-4884-9715-803a7b485b82/clr%20profiler.exe"&gt;http://download.microsoft.com/download/4/4/2/442d67c7-a1c1-4884-9715-803a7b485b82/clr%20profiler.exe&lt;/A&gt;.&lt;/P&gt;&lt;/SPAN&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=3173226" width="1" height="1"&gt;</content><author><name>tmarq</name><uri>http://blogs.msdn.com/tmarq/ProfileUrlRedirect.ashx</uri></author></entry><entry><title>Introduction</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/b/tmarq/archive/2007/06/09/introduction.aspx" /><link rel="enclosure" type="image/x-png" length="1590" href="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-components-postattachments/00-03-17-25-96/six.png" /><id>http://blogs.msdn.com/b/tmarq/archive/2007/06/09/introduction.aspx</id><published>2007-06-09T03:25:00Z</published><updated>2007-06-09T03:25:00Z</updated><content type="html">&lt;P&gt;Good afternoon!&amp;nbsp; My name is Thomas Marquardt.&amp;nbsp; I am a developer at Microsoft on the ASP.NET team.&amp;nbsp; I implemented/designed the ASP.NET/IIS7 integrated pipeline.&amp;nbsp; I own the core of ASP.NET, the pipeline, the hosting interfaces, and application services including the cache and file change notifications.&amp;nbsp; When I began working here in July of 1998, the CLR was known as COM+ and consisted primarily of an execution engine.&amp;nbsp; You could almost say there weren't any compilers or languages targeting the runtime at that time.&amp;nbsp; Yes, it had a small class library but only really simple stuff like Object, Type, and String, and the compiler was a hack that allowed&amp;nbsp;some testing of the runtime.&amp;nbsp; From there we went on to release .NET Framework v1.0 and Visual Studio .NET and three new languages (well, at least one new language and two&amp;nbsp;with significant modifications)&amp;nbsp;in February of 2002, which is really quite a bit of work to have completed&amp;nbsp;in such a short time.&amp;nbsp; Oh, and of course ASP.NET was part of that release.&lt;/P&gt;
&lt;P&gt;Before Microsoft I was working on a Ph.D. in Mathematics at the University of Oregon when I decided to shift gears and move to Seattle.&amp;nbsp; I still have a passion for mathematics but I also enjoy creating great software.&lt;/P&gt;
&lt;P mce_keep="true"&gt;&amp;nbsp;&lt;/P&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=3172596" width="1" height="1"&gt;</content><author><name>tmarq</name><uri>http://blogs.msdn.com/tmarq/ProfileUrlRedirect.ashx</uri></author></entry></feed>