<?xml version="1.0" encoding="utf-8"?>
<rss xmlns:atom="https://www.w3.org/2005/Atom" xmlns:sy="https://purl.org/rss/1.0/modules/syndication/" xmlns:dc="https://purl.org/dc/elements/1.1/" xmlns:content="https://purl.org/rss/1.0/modules/content/" xmlns:wfw="https://wellformedweb.org/CommentAPI/" xmlns:slash="https://purl.org/rss/1.0/modules/slash/"
  version="2.0">
  <channel>
    <title>Thomas Marquardt's Blog</title>
    <atom:link
      href="https://docs.microsoft.com/archive/blogs/tmarq/feed.xml"
      rel="self"
      type="application/rss+xml" />
    <link>https://docs.microsoft.com/archive/blogs/tmarq/feed.xml</link>
    <description />
    <lastBuildDate>Wed, 01 Apr 2015 21:46:39 GMT</lastBuildDate>
    <language>en-US</language>
    <sy:updatePeriod>hourly</sy:updatePeriod>
    <sy:updateFrequency>1</sy:updateFrequency>
    <item>
      <title>ASP.NET v4.0 Security Update and ISAPI Filters on IIS 6.0</title>
      <link>https://docs.microsoft.com/archive/blogs/tmarq/asp-net-v4-0-security-update-and-isapi-filters-on-iis-6-0</link>
      <pubDate>Fri, 08 Oct 2010 10:54:00 GMT</pubDate>
      <dc:creator><![CDATA[Thomas L. Marquardt]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/tmarq/2010/10/08/asp-net-v4-0-security-update-and-isapi-filters-on-iis-6-0/</guid>
      <description><![CDATA[A security patch for ASP.NET v4.0 was released recently.&nbsp; The details of the patch can be found...]]></description>
      <content:encoded><![CDATA[<p>A security patch for ASP.NET v4.0 was released recently.&nbsp; The details of the patch can be found at <a href="https://www.microsoft.com/technet/security/bulletin/ms10-070.mspx">https://www.microsoft.com/technet/security/bulletin/ms10-070.mspx</a>.</p>
<p>I really only have one thing to say in this blog post:&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&nbsp;<a href="https://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">disable the ASP.NET extensionless URL feature</a>.&nbsp; The security patch unfortunately deletes that registry setting, so you will have to reset it after you install the patch.&nbsp; The&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.&nbsp;&nbsp;The rest of this blog post provides more detail.&nbsp; </p>
<p>The security patch also has an unfortunate side-effect on IIS 6.0 of moving the ASP.NET v4.0&nbsp;ISAPI Filter&nbsp;(aspnet_filter.dll) to the&nbsp;bottom of the global&nbsp;(effects all web sites)&nbsp;ISAPI Filter list in IIS configuration.&nbsp; This could break applications if they have an ISAPI Filter that needs to come after aspnet_filter.dll.&nbsp; If&nbsp;you installed the patch and&nbsp;suddenly started seeing failures with "/eurl.axd/{GUID}" (where {GUID} is a hexadecimal number)&nbsp;in the URL, then this is the most likely cause.&nbsp; You&nbsp;can try re-ordering&nbsp;your ISAPI Filters list, but if you're not using the ASP.NET extensionless URL&nbsp;feature on IIS 6.0&nbsp;then you should disable it as described at <a href="https://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">https://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</a>.&nbsp; That blog post also explains how the ASP.NET v4.0&nbsp;Extensionless URL feature works on IIS 6.0.</p>
<p>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&nbsp;finally select the "ISAPI Filters" tab.&nbsp; From here, you can select an ISAPI&nbsp;Filter in the list&nbsp;and click the "Move up" button to change the order.&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".&nbsp; For more details on ISAPI Filters, see <a href="https://msdn.microsoft.com/en-us/library/ms524610(VS.90).aspx">https://msdn.microsoft.com/en-us/library/ms524610(VS.90).aspx</a>.&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.</p>
<p>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).&nbsp;&nbsp;The ASP.NET ISAPI Extension then removes "/eurl.axd/{GUID}" from the URL.&nbsp; The problems with "/eurl.axd/{GUID}" appearing in URLs occur when an ISAPI Rewrite Filter gets between the ASP.NET ISAPI Filter&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,&nbsp;and so "/eurl.axd/{GUID}"&nbsp;remains in the URL.&nbsp; </p>
<p>The ASP.NET v4.0 Extensionless URL (EURL) feature does its work&nbsp;during the&nbsp;<a target="_blank" href="https://msdn.microsoft.com/en-us/library/ms525878(VS.90).aspx">SF_NOTIFY_PREPROC_HEADERS</a> notification on IIS 6.0.&nbsp;&nbsp;For those of you having problems with the <a target="_blank" href="https://iirf.codeplex.com/">Ionic&rsquo;s ISAPI Rewrite Filter</a>, I looked at the Ionic's source code and understand why ASP.NET's EURL feature breaks.&nbsp;&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.&nbsp; So an older version of the Ionic&rsquo;s ISAPI Rewrite Filter would be compatible with the ASP.NET EURL feature as long as it's ISAPI Filter&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&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<strong> </strong>which always comes in between the ASP.NET ISAPI Filter and the ASP.NET ISAPI Extension.&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.&nbsp; All you should do is <a href="https://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">disable the ASP.NET extensionless URL feature</a>.</p>
<p>&nbsp;</p>]]></content:encoded>
    </item>
    <item>
      <title>How to Disable the ASP.NET v4.0 Extensionless URL feature on IIS 6.0</title>
      <link>https://docs.microsoft.com/archive/blogs/tmarq/how-to-disable-the-asp-net-v4-0-extensionless-url-feature-on-iis-6-0</link>
      <pubDate>Fri, 18 Jun 2010 10:44:00 GMT</pubDate>
      <dc:creator><![CDATA[Thomas L. Marquardt]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/tmarq/2010/06/18/how-to-disable-the-asp-net-v4-0-extensionless-url-feature-on-iis-6-0/</guid>
      <description><![CDATA[You can disable the v4.0 ASP.NET extensionless URL feature on IIS6 by setting a DWORD at...]]></description>
      <content:encoded><![CDATA[<p>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.&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.&nbsp; Note that&nbsp;for Wow64 (i.e.,&nbsp;32-bit worker process running on&nbsp;64-bit OS), this registry key must be set at HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\ASP.NET\4.0.30319.0\EnableExtensionlessUrls.</p>
<p>Here&rsquo;s how the v4.0 ASP.NET extensionless URL features works on IIS 6.&nbsp; We have an ISAPI Filter named aspnet_filter.dll that appends &ldquo;/eurl.axd/GUID&rdquo; to extensionless URLs.&nbsp; This happens early on in the request processing.&nbsp; We also have a script mapping so that &ldquo;*.axd&rdquo; requests are handled by our ISAPI, aspnet_isapi.dll.&nbsp; When we append &ldquo;/eurl.axd/GUID&rdquo; to extensionless URLs, it causes them to be mapped to our aspnet_isapi.dll, as long as the script map exists as expected.&nbsp; These requests then enter ASP.NET where we remove &ldquo;/eurl.axd/GUID&rdquo; from the URL, that is, we restore the original URL.&nbsp; The restoration of the original URL happens very early.&nbsp; Now the URL is extensionless again, and if no further changes are made and you&rsquo;re using the default &lt;httpHandlers&gt; section in web.config, this will be assigned to the DefaultHttpHandler, since it has path=&rdquo;*&rdquo; and is the first handler to match the extensionless URL.&nbsp; The DefaultHttpHandler will then redirect this request back to IIS, but this time our filter will not append &ldquo;/eurl.axd/GUID&rdquo; and the request will be handled as it would be normally.&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.</p>
<p>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:</p>
<ol>
<li>EnableExtensionlessUrls is not defined or has a value of 1.</li>
<li>v4.0 aspnet_filter.dll is registered as an ISAPI Filter.</li>
<li>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).</li>
<li>v4.0 aspnet_isapi.dll is marked as "Allowed" in the ISAPI Restriction list.</li>
<li>The web site has read and script permission enabled.</li>
</ol>
<p>I've heard that it is not compatible with <strong>Ionics ISAPI Rewrite Filter</strong>, and it may not be compatible with other URL rewriting components.&nbsp; Fortunately&nbsp;the ASP.NET feature&nbsp;is easy to disable.&nbsp; By the way, the most common&nbsp;way for the ASP.NET extensionless URL feature to fail is for it to leave "/eurl.axd/GUID" on the URL.&nbsp; This happens when our aspnet_filter appends &ldquo;/eurl.axd/GUID&rdquo;, but we fail to find the &ldquo;*.axd&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.&nbsp; As a result, you will receive a 404 response and the URL will have "/eurl.axd/GUID" appended.</p>
<p>Thanks,<br />Thomas</p>]]></content:encoded>
    </item>
    <item>
      <title>How Extensionless URLs Are Handled By ASP.NET v4</title>
      <link>https://docs.microsoft.com/archive/blogs/tmarq/how-extensionless-urls-are-handled-by-asp-net-v4</link>
      <pubDate>Wed, 26 May 2010 11:48:00 GMT</pubDate>
      <dc:creator><![CDATA[Thomas L. Marquardt]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/tmarq/2010/05/26/how-extensionless-urls-are-handled-by-asp-net-v4/</guid>
      <description><![CDATA[ASP.NET v4.0 has a new feature, when hosted on IIS 7, that enables the execution of extensionless...]]></description>
      <content:encoded><![CDATA[<p>ASP.NET v4.0 has a new feature, when hosted on IIS 7, that enables the execution of extensionless URLs.&nbsp; This feature has a dependency on a QFE from IIS that enables extensionless handler mappings.&nbsp; See <a target="_blank" href="https://support.microsoft.com/kb/980368" title="KB 980368">KB 980368</a> for&nbsp;more information and a link to download the fix.</p>
<p>ASP.NET v4.0 adds extensionless handler mappings to&nbsp;IIS's configuration file&nbsp;(applicationHost.config).&nbsp; There are handler mappings for classic mode and integrated mode.&nbsp; The classic mode handler mappings map to aspnet_isapi.dll.&nbsp; The integrated mode handler mappings map to a new type named System.Web.Handlers.TransferRequestHandler.&nbsp; An example of the handler mappings is below:</p>
<div class="code_sample">
<pre>&lt;add<br />&nbsp; name="ExtensionlessUrl-ISAPI-4.0_32bit"<br />&nbsp; path="*."<br />&nbsp; verb="GET,HEAD,POST,DEBUG"<br />&nbsp; modules="IsapiModule"<br />&nbsp; scriptProcessor="%WINDIR%\Microsoft.NET\Framework\v4.0.30319\aspnet_isapi.dll"<br />&nbsp; preCondition="classicMode,runtimeVersionv4.0,bitness32" <br />&nbsp; responseBufferLimit="0" /&gt;</pre>
<pre>&lt;add<br />&nbsp; name="ExtensionlessUrl-ISAPI-4.0_64bit"<br />&nbsp; path="*." <br />&nbsp; verb="GET,HEAD,POST,DEBUG"<br />&nbsp; modules="IsapiModule"&nbsp;&nbsp; <br />&nbsp; scriptProcessor="C:\Windows\Microsoft.NET\Framework64\v4.0.30319\aspnet_isapi.dll"<br />&nbsp; preCondition="classicMode,runtimeVersionv4.0,bitness64"<br />&nbsp; responseBufferLimit="0" /&gt;</pre>
<pre>&lt;add <br />&nbsp; name="ExtensionlessUrl-Integrated-4.0" <br />&nbsp; path="*." verb="GET,HEAD,POST,DEBUG" <br />&nbsp; type="System.Web.Handlers.TransferRequestHandler" <br />&nbsp; preCondition="integratedMode,runtimeVersionv4.0" /&gt;</pre>
</div>
<p>&nbsp;</p>
<p>These handler mappings are installed when v4.0 is installed, but without the IIS 7 QFE, they are ignored for extensionless URLs.&nbsp; As soon as you install the QFE, extensionless URLs will enter ASP.NET.&nbsp; The QFE will eventually be included in a service pack.</p>
<p>In classic mode,&nbsp;extensionless URLs are first mapped to&nbsp;aspnet_isapi.dll by IIS, and then using the &lt;httpHandlers&gt; configuration section, ASP.NET maps them to the System.Web.DefaultHttpHandler.&nbsp; By default, the&nbsp;root web.config file contains a path="*" mapping to DefaultHttpHandler.&nbsp; In integrated mode they are mapped to TransferRequestHandler.&nbsp; DefaultHttpHandler and TransferRequestHandler are similar in what they do.&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.&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.</p>
<p>In order to do something interesting with extensionless URLs, you must remap the handler before the <a target="_blank" href="https://msdn.microsoft.com/en-us/library/system.web.httpapplication.maprequesthandler.aspx" title="MapRequestHandler">MapRequestHandler</a> event.&nbsp; To do this, you can call <a target="_blank" href="https://msdn.microsoft.com/en-us/library/system.web.httpcontext.remaphandler.aspx" title="HttpContext.RemapHandler">HttpContext.RemapHandler</a> and pass in an instance of IHttpHandler.&nbsp; At the lowest level, this is how you programmatically remap the handler.&nbsp; Alternatively use can a feature like <a target="_blank" href="https://msdn.microsoft.com/en-us/library/cc668201.aspx" title="URL Routing">URL Routing</a>&nbsp;to remap the handler.&nbsp;&nbsp;URL Routing&nbsp;allows you to define routes that send&nbsp;a URL to a specific handler.&nbsp; It simply calls RemapHandler for you.</p>]]></content:encoded>
    </item>
    <item>
      <title>Performing Asynchronous Work, or Tasks, in ASP.NET Applications</title>
      <link>https://docs.microsoft.com/archive/blogs/tmarq/performing-asynchronous-work-or-tasks-in-asp-net-applications</link>
      <pubDate>Wed, 14 Apr 2010 07:27:00 GMT</pubDate>
      <dc:creator><![CDATA[Thomas L. Marquardt]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/tmarq/2010/04/14/performing-asynchronous-work-or-tasks-in-asp-net-applications/</guid>
      <description><![CDATA[In this post I hope to clear up some misconceptions about the use of threads in ASP.NET applications...]]></description>
      <content:encoded><![CDATA[<p class="MsoNormal"><span style="font-family: Calibri; font-size: small;">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.<span style="mso-spacerun: yes">&nbsp; </span>If you&rsquo;re in a hurry and don&rsquo;t want to read the rest of this, then I suggest that you use </span><a href="https://msdn.microsoft.com/en-us/library/system.web.ui.pageasynctask.aspx"><span style="font-family: Calibri; font-size: small;">PageAsyncTask</span></a><span style="font-family: Calibri; font-size: small;"> to execute work asynchronously, and enjoy the rest of your day.</span></p>
<p>&nbsp;</p>
<p class="MsoNormal"><span style="font-family: Calibri; font-size: small;">I explained how ASP.NET and IIS use threads in an earlier post titled </span><a href="https://blogs.msdn.com/tmarq/archive/2007/07/21/asp-net-thread-usage-on-iis-7-0-and-6-0.aspx"><span style="font-family: Calibri; font-size: small;">&ldquo;ASP.NET Thread Usage on IIS 7.0 and 6.0&rdquo;</span></a><span style="font-family: Calibri; font-size: small;">.<span style="mso-spacerun: yes">&nbsp; </span>If you haven't read that, please do, but just so you're not left wondering,&nbsp;I will summarize the "flow" of an ASP.NET&nbsp;request again here:&nbsp; New requests are received by HTTP.sys, a kernel driver.<span style="mso-spacerun: yes">&nbsp; </span>HTTP.sys posts the request to an I/O completion port on which IIS listens.<span style="mso-spacerun: yes">&nbsp; </span>IIS picks up the request on one of its thread pool threads and calls into ASP.NET where ASP.NET immediately&nbsp;posts the request to the CLR ThreadPool and returns a pending status to IIS.&nbsp; Next the request is typically executed, although it can be placed in a queue until there are enough resources to execute it.&nbsp;&nbsp;To execute it, we raise all of the pipeline events and the modules and handlers in the pipeline work on the request,&nbsp;typically&nbsp;while remaining on the same thread, but they can alternatively handle these&nbsp;events asynchronously.&nbsp; When the last pipeline event completes, the response bytes are sent to the client asynchronously during our&nbsp;final send, and then the request is done and all the context for that request is released.</span></p>
<p>&nbsp;</p>
<p class="MsoNormal"><span style="font-family: Calibri; font-size: small;">In case you're curious, the IIS thread pool has a maximum thread count of 256.<span style="mso-spacerun: yes">&nbsp; </span>This thread pool is designed in such a way that it does not handle long running tasks well.<span style="mso-spacerun: yes">&nbsp; </span>The recommendation from the IIS team is to switch to another thread if you&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.<span style="mso-spacerun: yes">&nbsp; </span>Otherwise you will tie up IIS threads and prevent IIS from picking up completions from HTTP.sys.<span style="mso-spacerun: yes">&nbsp; </span><span style="mso-spacerun: yes">&nbsp;</span>So for this reason, ASP.NET always returns a pending status to IIS, and calls </span><a href="https://msdn.microsoft.com/en-us/library/kbf0f1ct.aspx"><span style="font-family: Calibri; font-size: small;">QueueUserWorkItem</span></a><span style="font-family: Calibri; font-size: small;"> to post the request to the CLR ThreadPool.<span style="mso-spacerun: yes">&nbsp; </span><span style="mso-spacerun: yes">&nbsp;</span>In v2.0, 3.5, and 4.0, ASP.NET initializes the CLR ThreadPool with 100 threads per processor (that&rsquo;s the default, this is configurable).<span style="mso-spacerun: yes">&nbsp; </span>So on a dual-core server, there will be a maximum of 200 threads in the pool.<span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>When a CLR ThreadPool thread becomes available (typically happens immediately), the request is picked up by ASP.NET.<span style="mso-spacerun: yes">&nbsp; </span>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).<span style="mso-spacerun: yes">&nbsp; </span>If all the modules and handlers in the pipeline are synchronous, the request will execute on a single CLR ThreadPool thread.<span style="mso-spacerun: yes">&nbsp; </span>If one or more modules or the handler are asynchronous, the request will not execute on a single thread.<span style="mso-spacerun: yes">&nbsp; </span>This is where your code comes into play, but first I have a little more information for you.</span></p>
<p>&nbsp;</p>
<p class="MsoNormal"><span style="font-family: Calibri; font-size: small;">If you read the paragraphs above, you&nbsp;probably noticed that a lot of thread switches take place in order to execute an ASP.NET request.<span style="mso-spacerun: yes">&nbsp; </span>There are always at least&nbsp;3 thread switches.<span style="mso-spacerun: yes">&nbsp; </span>The first is primarily a transition from kernel mode (HTTP.sys) to user mode (IIS).<span style="mso-spacerun: yes">&nbsp; </span>It also frees up HTTP.sys to pick up more incoming requests and hand them off to their respective listeners&mdash;not always IIS.<span style="mso-spacerun: yes">&nbsp; </span>If we tried to execute the entire request on that thread, HTTP.sys wouldn&rsquo;t be resilient to poorly performing listeners&mdash;in fact, a single process could shutdown HTTP on the entire server if&nbsp;that process&nbsp;were to deadlock and hold the existing request threads as well as any new incoming request threads.<span style="mso-spacerun: yes">&nbsp; </span>At the same time, there is a penalty paid for any thread switch.<span style="mso-spacerun: yes">&nbsp; </span>It&rsquo;s called a context switch, and they&rsquo;re expensive.<span style="mso-spacerun: yes">&nbsp; </span>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.<span style="mso-spacerun: yes">&nbsp; </span>The second thread switch is the one from IIS to ASP.NET, where ASP.NET calls </span><a href="https://msdn.microsoft.com/en-us/library/kbf0f1ct.aspx"><span style="font-family: Calibri; font-size: small;">QueueUserWorkItem</span></a><span style="font-family: Calibri; font-size: small;"> for the CLR ThreadPool.<span style="mso-spacerun: yes">&nbsp; </span>This one is not as critical.<span style="mso-spacerun: yes">&nbsp; </span>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.<span style="mso-spacerun: yes">&nbsp;&nbsp;</span>The thread switch&nbsp;for dynamic requests helps improve CPU cache locality for static requests (served by the IIS&nbsp;static file handler)&nbsp;which don't peform a thread switch and instead just execute until completion on the IIS thread.<span style="mso-spacerun: yes">&nbsp; Your specific workload may be quite different&mdash;the </span>context switch&nbsp;may be more expensive than any performance benefit from performing the thread switch.<span style="mso-spacerun: yes">&nbsp; </span>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&mdash;not always ASP.NET.<span style="mso-spacerun: yes">&nbsp; </span>This is similar to what we described in the Http.sys case.<span style="mso-spacerun: yes">&nbsp; </span>We decided it was better to perform the thread switch than not, and so that's what we do.<span style="mso-spacerun: yes">&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.&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.&nbsp; </span>Ok, back to your code..., lets discuss your code now.</span></p>
<p>&nbsp;</p>
<p class="MsoNormal"><span style="font-family: Calibri; font-size: small;">So, in your ASP.NET application, when should you perform work asynchronously instead of synchronously?<span style="mso-spacerun: yes">&nbsp; </span>Well, only 1 thread per CPU can execute at a time.&nbsp;&nbsp;Did&nbsp;you catch that?&nbsp;&nbsp;A lot of people seem to miss this point...only one thread executes at a time&nbsp;on a CPU.<span style="mso-spacerun: yes">&nbsp; </span>When you have more than this, you pay an expensive penalty--a context switch. <span style="mso-spacerun: yes">&nbsp;</span>However, if a thread is blocked waiting on work...then it makes sense to switch to another thread, one that can execute now.&nbsp; It also makes sense to&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.<span style="mso-spacerun: yes">&nbsp; </span>Pop quiz:<span style="mso-spacerun: yes">&nbsp; </span>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? <span style="mso-spacerun: yes">&nbsp;</span>No! The current thread is efficiently using the CPU, so switching will only incur the cost of a context switch.<span style="mso-spacerun: yes">&nbsp; </span>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.<span style="mso-spacerun: yes">&nbsp; </span>Again, it only makes sense to switch threads if the benefit from doing so out weights the cost of the switch.</span></p>
<p>&nbsp;</p>
<p class="MsoNormal"><span style="font-family: Calibri; font-size: small;">Ok, so you have a good reason to perform some work asynchronously, how should you do it?<span style="mso-spacerun: yes">&nbsp; </span>First of all, all of the code that you are able to run <b style="mso-bidi-font-weight: normal">during the execution of a request</b> must run within a module or handler.<span style="mso-spacerun: yes">&nbsp; </span>There is no other option.<span style="mso-spacerun: yes">&nbsp;&nbsp; </span>If you want work to be performed asynchronously&mdash;truly asynchronously, as in the current thread unwinds and execution of the request resumes only if and when your work completes&mdash;then you must run inside a module or handler that is asynchronous. <span style="mso-spacerun: yes">&nbsp;&nbsp;</span>If you don&rsquo;t want to implement your own asynchronous module or handler, you&rsquo;re in luck, because ASP.NET 2.0 introduced </span><a href="https://msdn.microsoft.com/en-us/magazine/cc163725.aspx"><span style="font-family: Calibri; font-size: small;">async pages</span></a><span style="font-family: Calibri; font-size: small;"> , a feature which builds upon </span><a href="https://msdn.microsoft.com/en-us/library/system.web.ihttpasynchandler.aspx"><span style="font-family: Calibri; font-size: small;">IHttpAsyncHandler</span></a><span style="font-family: Calibri; font-size: small;"> and makes it easy to run asynchronous tasks known as </span><a href="https://msdn.microsoft.com/en-us/library/system.web.ui.pageasynctask.aspx"><span style="font-family: Calibri; font-size: small;">PageAsyncTasks</span></a><span style="font-family: Calibri; font-size: small;">.<span style="mso-spacerun: yes">&nbsp; </span>There is a nice introduction to asynchronous pages in </span><a href="https://blogs.msdn.com/dmitryr/archive/2005/07/05/435647.aspx"><span style="font-family: Calibri; font-size: small;">Dmitry&rsquo;s TechEd presentation</span></a><span style="font-family: Calibri; font-size: small;">.<span style="mso-spacerun: yes">&nbsp; </span>I would think most ASP.NET developers would prefer to use async pages with one or more PageAsyncTasks in order to perform work asynchronously.<span style="mso-spacerun: yes">&nbsp; </span>So I encourage you to read Dmitry&rsquo;s TechEd presentation, and focus on </span><a href="https://blogs.msdn.com/dmitryr/attachment/435647.ashx"><span style="font-family: Calibri; color: #0000ff; font-size: small;">slides 31 and 32</span></a><span style="font-family: Calibri; font-size: small;">.</span></p>
<p>&nbsp;</p>
<p class="MsoNormal"><span style="font-family: Calibri; font-size: small;">If instead you would prefer to write your own asynchronous module or handler, I&rsquo;ve included sample code for an asynchronous IHttpModule below along with the same code for a synchronous version of that IHttpModule.<span style="mso-spacerun: yes">&nbsp; </span>Implementing an IHttpAsyncHandler is very similar, so I didn't bother to provide an example.</span></p>
<p>&nbsp;</p>
<p>"AsyncModule.cs":</p>
<div class="code_sample">
<pre>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<br />    //  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);
            }
        }
    }
}</pre>
</div>
<p>&nbsp;</p>
<p>"SyncModule.cs":</p>
<div class="code_sample">
<pre>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");
    }
}</pre>
</div>
<p>&nbsp;</p>
<p class="MsoNormal"><b style="mso-bidi-font-weight: normal"><span style="font-size: small;"><span style="font-family: Calibri;">FAQ<o:p></o:p></span></span></b></p>
<p>&nbsp;</p>
<p class="MsoNormal"><span style="font-size: small;"><span style="font-family: Calibri;"><b style="mso-bidi-font-weight: normal">Q1) <span style="mso-spacerun: yes">&nbsp;</span></b>How many thread pools are there?</span></span></p>
<p>&nbsp;</p>
<p class="MsoNormal"><span style="font-family: Calibri; font-size: small;">A1)<span style="mso-spacerun: yes">&nbsp; </span>There is only one managed thread pool:<span style="mso-spacerun: yes">&nbsp; </span>the CLR </span><a href="https://msdn.microsoft.com/en-us/library/system.threading.threadpool.aspx"><span style="font-family: Calibri; font-size: small;">ThreadPool</span></a><span style="font-family: Calibri; font-size: small;">.<span style="mso-spacerun: yes">&nbsp; </span>This is the thread pool that is used by the .NET Framework.<span style="mso-spacerun: yes">&nbsp; </span>However, using Win32 APIs you are free to implement as many thread pools as you like.<span style="mso-spacerun: yes">&nbsp; </span>Almost nobody does this because it is very difficult to get it right.</span></p>
<p>&nbsp;</p>
<p class="MsoNormal"><span style="font-size: small;"><span style="font-family: Calibri;"><b style="mso-bidi-font-weight: normal">Q2) </b><span style="mso-spacerun: yes">&nbsp;</span>When should I perform work asynchronously?</span></span></p>
<p>&nbsp;</p>
<p class="MsoNormal"><span style="font-family: Calibri; font-size: small;">A2)<span style="mso-spacerun: yes">&nbsp; </span>When the benefit of switching threads out weights the cost of the context switch.<span style="mso-spacerun: yes">&nbsp; </span>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.&nbsp; This is an oversimplification, but I'm trying to make the decision simple.&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.&nbsp; That way the ASP.NET request thread is not blocked doing nothing while you wait for the web service request to complete.</span></p>
<p>&nbsp;</p>
<p class="MsoNormal"><span style="font-size: small;"><span style="font-family: Calibri;"><b style="mso-bidi-font-weight: normal">Q3) </b><span style="mso-spacerun: yes">&nbsp;</span>If my ASP.NET Application uses CLR </span></span><a href="https://msdn.microsoft.com/en-us/library/system.threading.threadpool.aspx"><span style="font-family: Calibri; font-size: small;">ThreadPool</span></a><span style="font-family: Calibri; font-size: small;"> threads, won&rsquo;t I starve ASP.NET, which also uses the CLR ThreadPool to execute requests?</span></p>
<p>&nbsp;</p>
<p class="MsoNormal"><span style="font-family: Calibri; font-size: small;">A3) <span style="mso-spacerun: yes">&nbsp;</span>Think about it this way:<span style="mso-spacerun: yes">&nbsp; </span>if you stay on the same thread, you&rsquo;re using one of ASP.NET&rsquo;s threads.<span style="mso-spacerun: yes">&nbsp; </span>If you switch to another ThreadPool thread and let ASP.NET&rsquo;s thread unwind, you&rsquo;re still only using one thread.<span style="mso-spacerun: yes">&nbsp; </span>While it is true that ASP.NET in classic mode will queue requests if there are more than 12 <b style="mso-bidi-font-weight: normal">threads per CPU</b> currently in-use (by default, see </span><a href="https://support.microsoft.com/kb/821268"><span style="font-family: Calibri; font-size: small;">KB 821268</span></a><span style="font-family: Calibri; font-size: small;">), for most scenarios you don&rsquo;t want more than 12 threads to be executing at any given time.<span style="mso-spacerun: yes">&nbsp; </span>When you&rsquo;re doing asynchronous work, you&rsquo;re not always using a thread.<span style="mso-spacerun: yes">&nbsp; </span>For example, if you made an async web service request, your client will not be using any threads between the &ldquo;send&rdquo; and &ldquo;receive&rdquo;.<span style="mso-spacerun: yes">&nbsp; </span>You unwind after the &ldquo;send&rdquo;, and the &ldquo;receive&rdquo; occurs on an I/O completion port, at which time your callback is invoked and you will then be using a thread again.<span style="mso-spacerun: yes">&nbsp; </span>(Note that for this scenario your callback is executed on an i/o thread&mdash;ASP.NET only uses worker threads, and only includes worker threads when it counts the threads in-use.)<span style="mso-spacerun: yes">&nbsp; </span>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.<span style="mso-spacerun: yes">&nbsp; </span>IIS 7 integrated mode is different, in that ASP.NET does not limit the number of threads in-use by default&mdash;but you do need to set MaxConcurrentRequestsPerCPU = 5000 in v2.0/3.5, as I mentioned in another post.<span style="mso-spacerun: yes">&nbsp; </span>So to summarize, don&rsquo;t worry about starving ASP.NET of threads, and if you think there&rsquo;s a problem here let me know and we&rsquo;ll take care of it.</span></p>
<p>&nbsp;</p>
<p class="MsoNormal"><span style="font-size: small;"><span style="font-family: Calibri;"><b style="mso-bidi-font-weight: normal">Q4)</b> Should I create my own threads (new </span></span><a href="https://msdn.microsoft.com/en-us/library/system.threading.thread.aspx"><span style="font-family: Calibri; font-size: small;">Thread</span></a><span style="font-family: Calibri; font-size: small;">)?<span style="mso-spacerun: yes">&nbsp; </span>Won&rsquo;t this be better for ASP.NET, since it uses the CLR </span><a href="https://msdn.microsoft.com/en-us/library/system.threading.threadpool.aspx"><span style="font-family: Calibri; font-size: small;">ThreadPool</span></a><span style="font-family: Calibri; font-size: small;">.</span></p>
<p>&nbsp;</p>
<p class="MsoNormal"><span style="font-size: small;"><span style="font-family: Calibri;">A4) <span style="mso-spacerun: yes">&nbsp;</span>Please don&rsquo;t.<span style="mso-spacerun: yes">&nbsp; </span>Or to put it a different way, no!!!<span style="mso-spacerun: yes">&nbsp; </span>If you&rsquo;re really smart&mdash;much smarter than me&mdash;then you can create your own threads; otherwise, don&rsquo;t even think about it.<span style="mso-spacerun: yes">&nbsp; </span>Here are some reasons why you should not frequently create new threads: <span style="mso-spacerun: yes">&nbsp;</span></span></span></p>
<p>&nbsp;</p>
<p class="MsoNormal"><span style="font-size: small;"><span style="font-family: Calibri;">1) <span style="mso-spacerun: yes">&nbsp;</span>It is&nbsp;very expensive, compared to QueueUserWorkItem.<span style="mso-spacerun: yes">&nbsp; </span></span></span></p>
<p>&nbsp;</p>
<p class="MsoNormal"><span style="font-family: Calibri; font-size: small;">2) Before you allow your thread to exit, you must </span><a href="https://msdn.microsoft.com/en-us/library/ms683234(VS.85).aspx"><span style="font-family: Calibri; font-size: small;">check for pending I/O</span></a><span style="font-family: Calibri; font-size: small;"> because your new thread might call into a .NET Framework API that initiates an asynchronous operation, and there&rsquo;s really no way for you to know whether or not this happened unless you don&rsquo;t call into framework APIs. </span></p>
<p>&nbsp;</p>
<p class="MsoNormal"><span style="font-family: Calibri; font-size: small;">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.<span style="mso-spacerun: yes">&nbsp; </span>By the way, if you can write a better ThreadPool than the CLR&rsquo;s, I encourage you to apply for a job at Microsoft, because we&rsquo;re definitely looking for people like you!</span></p>
<p>&nbsp;</p>
<p class="MsoNormal"><span style="font-size: small;"><span style="font-family: Calibri;"><b style="mso-bidi-font-weight: normal">Q5)</b> What if I want to perform work in the background, independent of any requests?</span></span></p>
<p>&nbsp;</p>
<p class="MsoNormal"><span style="font-family: Calibri; font-size: small;">A5) Do you really need it to be independent of a request?<span style="mso-spacerun: yes">&nbsp; </span>It really doesn&rsquo;t matter how long it runs, it can still be run in the context of a request.<span style="mso-spacerun: yes">&nbsp; </span>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&rsquo;re doing.<span style="mso-spacerun: yes">&nbsp; </span>In v4.0 ASP.NET, we won&rsquo;t unload the AppDomain until all requests have completed.<span style="mso-spacerun: yes">&nbsp; </span>In v2.0/3.5 ASP.NET in IIS 7 integrated mode, we also won&rsquo;t unload the AppDomain until all the requests have completed.<span style="mso-spacerun: yes">&nbsp; </span>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.<span style="mso-spacerun: yes">&nbsp; </span>To work around this, you&rsquo;d want the shutdownTimeout to be sufficiently long.</span></p>
<p>&nbsp;</p>
<p class="MsoNormal"><span style="font-family: Calibri; font-size: small;">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. <span style="mso-spacerun: yes">&nbsp;</span>There are three ways to do this: </span></p>
<p class="MsoListParagraphCxSpFirst"><span style="mso-bidi-font-family: Calibri; mso-bidi-theme-font: minor-latin"><span style="mso-list: Ignore"><span style="font-family: Calibri; font-size: small;">1)</span><span style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span style="font-size: small;"><span style="font-family: Calibri;">Use the <a href="https://msdn.microsoft.com/en-us/library/ms178473.aspx">ApplicationEnd</a> event.</span></span></p>
<p class="MsoListParagraphCxSpFirst"><span style="font-size: small;"><span style="font-family: Calibri;">2)&nbsp;&nbsp; T</span><span style="font-family: Calibri;">here is an </span></span><a href="https://msdn.microsoft.com/en-us/library/system.appdomain.domainunload.aspx"><span style="font-family: Calibri; font-size: small;">AppDomain.DomainUnload</span></a><span style="font-family: Calibri; font-size: small;"> event which is fired immediately before the AppDomain is unloaded.</span></p>
<p class="MsoListParagraphCxSpLast"><span style="mso-bidi-font-family: Calibri; mso-bidi-theme-font: minor-latin"><span style="mso-list: Ignore"><span style="font-family: Calibri; font-size: small;">3)</span><span style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span style="font-family: Calibri; font-size: small;">You can create an object that implements the </span><a href="https://msdn.microsoft.com/en-us/library/system.web.hosting.iregisteredobject.aspx"><span style="font-family: Calibri; font-size: small;">IRegisteredObject</span></a><span style="font-family: Calibri; font-size: small;"> interface and call </span><a href="https://msdn.microsoft.com/en-us/library/system.web.hosting.hostingenvironment.registerobject.aspx"><span style="font-family: Calibri; font-size: small;">HostingEnvironment.RegisterObject</span></a><span style="font-family: Calibri; font-size: small;"> to &ldquo;register&rdquo; it with ASP.NET.<span style="mso-spacerun: yes">&nbsp; </span>When the AppDomain is about to be unloaded, we will call your implementation of IRegisteredObject.Stop(bool immediate).<span style="mso-spacerun: yes">&nbsp; </span>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.<span style="mso-spacerun: yes">&nbsp; </span>You are supposed to call </span><a href="https://msdn.microsoft.com/en-us/library/system.web.hosting.hostingenvironment.unregisterobject.aspx"><span style="font-family: Calibri; font-size: small;">HostingEnvironment.UnregisterObject</span></a><span style="font-family: Calibri; font-size: small;"> as soon as your registered object has stopped, so ASP.NET knows it doesn&rsquo;t need to call your Stop method.<span style="mso-spacerun: yes">&nbsp; </span>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&rsquo;s your final chance and if you&rsquo;re still running after that you will be rudely aborted.<span style="mso-spacerun: yes">&nbsp; </span>This functionality was not implemented specifically for the ability to execute long running tasks independent of any requests, but it will enable<span style="mso-spacerun: yes">&nbsp; </span>you to do this and safely shutdown/complete your work.<span style="mso-spacerun: yes">&nbsp; </span>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.<span style="mso-spacerun: yes">&nbsp; </span>If you need to, you can hold up the unload as long as you like, because we won&rsquo;t unload until your Stop method returns the second time.<span style="mso-spacerun: yes">&nbsp; </span>However, you should be well behaved, and return in a reasonable amount of time, and preferably immediately the first time Stop is called.</span></p>
<p>&nbsp;</p>
<p class="MsoNormal"><o:p><span style="font-family: Calibri; font-size: small;"><strong>Q6)</strong> You didn't answer my question.</span></o:p></p>
<p>&nbsp;</p>
<p class="MsoNormal"><o:p><span style="font-family: Calibri; font-size: small;">A6) To be fair, I don't know what your question is.&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.</span></o:p></p>
<p>&nbsp;</p>
<p class="MsoNormal"><o:p><span style="font-family: Calibri; font-size: small;">Thank you,<br /></span></o:p><o:p><span style="font-family: Calibri; font-size: small;">Thomas</span></o:p></p>]]></content:encoded>
    </item>
    <item>
      <title>How ASP.NET MVC Routing Works and its Impact on the Performance of Static Requests</title>
      <link>https://docs.microsoft.com/archive/blogs/tmarq/how-asp-net-mvc-routing-works-and-its-impact-on-the-performance-of-static-requests</link>
      <pubDate>Thu, 01 Apr 2010 10:12:00 GMT</pubDate>
      <dc:creator><![CDATA[Thomas L. Marquardt]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/tmarq/2010/04/01/how-asp-net-mvc-routing-works-and-its-impact-on-the-performance-of-static-requests/</guid>
      <description><![CDATA[Recently a number of people have asked how MVC and ASP.NET routing impacts the performance of static...]]></description>
      <content:encoded><![CDATA[<p><span style="FONT-FAMILY: 'Arial','sans-serif'; FONT-SIZE: 10pt">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).&nbsp; I'll answer that question below while explaining how&nbsp;routing is implemented in the ASP.NET pipeline on IIS 6 and IIS 7.&nbsp; This applies to both ASP.NET 3.5 and 4.0, unless otherwise stated.&nbsp; I'll also talk about the new extensionless URL routing feature in ASP.NET 4.0.</span></p>
<h3>How ASP.NET Routing Works</h3>
<p><span style="FONT-FAMILY: 'Arial','sans-serif'; FONT-SIZE: 10pt">URLs are mapped to handlers by IIS when the request is initially received.&nbsp; I'll refer to the ASP.NET handlers as managed handlers, and everything else as&nbsp;unmanaged handlers.&nbsp; A request with a managed handler will enter ASP.NET and be processed by the various managed modules in the pipeline.&nbsp; On IIS 6,&nbsp;a request with an unmanaged handler won't be processed by managed code.&nbsp; On IIS 7, a request with an unmanaged handler, also, won't be processed by managed code, at least not by default.<o:p></o:p></span></p>
<p><span style="FONT-FAMILY: 'Arial','sans-serif'; FONT-SIZE: 10pt">The first step to make routing work for requests with unmanaged handlers is to direct these requests into managed code.&nbsp; On IIS 6 this is done with a wildcard mapping to aspnet_isapi.dll.&nbsp; On IIS 7&nbsp;this is done either by setting <strong><span style="FONT-FAMILY: 'Arial','sans-serif'">runAllManagedModulesForAllRequests=&rdquo;true&rdquo;</span></strong> or removing the <strong><span style="FONT-FAMILY: 'Arial','sans-serif'">"managedHandler" preCondition</span></strong> for the <strong><span style="FONT-FAMILY: 'Arial','sans-serif'">UrlRoutingModule</span></strong>.&nbsp; Ok, so now the requests are coming into managed code, what happens next?&nbsp; Well, the <strong><span style="FONT-FAMILY: 'Arial','sans-serif'">UrlRoutingModule</span></strong> inspects the requests and will change the handler mapping according to the route table.&nbsp; If it doesn't change the handler mapping, then the original handler mapping will be used--the one set by IIS.&nbsp; It's actually a bit more complicated on IIS 6 because of the wildcard mapping.&nbsp; On IIS 6, if the <strong><span style="FONT-FAMILY: 'Arial','sans-serif'">UrlRoutingModule</span></strong> doesn't change the handler,&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.&nbsp; Make sense?</span></p>
<h3>Impact on Performance of Static Requests</h3>
<p><span style="FONT-FAMILY: 'Arial','sans-serif'; FONT-SIZE: 10pt">Okay, so what about the performance of static requests (HTML, JPG, GIF, CSS, JS, etc)?&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.&nbsp;&nbsp;Note that I said *approximately*, because the actual result will depend upon hardware, software, the size of the file, etc.&nbsp; Fortunately kernel caching comes to the rescue on IIS 7.&nbsp; If you have an application that uses routing in such a way that *all* requests enter managed code, as long as you don&rsquo;t change the handler for static requests, they will be kernel cached on IIS 7&nbsp;(if they&rsquo;re hot, and meet kernel cache requirements).&nbsp; </span><span style="FONT-FAMILY: 'Arial','sans-serif'; FONT-SIZE: 10pt">What about kernel caching on IIS 6?&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.</span></p>
<p><span style="FONT-FAMILY: 'Arial','sans-serif'; FONT-SIZE: 10pt"><span style="FONT-FAMILY: 'Arial','sans-serif'; FONT-SIZE: 10pt">So does that mean I should put my static&nbsp;content in an application that isn&rsquo;t using routing,&nbsp;if I'm concerned about the performance of static requests?&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.&nbsp; On IIS 7 it's only the first couple requests to the static file&nbsp;that miss the kernel cache, assuming the request meets kernel caching requirements.&nbsp; However, there is a better&nbsp;way to enable ASP.NET routing&nbsp;on IIS 7, and ASP.NET 4.0 makes use of it by default.&nbsp; Keep reading and I'll also tell you how to enable this on ASP.NET 3.5.</span></span></p>
<h3>ASP.NET 4.0 Enables Routing of Extensionless URLs</h3>
<p><span style="FONT-FAMILY: 'Arial','sans-serif'; FONT-SIZE: 10pt">In ASP.NET v4.0, there is a better&nbsp;way to&nbsp;enable routing.&nbsp; Normally you're only interested in routing extensionless URLs, and have no need to route static requests (HTML, JPG, GIF, CSS, JS, etc).&nbsp; In v4.0 there is a new feature that allows extensionless URLs to be&nbsp;directed into managed code, without&nbsp;impacting static requests (HTML, JPG, GIF, CSS, JS, etc).&nbsp;&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 <strong>runAllManagedModulesForAllRequests=&rdquo;true&rdquo;</strong> or remove the <strong>"managedHandler" precondition</strong> for the <strong>UrlRoutingModule</strong>.&nbsp; It works by default on both IIS 6 and IIS 7, except that&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.&nbsp; Once you obtain the IIS 7 QFE and install v4.0 ASP.NET, you&rsquo;ll be able to route extensionless URLs without impacting static requests.&nbsp; The QFE enables a new &ldquo;*.&rdquo; handler mapping&mdash;the notation may seem weird, but all you care about is the fact that this maps to URLs without an extension.&nbsp; ASP.NET registers a &ldquo;*.&rdquo; handler mapping when v4.0 is installed.&nbsp; If you don&rsquo;t have the IIS 7 QFE, that handler mapping does nothing.&nbsp; If you have the IIS 7 QFE, extensionless URLs are mapped to our handler, which enables&nbsp;them to be routed by the UrlRoutingModule.&nbsp;&nbsp;Information about the IIS 7 QFE and steps to download it can be found at <a href="https://support.microsoft.com/kb/980368">https://support.microsoft.com/kb/980368</a>.&nbsp; For the record, the implementation of this feature on IIS 6 is done quite differently, and I won't go into that here.</span></p>
<p><span style="FONT-FAMILY: 'Arial','sans-serif'; FONT-SIZE: 10pt">Just in case you need to disable the feature, I'll tell you how.&nbsp; </span><span style="FONT-FAMILY: 'Arial','sans-serif'; FONT-SIZE: 10pt">On IIS 6, you can disable the feature by setting a DWORD registry key&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).&nbsp; </span><span style="FONT-FAMILY: 'Arial','sans-serif'; FONT-SIZE: 10pt">On IIS 7, you can disable the feature by removing the "*." handler mappings.&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:&nbsp; "ExtensionlessUrl-ISAPI-4.0_32bit", "ExtensionlessUrl-ISAPI-4.0_64bit", and "ExtensionlessUrl-Integrated-4.0".</span></p>
<h3>How to Enable Extensionless URL Routing on ASP.NET 3.5 for MVC</h3>
<p>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)&nbsp;without&nbsp;setting runAllManagedModulesForAllRequests="true" or configuring a wildcard script handler.&nbsp; To do this, you need one of the following operating systems:&nbsp; Windows Vista SP2, Windows Server 2008 SP2, Windows Server 2008 R2, or Windows 7.&nbsp; And you need to patch it with the hotfix available at <a href="https://support.microsoft.com/kb/980368">https://support.microsoft.com/kb/980368</a>&nbsp;(this hotfix will eventually be rolled into a future service pack, of course).&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.&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.&nbsp; After doing this, extensionless URLs will be directed into managed code, and you can then route them to a different handler.&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.</p>
<p>"web.config":</p>
<div class="code_sample">
<pre>&lt;configuration&gt;<br />&nbsp; &lt;system.webServer&gt;<br />&nbsp;&nbsp;&nbsp; &lt;handlers&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;!-- THESE HANDLER MAPPINGS SHOULD GO BEFORE <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ANY OTHER HANDLER MAPPINGS --&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;add name="EURL-ISAPI-2.0_32bit" <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; path="*." <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; verb="GET,HEAD,POST,DEBUG" <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; modules="IsapiModule"<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; scriptProcessor="%SystemRoot%\Microsoft.NET\Framework\v2.0.50727\aspnet_isapi.dll" <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; preCondition="classicMode,runtimeVersionv2.0,bitness32" <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; responseBufferLimit="0"/&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;add name="EURL-ISAPI-2.0_64bit" <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; path="*." verb="GET,HEAD,POST,DEBUG"<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; modules="IsapiModule" <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; scriptProcessor="%SystemRoot%\Microsoft.NET\Framework64\v2.0.50727\aspnet_isapi.dll" <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; preCondition="classicMode,runtimeVersionv2.0,bitness64" <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; responseBufferLimit="0"/&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;add name="EURL-integrated-2.0" <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; path="*." <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; verb="GET,HEAD,POST,DEBUG" <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; type="MySample.MyTransferRequestHandler" <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; preCondition="integratedMode,runtimeVersionv2.0"/&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;!-- PLACE ADDITIONAL HANDLER MAPPINGS BELOW HERE --&gt;<br />&nbsp;&nbsp;&nbsp; &lt;/handlers&gt;<br />&nbsp;&nbsp;&nbsp; &lt;modules runAllManagedModulesForAllRequests="false"&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;!-- BE CERTAIN TO SET runAllManagedModulesForAllRequests="false" --&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;!-- AND IF YOU ADDED A WILDCARD SCRIPT MAPPING, REMOVE THAT TOO --&gt;<br />&nbsp;&nbsp;&nbsp; &lt;/modules&gt;<br />&nbsp; &lt;/system.webServer&gt;<br />&lt;/configuration&gt;</pre>
</div>
<p>&nbsp;</p>
<p>"App_Code/MyTransferRequestHandler":</p>
<div class="code_sample">
<pre>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: https://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;
            }
        }
    }
}</pre>
</div>
<p>&nbsp;</p>
<p><span style="FONT-FAMILY: 'Arial','sans-serif'; FONT-SIZE: 10pt"><span style="FONT-FAMILY: 'Arial','sans-serif'; FONT-SIZE: 10pt"><o:p><span style="FONT-FAMILY: 'Arial','sans-serif'; FONT-SIZE: 10pt"><span style="FONT-FAMILY: 'Arial','sans-serif'; FONT-SIZE: 10pt">Wondering how the extensionless URL handler mappings work?&nbsp;&nbsp;Wondering why it works for both classic and integrated mode?&nbsp;In classic mode, the "*." handler mapping is to our aspnet_isapi.dll.&nbsp;&nbsp;Extensionless URLs&nbsp;will be mapped to the DefaultHttpHandler in classic mode.&nbsp; The DefaultHttpHandler is listed in the &lt;httpHandlers&gt; section in the root web.config file with path="*", so it catches everything that is not caught by something else.&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.&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.&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.&nbsp; This is what ASP.NET routing does in v4.0--in v3.5, routing essentially does&nbsp;the same, but&nbsp;in a much more round about manner.&nbsp; So that's how the extensionless URL handler works in classic mode.&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.&nbsp; The ISAPI code path actually uses these same APIs, but not directly.</span></span></o:p></span></span></p>
<h3>Troubleshooting</h3>
<p><span style="FONT-FAMILY: 'Arial','sans-serif'; FONT-SIZE: 10pt"><span style="FONT-FAMILY: 'Arial','sans-serif'; FONT-SIZE: 10pt"><o:p><span style="FONT-FAMILY: 'Arial','sans-serif'; FONT-SIZE: 10pt"><span style="FONT-FAMILY: 'Arial','sans-serif'; FONT-SIZE: 10pt"></span></span></o:p></span>Having trouble with the MyTransferRequestHandler shown above?&nbsp; That code only directs extensionless URLs into managed code.&nbsp; You need to use routing or something else in order to handle them.&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&nbsp;a file named MyInterceptorModule.cs within your App_Code directory.&nbsp; The MyInterceptorModule intercepts all requests that enter managed code and displays information about the URL and the values of several server variables.</span></p>
<p>&nbsp;</p>
<p>"web.config additions":</p>
<div class="code_sample">
<pre>&lt;system.web&gt;
    &lt;httpModules&gt;
      &lt;add 
        name="MyInterceptorModule" 
        type="MySample.MyInterceptorModule"/&gt;
    &lt;/httpModules&gt;
  &lt;/system.web&gt;
  &lt;system.webServer&gt;
    &lt;validation validateIntegratedModeConfiguration="false"/&gt;
    &lt;modules runAllManagedModulesForAllRequests="false"&gt;
      &lt;add name="MyInterceptorModule" 
        type="MySample.MyInterceptorModule" 
        preCondition="managedHandler"/&gt;</pre>
<pre>  &lt;/modules&gt;
  &lt;/system.webServer&gt;</pre>
</div>
<p>&nbsp;</p>
<p>"App_Code/MyInterceptor":</p>
<div class="code_sample">
<pre>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("&lt;html&gt;&lt;head&gt;&lt;title&gt;intercepted&lt;/title&gt;&lt;/head&gt;&lt;body&gt;&lt;pre&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("&lt;/pre&gt;&lt;/body&gt;&lt;/html&gt;");
            app.CompleteRequest();
        } 
    }
}</pre>
</div>
<p>&nbsp;</p>
<p><span style="FONT-FAMILY: 'Arial','sans-serif'; FONT-SIZE: 10pt"><o:p><span style="FONT-FAMILY: 'Arial','sans-serif'; FONT-SIZE: 10pt"><span style="FONT-FAMILY: 'Arial','sans-serif'; FONT-SIZE: 10pt"></span></span></o:p></span>&nbsp;</p>
<p><span style="FONT-FAMILY: 'Arial','sans-serif'; FONT-SIZE: 10pt"><o:p><span style="FONT-FAMILY: 'Arial','sans-serif'; FONT-SIZE: 10pt"><span style="FONT-FAMILY: 'Arial','sans-serif'; FONT-SIZE: 10pt">Note that if you're experimenting and want to see what is kernel cached, there are a number of things that prevent&nbsp;a resource from being kernel cached.&nbsp; Be sure to include an Accept-Encoding header, for example most browsers include "Accept-Encoding: gzip,deflate".&nbsp; Also don't&nbsp;issue a conditional GET, for example, make sure your browser is not using "If-Modified-Since".&nbsp;Request the page a couple times in order to kernel cache it.&nbsp; To see if it is served from the kernel cache, start&nbsp;Performance&nbsp;Monitor&nbsp;(Start|Run|type perfmon.msc) and add the following performance counters:&nbsp;"Web Service Cache\Kernel: URI Cache Hits", "Web Service Cache\Kernel: URI Cache Misses", and "Web Service Cache\Kernel: Current URIs Cached".</span></span><span style="FONT-FAMILY: 'Arial','sans-serif'; FONT-SIZE: 10pt"><span style="FONT-FAMILY: 'Arial','sans-serif'; FONT-SIZE: 10pt">&nbsp;</span></span></o:p></span></p>
<p><span style="FONT-FAMILY: 'Arial','sans-serif'; FONT-SIZE: 10pt">Regards,<br />Thomas<o:p></o:p></span></p>
<p class="MsoNormal"><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"><o:p>&nbsp;</o:p></span></p>]]></content:encoded>
    </item>
    <item>
      <title>Correct use of System.Web.HttpResponse.Redirect</title>
      <link>https://docs.microsoft.com/archive/blogs/tmarq/correct-use-of-system-web-httpresponse-redirect</link>
      <pubDate>Thu, 25 Jun 2009 13:10:00 GMT</pubDate>
      <dc:creator><![CDATA[Thomas L. Marquardt]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/tmarq/2009/06/25/correct-use-of-system-web-httpresponse-redirect/</guid>
      <description><![CDATA[Try very, very&nbsp;hard to avoid using Response.Redirect(url), instead, use Response.Redirect(url,...]]></description>
      <content:encoded><![CDATA[<P style="MARGIN: 0in 0in 0pt" class=MsoNormal><SPAN style="FONT-FAMILY: 'Tahoma','sans-serif'; FONT-SIZE: 10pt">Try very, very&nbsp;hard to avoid using Response.Redirect(url), instead, use Response.Redirect(url, <STRONG>false</STRONG>).&nbsp; Response.Redirect(url), after writing a 302 redirect response to the response buffers, calls Response.End.&nbsp; This is very expensive.&nbsp; The alternative, Response.Redirect(url, <STRONG>false</STRONG>) is fast, but unlike Response.Redirect(url), the lines of code which&nbsp;follow the call to Response.Redirect(url, <STRONG>false</STRONG>) will be executed.&nbsp; More on this later, but first, let me tell you about the horrors of Response.End.</SPAN></P>
<P style="MARGIN: 0in 0in 0pt" class=MsoNormal><SPAN style="FONT-FAMILY: 'Tahoma','sans-serif'; FONT-SIZE: 10pt"></SPAN><SPAN style="FONT-FAMILY: 'Tahoma','sans-serif'; FONT-SIZE: 10pt"></SPAN>&nbsp;</P>
<P style="MARGIN: 0in 0in 0pt" class=MsoNormal><SPAN style="FONT-FAMILY: 'Tahoma','sans-serif'; FONT-SIZE: 10pt">Response.End is a terrible method.&nbsp; It was added in ASP.NET 1.0 for compatibility with classic ASP.&nbsp;&nbsp;In classic ASP, this method terminated script processing, so that the lines of script that followed this call never executed.&nbsp; How do you simulate that in managed code?&nbsp; The only way is to abort the thread, which raises a ThreadAbortException and&nbsp;is&nbsp;outrageously expensive.&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().&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.</SPAN></P>
<P style="MARGIN: 0in 0in 0pt" class=MsoNormal><SPAN style="FONT-FAMILY: 'Tahoma','sans-serif'; FONT-SIZE: 10pt"></SPAN>&nbsp;</P>
<P style="MARGIN: 0in 0in 0pt" class=MsoNormal><SPAN style="FONT-FAMILY: 'Tahoma','sans-serif'; FONT-SIZE: 10pt">Ok, so what if&nbsp;you don't want the lines of code to execute after&nbsp;you redirect?&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().&nbsp; It's not the same as aborting the thread, which truly does prevent all subsequent lines of code form running.&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.&nbsp; This is usually all you need.</SPAN></P>
<P style="MARGIN: 0in 0in 0pt" class=MsoNormal><SPAN style="FONT-FAMILY: 'Tahoma','sans-serif'; FONT-SIZE: 10pt"></SPAN>&nbsp;</P>
<P style="MARGIN: 0in 0in 0pt" class=MsoNormal><SPAN style="FONT-FAMILY: 'Tahoma','sans-serif'; FONT-SIZE: 10pt">So to summarize...</SPAN></P>
<P style="MARGIN: 0in 0in 0pt" class=MsoNormal><SPAN style="FONT-FAMILY: 'Tahoma','sans-serif'; FONT-SIZE: 10pt"></SPAN>&nbsp;</P>
<P style="MARGIN: 0in 0in 0pt" class=MsoNormal><SPAN style="FONT-FAMILY: 'Tahoma','sans-serif'; FONT-SIZE: 10pt"><STRONG>BAD</STRONG>:</SPAN></P><SPAN style="FONT-FAMILY: 'Tahoma','sans-serif'; FONT-SIZE: 10pt">
<P style="MARGIN: 0in 0in 0pt 0.5in" class=MsoNormal><FONT size=3 face=Calibri>Response.Redirect(url);</FONT></P>
<P style="MARGIN: 0in 0in 0pt" class=MsoNormal><SPAN style="FONT-FAMILY: 'Tahoma','sans-serif'; FONT-SIZE: 10pt"><?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" /><o:p>&nbsp;</o:p></SPAN></P>
<P style="MARGIN: 0in 0in 0pt" class=MsoNormal><SPAN style="FONT-FAMILY: 'Tahoma','sans-serif'; FONT-SIZE: 10pt"><o:p><STRONG>GOOD</STRONG>:</o:p></SPAN></P><SPAN style="FONT-FAMILY: 'Tahoma','sans-serif'; FONT-SIZE: 10pt"><o:p>
<P style="MARGIN: 0in 0in 0pt 0.5in" class=MsoNormal><FONT size=3 face=Calibri>Response.Redirect(url, <B>false</B>);</FONT></P>
<P style="MARGIN: 0in 0in 0pt 0.5in" class=MsoNormal><FONT size=3 face=Calibri>Context.ApplicationInstance.<B>CompleteRequest()</B>;</FONT></P>
<P style="MARGIN: 0in 0in 0pt" class=MsoNormal><SPAN style="FONT-FAMILY: 'Tahoma','sans-serif'; FONT-SIZE: 10pt"><o:p>&nbsp;</o:p></SPAN></o:p></SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN></P>
<P style="MARGIN: 0in 0in 0pt" class=MsoNormal><SPAN style="FONT-FAMILY: 'Tahoma','sans-serif'; FONT-SIZE: 10pt"></SPAN><SPAN style="FONT-FAMILY: 'Tahoma','sans-serif'; FONT-SIZE: 10pt">But before I put my pen away, there's one more common practice I've seen that people should be aware of .&nbsp; </SPAN><SPAN style="FONT-FAMILY: 'Tahoma','sans-serif'; FONT-SIZE: 10pt">In classic mode, calling <STRONG>Response.Redirect(url)</STRONG> from Application_Error <STRONG>without calling Server.ClearError</STRONG> is doubly expensive.&nbsp; It causes three exceptions to be thrown, and then either raises a ThreadAbortException or does a synchronous flush and completes the response.&nbsp; And in integrated mode, calling Response.Redirect(url) from Application_Error <STRONG>without calling Server.ClearError</STRONG> does not even work.&nbsp; Instead, you should use the following code, which&nbsp;performs&nbsp;well in both classic mode and integrated mode.&nbsp; If you’re going to redirect from Application_Error, you should do it the following way:</SPAN></P>
<P style="MARGIN: 0in 0in 0pt" class=MsoNormal><SPAN style="FONT-FAMILY: 'Tahoma','sans-serif'; FONT-SIZE: 10pt"><o:p></o:p></SPAN>&nbsp;</P>
<P style="MARGIN: 0in 0in 0pt" class=MsoNormal><SPAN style="FONT-FAMILY: 'Tahoma','sans-serif'; FONT-SIZE: 10pt"><SPAN style="FONT-FAMILY: 'Tahoma','sans-serif'; FONT-SIZE: 10pt"><o:p><STRONG>GOOD</STRONG>:</o:p></SPAN>&nbsp;<o:p></o:p></SPAN></P>
<P style="MARGIN: 0in 0in 0pt 0.5in" class=MsoNormal><FONT size=3 face=Calibri>void Application_Error(object sender, EventArgs e)</FONT></P>
<P style="MARGIN: 0in 0in 0pt 0.5in" class=MsoNormal><FONT size=3 face=Calibri>&nbsp;&nbsp;&nbsp; {</FONT></P>
<P style="MARGIN: 0in 0in 0pt 0.5in" class=MsoNormal><FONT size=3 face=Calibri>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Server.<B>ClearError()</B>;</FONT></P>
<P style="MARGIN: 0in 0in 0pt 0.5in" class=MsoNormal><FONT size=3 face=Calibri>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Response.Redirect(url, <B>false</B>);</FONT></P>
<P style="MARGIN: 0in 0in 0pt 0.5in" class=MsoNormal><FONT size=3 face=Calibri>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Context.ApplicationInstance.<B>CompleteRequest()</B>;</FONT></P>
<P style="MARGIN: 0in 0in 0pt 0.5in" class=MsoNormal><FONT size=3 face=Calibri>&nbsp;&nbsp;&nbsp; }</FONT></P>
<P style="MARGIN: 0in 0in 0pt" class=MsoNormal><SPAN style="FONT-FAMILY: 'Tahoma','sans-serif'; FONT-SIZE: 10pt"><o:p>&nbsp;</o:p></SPAN></P>
<P style="MARGIN: 0in 0in 0pt" class=MsoNormal><SPAN style="FONT-FAMILY: 'Tahoma','sans-serif'; FONT-SIZE: 10pt">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.&nbsp; You might notice that with a default 3.5 install, if you remove the ASP.NET ScriptModule from the IIS &lt;modules&gt; section, you're suddenly able to call Redirect from Application_Error without calling Server.ClearError.&nbsp; ScriptModule registers a PreSendRequestHeaders handler.&nbsp; In integrated mode, the PreSendRequestHeaders event is implemented differently.&nbsp; As a result, in integrated mode, the exception will be rendered if you try to redirect without clearing the error from Application_Error.&nbsp; I’ve attached a sample that demonstrates the difference between the two pipelines.&nbsp; This sample will demonstrate the difference regardless of whether or not ScriptModule is installed.&nbsp; Just request default.aspx.&nbsp; Then change the value of demonstratePipelineDifference in global.asax, and request default.aspx again.&nbsp; Do this in both classic mode and integrated mode, and observe the behavior.<o:p></o:p></SPAN></P>
<P style="MARGIN: 0in 0in 0pt" class=MsoNormal><SPAN style="FONT-FAMILY: 'Tahoma','sans-serif'; FONT-SIZE: 10pt"><o:p>&nbsp;</o:p></SPAN></P>
<P style="MARGIN: 0in 0in 0pt" class=MsoNormal><SPAN style="FONT-FAMILY: 'Tahoma','sans-serif'; FONT-SIZE: 10pt">Thanks,<o:p></o:p></SPAN></P>
<P style="MARGIN: 0in 0in 0pt" class=MsoNormal><SPAN style="FONT-FAMILY: 'Tahoma','sans-serif'; FONT-SIZE: 10pt">Thomas<o:p></o:p></SPAN></P>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Components.PostAttachments/00/09/80/39/56/sample.zip" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-components-postattachments/00-09-80-39-56/sample.zip">sample.zip</a></p>]]></content:encoded>
    </item>
    <item>
      <title>Obtaining more information from an HttpException</title>
      <link>https://docs.microsoft.com/archive/blogs/tmarq/obtaining-more-information-from-an-httpexception</link>
      <pubDate>Tue, 10 Mar 2009 16:44:00 GMT</pubDate>
      <dc:creator><![CDATA[Thomas L. Marquardt]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/tmarq/2009/03/10/obtaining-more-information-from-an-httpexception/</guid>
      <description><![CDATA[ASP.NET throws HttpException for numerous types of errors, making it difficult to determine exactly...]]></description>
      <content:encoded><![CDATA[<P>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,&nbsp;and therefore&nbsp;parsing&nbsp;it can be&nbsp;non-trivial.&nbsp; There is, however, a way that you can identify a few specific types of errors.</P>
<P>In v2.0 there is an internal&nbsp;property on HttpException named WebEventCode&nbsp;that returns an int.&nbsp; This value contains additional information which you can use to determine the type of error.&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.&nbsp; Below I've listed the meaning of several of the possible values of this property:</P>
<UL>
<LI>If the HttpException&nbsp;is because the request timed out ("Request timed out."), the value will be&nbsp;&nbsp;System.Web.Management.<STRONG>WebEventCodes.RuntimeErrorRequestAbort</STRONG>.</LI>
<LI>If it's because the client posted too much data ("Maximum request length exceeded."), the value will be <STRONG>WebEventCodes.RuntimeErrorPostTooLarge</STRONG>.</LI>
<LI>If it's&nbsp;caused by a ViewState error, the value will be <STRONG>WebEventCodes.RuntimeErrorViewStateFailure</STRONG>.</LI></UL>
<P>Again, this property has been made public in v4.0, but for v2.0, you'll have to use private reflection to access it.</P>
<P>-Thomas</P>]]></content:encoded>
    </item>
    <item>
      <title>Using IIS 7.0 Dynamic Compression with ASP.NET Output Cache</title>
      <link>https://docs.microsoft.com/archive/blogs/tmarq/using-iis-7-0-dynamic-compression-with-asp-net-output-cache</link>
      <pubDate>Wed, 27 Aug 2008 15:31:00 GMT</pubDate>
      <dc:creator><![CDATA[Thomas L. Marquardt]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/tmarq/2008/08/27/using-iis-7-0-dynamic-compression-with-asp-net-output-cache/</guid>
      <description><![CDATA[This post&nbsp;discusses an efficient way to compress content served by the ASP.NET output...]]></description>
      <content:encoded><![CDATA[<P>This post&nbsp;discusses an efficient way to compress content served by the ASP.NET output cache.&nbsp;&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 <A class="" href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.11" mce_href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.11">Content-Encoding</A> header.&nbsp; And IIS 7.0 has efficient compression algorithms for gzip and deflate.&nbsp; Put the two together, and you can efficiently compress responses once, and serve them from the cache many times.</P>
<P>Here's how you can do this on Windows Server 2008:</P>
<P><STRONG>1.</STRONG>&nbsp; If you haven't already, install the Dynamic Content Compression role for IIS 7.0 using the Server Manager.</P>
<P><STRONG>2.</STRONG>&nbsp; Step 1 will add the <STRONG>&lt;httpCompression&gt;</STRONG> configuration section to applicationHost.config.&nbsp; Note that only gzip compression is supported by default.&nbsp; However, deflate is also implemented in gzip.dll, so if you add the following to the <STRONG>&lt;httpCompression&gt;</STRONG> configuration section, you'll also have deflate enabled:</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <STRONG>&lt;scheme name="deflate" dll="%Windir%\system32\inetsrv\gzip.dll" /&gt;</STRONG> </P>
<P><STRONG>3.</STRONG> Create an application with an ASPX page like the one below.&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.</P>
<BLOCKQUOTE>
<P>&lt;%@ Page language="C#" %&gt;<BR>&lt;%@ OutputCache duration="600" varyByParam="None" varyByContentEncoding=<STRONG>"gzip;deflate"</STRONG> %&gt;</P>
<P>&lt;script runat="server"&gt;<BR>&nbsp;&nbsp;&nbsp; void Page_Load() {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Request.ServerVariables[<STRONG>"IIS_EnableDynamicCompression"</STRONG>] = <STRONG>"1"</STRONG>; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Response.Write(DateTime.Now);<BR>&nbsp;&nbsp;&nbsp; }<BR>&lt;/script&gt;&nbsp;</P></BLOCKQUOTE>
<P><STRONG>4.</STRONG>&nbsp; You must enable the IIS 7.0 "dynamic compression before cache" feature.&nbsp; You can do this by adding the following to your application's web.config file.&nbsp; </P>
<BLOCKQUOTE>
<P>&lt;system.webServer&gt;<BR>&nbsp;&nbsp;&nbsp; &lt;urlCompression doDynamicCompression=<STRONG>"false"</STRONG> dynamicCompressionBeforeCache=<STRONG>"true"</STRONG> /&gt;<BR>&lt;/system.webServer&gt;</P></BLOCKQUOTE>
<P mce_keep="true">Note that in Step 4, doDynamicCompression=<STRONG>"false"</STRONG>.&nbsp; If you set this to <STRONG>"true"</STRONG>, all responses will be compressed.&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.&nbsp;&nbsp;Our example sets doDynamicCompression to <STRONG>"false"</STRONG> in configuration, but then uses the server variable <STRONG>"IIS_EnableDynamicCompression"</STRONG> to enable it for a specific page.&nbsp; Alternatively, you could use configuration and &lt;location path=""&gt; to limit the scope of compression.</P>
<P mce_keep="true">So how does this work?&nbsp; When a request is sent to the server, it may or may not include an <A class="" href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.3" mce_href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.3">Accept-Encoding</A> header, indicating one or more acceptable encodings.&nbsp; For example, Internet Explorer sends "Accept-Encoding: gzip, deflate".&nbsp; When the ASP.NET Output Cache receives the request, if the page is using the VaryByContentEncoding feature,&nbsp;ASP.NET will check if any of the encodings specified by VaryByContentEncoding match the request's Accept-Encoding header.&nbsp; If there is a match, ASP.NET will see if that response is in the cache and serve it.&nbsp; If it's not cached, ASP.NET will render the response and the IIS 7.0 DynamicCompressionModule, which runs during <A class="" href="https://blogs.msdn.com/tmarq/archive/2007/08/30/iis-7-0-asp-net-pipelines-modules-handlers-and-preconditions.aspx" mce_href="https://blogs.msdn.com/tmarq/archive/2007/08/30/iis-7-0-asp-net-pipelines-modules-handlers-and-preconditions.aspx">RQ_RELEASE_REQUEST_STATE</A>, will encode the response according to the request's Accept-Encoding header, and it will set the response's Content-Encoding header.&nbsp; For the example request, the response header will be set to "Content-Encoding: gzip".&nbsp; When the ASP.NET OutputCacheModule runs during <A class="" href="https://blogs.msdn.com/tmarq/archive/2007/08/30/iis-7-0-asp-net-pipelines-modules-handlers-and-preconditions.aspx" mce_href="https://blogs.msdn.com/tmarq/archive/2007/08/30/iis-7-0-asp-net-pipelines-modules-handlers-and-preconditions.aspx">RQ_UPDATE_REQUEST_CACHE</A>, it will check to see if the Content-Encoding header has been set, and&nbsp;if specified in VaryByContentEncoding, it will cache the response according to that encoding.&nbsp; If the Content-Encoding is not set, the response can be cached as the identity.</P>
<P>This ASP.NET Output Cache feature can be used via the &lt;%@ OutputCache varyByContentEncoding= %&gt; page directive, programmatically via HttpCachePolicy.VaryByContentEncodings, or via configuration outputCacheProfiles&gt;&lt;add varyByContentEncoding=””&gt;&lt;/outputCacheProfiles&gt;.&nbsp; </P>
<P>Here are a few of the implementation details:</P>
<UL>
<LI>
<DIV mce_keep="true">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").&nbsp; </DIV></LI>
<LI>
<DIV mce_keep="true">The identity, or plain text, is a special case.&nbsp; You can consider it to be implicitly included in the value of VaryByContentEncoding.&nbsp; E.g., if you specify varyByContentEncoding="gzip", ASP.NET will cache at most two responses, gzip and the identity.</DIV></LI>
<LI>
<DIV mce_keep="true">A response&nbsp;can be cached only if&nbsp;the value of its&nbsp;Content-Encoding header is&nbsp;included in VaryByContentEncoding; except for the identity, which is a special case.&nbsp; If the response does not have a Content-Encoding header, it can be cached as the identity.</DIV></LI>
<LI>
<DIV mce_keep="true">A response will be served from the cache if it&nbsp;has an&nbsp;acceptable encoding according to <A class="" href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.3" mce_href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.3">RFC 2616, Section 14.3</A>&nbsp;(qvalues are supported).&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.</DIV></LI>
<LI>
<DIV mce_keep="true">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). </DIV></LI>
<LI>
<DIV mce_keep="true">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).</DIV></LI>
<LI>
<DIV mce_keep="true">If the request's Accept-Encoding doesn't match any cached responses, and is not listed in VaryByContentEncoding,&nbsp;and the identity is available in the cache, then the response served will be the identity.&nbsp;</DIV></LI></UL>
<P mce_keep="true">Regards,<BR>Thomas</P>]]></content:encoded>
    </item>
    <item>
      <title>ASP.NET Cache can notify you before an entry is removed</title>
      <link>https://docs.microsoft.com/archive/blogs/tmarq/asp-net-cache-can-notify-you-before-an-entry-is-removed</link>
      <pubDate>Tue, 22 Jul 2008 12:32:00 GMT</pubDate>
      <dc:creator><![CDATA[Thomas L. Marquardt]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/tmarq/2008/07/22/asp-net-cache-can-notify-you-before-an-entry-is-removed/</guid>
      <description><![CDATA[.NET Framework v2.0 SP2, which will be included with .NET Framework v3.5 SP1, is scheduled to...]]></description>
      <content:encoded><![CDATA[<P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal"><SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Verdana','sans-serif'">.NET Framework v2.0 SP2, which will be included with .NET Framework v3.5 SP1, is scheduled to release this summer.<SPAN style="mso-spacerun: yes">&nbsp; </SPAN>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.<SPAN style="mso-spacerun: yes">&nbsp; </SPAN>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.<SPAN style="mso-spacerun: yes">&nbsp; </SPAN>Here's the specification:<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" /><o:p></o:p></SPAN></P>
<P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal"><SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Verdana','sans-serif'"><o:p>&nbsp;</o:p></SPAN></P>
<P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal"><SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Verdana','sans-serif'"><o:p>&nbsp;</o:p></SPAN></P>
<P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal"><U><SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Verdana','sans-serif'">A new Cache.Insert overload</SPAN></U><SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Verdana','sans-serif'">:<o:p></o:p></SPAN></P>
<P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal"><SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Verdana','sans-serif'"><o:p>&nbsp;</o:p></SPAN></P>
<P class=MsoNormal style="MARGIN: 0in 0in 0pt 0.5in; LINE-HEIGHT: normal"><SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Courier New'">void Cache.Insert( String key,<o:p></o:p></SPAN></P>
<P class=MsoNormal style="MARGIN: 0in 0in 0pt 0.5in; LINE-HEIGHT: normal"><SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Courier New'"><SPAN style="mso-spacerun: yes">&nbsp;&nbsp; </SPAN><SPAN style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="mso-spacerun: yes">&nbsp;</SPAN><SPAN style="mso-spacerun: yes">&nbsp;</SPAN><SPAN style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN>Object expensiveObject,<o:p></o:p></SPAN></P>
<P class=MsoNormal style="MARGIN: 0in 0in 0pt 0.5in; LINE-HEIGHT: normal"><SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Courier New'"><SPAN style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN><SPAN style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;</SPAN>CacheDependency dependencies,<o:p></o:p></SPAN></P>
<P class=MsoNormal style="MARGIN: 0in 0in 0pt 0.5in; LINE-HEIGHT: normal"><SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Courier New'"><SPAN style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN><SPAN style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;</SPAN>DateTime absoluteExpiration,<o:p></o:p></SPAN></P>
<P class=MsoNormal style="MARGIN: 0in 0in 0pt 0.5in; LINE-HEIGHT: normal"><SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Courier New'"><SPAN style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN><SPAN style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;</SPAN>TimeSpan slidingExpiration,<o:p></o:p></SPAN></P>
<P class=MsoNormal style="MARGIN: 0in 0in 0pt 0.5in; LINE-HEIGHT: normal"><SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Courier New'"><SPAN style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN><SPAN style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;</SPAN>CacheItemUpdateCallback onUpdateCallback <o:p></o:p></SPAN></P>
<P class=MsoNormal style="MARGIN: 0in 0in 0pt 0.5in; LINE-HEIGHT: normal"><SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Courier New'"><SPAN style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>);</SPAN><SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Verdana','sans-serif'"><o:p></o:p></SPAN></P>
<P class=MsoNormal style="MARGIN: 0in 0in 0pt 0.5in; LINE-HEIGHT: normal"><SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Verdana','sans-serif'"><SPAN style="mso-spacerun: yes">&nbsp; </SPAN><o:p></o:p></SPAN></P>
<P class=MsoNormal style="MARGIN: 0in 0in 0pt 0.5in; LINE-HEIGHT: normal"><SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Verdana','sans-serif'">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.<SPAN style="mso-spacerun: yes">&nbsp; </SPAN>Objects inserted into the cache with this method should be expensive to generate.<SPAN style="mso-spacerun: yes">&nbsp; </SPAN>You should not use this method for objects that are inexpensive to generate, because there is overhead in the implementation.<SPAN style="mso-spacerun: yes">&nbsp; </SPAN>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.<SPAN style="mso-spacerun: yes">&nbsp; </SPAN>The stale cache entry will be accessible while the callback is executing and the replacement object is generated.<o:p></o:p></SPAN></P>
<P class=MsoNormal style="MARGIN: 0in 0in 0pt 0.5in; LINE-HEIGHT: normal"><SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Verdana','sans-serif'"><o:p>&nbsp;</o:p></SPAN></P>
<P class=MsoNormal style="MARGIN: 0in 0in 0pt 0.5in; LINE-HEIGHT: normal"><SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Verdana','sans-serif'">Cache entries inserted by this method are not subject to eviction by the cache memory manager.<SPAN style="mso-spacerun: yes">&nbsp; </SPAN>These objects only become invalid when they expire or a dependency changes, and therefore these objects <B style="mso-bidi-font-weight: normal">MUST</B> have some form of invalidation (i.e., it is not possible to insert the entry with a null dependency, NoAbsoluteExpiration, and NoSlidingExpriation).<o:p></o:p></SPAN></P>
<P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal"><SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Verdana','sans-serif'"><o:p>&nbsp;</o:p></SPAN></P>
<P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal"><SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Verdana','sans-serif'"><o:p>&nbsp;</o:p></SPAN></P>
<P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal"><U><SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Verdana','sans-serif'">A new update callback</SPAN></U><SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Verdana','sans-serif'">:<o:p></o:p></SPAN></P>
<P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal"><SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Verdana','sans-serif'"><o:p>&nbsp;</o:p></SPAN></P>
<P class=MsoNormal style="MARGIN: 0in 0in 0pt 0.5in; LINE-HEIGHT: normal"><SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Courier New'">void CacheItemUpdateCallback( String key,<o:p></o:p></SPAN></P>
<P class=MsoNormal style="MARGIN: 0in 0in 0pt 0.5in; LINE-HEIGHT: normal"><SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Courier New'"><SPAN style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>CacheItemUpdateReason reason,<BR><SPAN style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN><SPAN style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN>out Object expensiveObject,<o:p></o:p></SPAN></P>
<P class=MsoNormal style="MARGIN: 0in 0in 0pt 0.5in; LINE-HEIGHT: normal"><SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Courier New'"><SPAN style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>out CacheDependency dependencies,<o:p></o:p></SPAN></P>
<P class=MsoNormal style="MARGIN: 0in 0in 0pt 0.5in; LINE-HEIGHT: normal"><SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Courier New'"><SPAN style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </SPAN><SPAN style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN>out DateTime absoluteExpiration,<o:p></o:p></SPAN></P>
<P class=MsoNormal style="MARGIN: 0in 0in 0pt 0.5in; LINE-HEIGHT: normal"><SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Courier New'"><SPAN style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>out TimeSpan slidingExpiration <o:p></o:p></SPAN></P>
<P class=MsoNormal style="MARGIN: 0in 0in 0pt 0.5in; LINE-HEIGHT: normal"><SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Courier New'"><SPAN style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>);</SPAN><SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Verdana','sans-serif'"> <o:p></o:p></SPAN></P>
<P class=MsoNormal style="MARGIN: 0in 0in 0pt 0.5in; LINE-HEIGHT: normal"><SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Verdana','sans-serif'"><o:p>&nbsp;</o:p></SPAN></P>
<P class=MsoNormal style="MARGIN: 0in 0in 0pt 0.5in; LINE-HEIGHT: normal"><SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Verdana','sans-serif'">When the cache entry becomes invalid, ASP.NET invokes this callback, allowing the application developer to generate a replacement cache entry.<SPAN style="mso-spacerun: yes">&nbsp; </SPAN>The developer is passed only two pieces of information:<SPAN style="mso-spacerun: yes">&nbsp; </SPAN>the key for the cache entry, and an enum value indicating why the entry is invalid.<SPAN style="mso-spacerun: yes">&nbsp; </SPAN>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.<o:p></o:p></SPAN></P>
<P class=MsoNormal style="MARGIN: 0in 0in 0pt 0.5in; LINE-HEIGHT: normal"><SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Verdana','sans-serif'"><SPAN style="mso-spacerun: yes">&nbsp;</SPAN><o:p></o:p></SPAN></P>
<P class=MsoNormal style="MARGIN: 0in 0in 0pt 0.5in; LINE-HEIGHT: normal"><SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Verdana','sans-serif'">Do not modify the existing cache entry.<SPAN style="mso-spacerun: yes">&nbsp; </SPAN>The stale cache entry is still alive and potentially being used by other threads.<SPAN style="mso-spacerun: yes">&nbsp; </SPAN>It is not included in the callback arguments in order to minimize the likelihood of a developer accidentally updating portions of the cache entry.<SPAN style="mso-spacerun: yes">&nbsp; </SPAN>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. <o:p></o:p></SPAN></P>
<P class=MsoNormal style="MARGIN: 0in 0in 0pt 0.5in; LINE-HEIGHT: normal"><SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Verdana','sans-serif'"><o:p>&nbsp;</o:p></SPAN></P>
<P class=MsoNormal style="MARGIN: 0in 0in 0pt 0.5in; LINE-HEIGHT: normal"><SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Verdana','sans-serif'">To remove the cache entry, simply set the argument <I style="mso-bidi-font-style: normal">expensiveObject</I> to null.<SPAN style="mso-spacerun: yes">&nbsp; </SPAN>Otherwise, set it to the newly generated replacement object.<SPAN style="mso-spacerun: yes">&nbsp; </SPAN>The developer <B style="mso-bidi-font-weight: normal">MUST</B> also specify expiration and/or dependency information using the other out parameters.<SPAN style="mso-spacerun: yes">&nbsp; </SPAN>As stated above, the cache entry is not subject to eviction by the cache memory manager, and therefore must have some form of invalidation.<SPAN style="mso-spacerun: yes">&nbsp; </SPAN>It is an error to pass back a non-null value for <I style="mso-bidi-font-style: normal">expensiveObject</I> while setting <I style="mso-bidi-font-style: normal">dependencies</I> to null, <I style="mso-bidi-font-style: normal">absoluteExpiration</I> to NoAbsolteExpiration, and <I style="mso-bidi-font-style: normal">slidingExpriation</I> to NoSlidingExpiration.<SPAN style="mso-spacerun: yes">&nbsp; </SPAN>The cache entry will be removed if this occurs.<o:p></o:p></SPAN></P>
<P class=MsoNormal style="MARGIN: 0in 0in 0pt 0.5in; LINE-HEIGHT: normal"><SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Verdana','sans-serif'"><o:p>&nbsp;</o:p></SPAN></P>
<P class=MsoNormal style="MARGIN: 0in 0in 0pt 0.5in; LINE-HEIGHT: normal"><SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Verdana','sans-serif'">If the developer’s callback throws an exception, ASP.NET will suppress the exception and remove the cache entry.<SPAN style="mso-spacerun: yes">&nbsp; </SPAN>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.<SPAN style="mso-spacerun: yes">&nbsp; </SPAN>The cache entry&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.<o:p></o:p></SPAN></P>
<P class=MsoNormal style="MARGIN: 0in 0in 0pt 0.5in; LINE-HEIGHT: normal"><SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Verdana','sans-serif'"><SPAN style="mso-spacerun: yes">&nbsp;</SPAN><o:p></o:p></SPAN></P>
<P class=MsoNormal style="MARGIN: 0in 0in 0pt 0.5in; LINE-HEIGHT: normal"><SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Verdana','sans-serif'">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).<SPAN style="mso-spacerun: yes">&nbsp; </SPAN>These operations indicate the developer’s intent to remove the item, so the update callback will not be triggered. <o:p></o:p></SPAN></P>
<P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal"><SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Verdana','sans-serif'"><o:p>&nbsp;</o:p></SPAN></P>
<P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal"><SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Verdana','sans-serif'"><o:p>&nbsp;</o:p></SPAN></P>
<P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal"><U><SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Verdana','sans-serif'">A new update reason enumeration</SPAN></U><SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Verdana','sans-serif'">:<o:p></o:p></SPAN></P>
<P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal"><SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Verdana','sans-serif'"><o:p>&nbsp;</o:p></SPAN></P>
<P class=MsoNormal style="MARGIN: 0in 0in 0pt 0.5in; LINE-HEIGHT: normal"><SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Courier New'">public enum CacheItemUpdateReason {<o:p></o:p></SPAN></P>
<P class=MsoNormal style="MARGIN: 0in 0in 0pt 0.5in; LINE-HEIGHT: normal"><SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Courier New'"><SPAN style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </SPAN>// The item was removed from the cache because either <o:p></o:p></SPAN></P>
<P class=MsoNormal style="MARGIN: 0in 0in 0pt 0.5in; LINE-HEIGHT: normal"><SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Courier New'"><SPAN style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </SPAN>// the absolute or sliding expiration interval expired.<o:p></o:p></SPAN></P>
<P class=MsoNormal style="MARGIN: 0in 0in 0pt 0.5in; LINE-HEIGHT: normal"><SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Courier New'"><o:p>&nbsp;</o:p></SPAN></P>
<P class=MsoNormal style="MARGIN: 0in 0in 0pt 0.5in; LINE-HEIGHT: normal"><SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Courier New'"><SPAN style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </SPAN>Expired = 1,<o:p></o:p></SPAN></P>
<P class=MsoNormal style="MARGIN: 0in 0in 0pt 0.5in; LINE-HEIGHT: normal"><SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Courier New'"><o:p>&nbsp;</o:p></SPAN></P>
<P class=MsoNormal style="MARGIN: 0in 0in 0pt 0.5in; LINE-HEIGHT: normal"><SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Courier New'"><SPAN style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </SPAN>// The item was removed from the cache because an <o:p></o:p></SPAN></P>
<P class=MsoNormal style="MARGIN: 0in 0in 0pt 0.5in; LINE-HEIGHT: normal"><SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Courier New'"><SPAN style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </SPAN>// associated cache dependency was changed.<o:p></o:p></SPAN></P>
<P class=MsoNormal style="MARGIN: 0in 0in 0pt 0.5in; LINE-HEIGHT: normal"><SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Courier New'"><o:p>&nbsp;</o:p></SPAN></P>
<P class=MsoNormal style="MARGIN: 0in 0in 0pt 0.5in; LINE-HEIGHT: normal"><SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Courier New'"><SPAN style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </SPAN>DependencyChanged = 2<o:p></o:p></SPAN></P>
<P class=MsoNormal style="MARGIN: 0in 0in 0pt 0.5in; LINE-HEIGHT: normal"><SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Courier New'">}<o:p></o:p></SPAN></P>
<P class=MsoNormal style="MARGIN: 0in 0in 0pt 0.5in; LINE-HEIGHT: normal"><SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Verdana','sans-serif'"><o:p>&nbsp;</o:p></SPAN></P>
<P class=MsoNormal style="MARGIN: 0in 0in 0pt 0.5in; LINE-HEIGHT: normal"><SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Verdana','sans-serif'">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).<o:p></o:p></SPAN></P>
<P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal"><SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Verdana','sans-serif'"><o:p>&nbsp;</o:p></SPAN></P>
<P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal"><SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Verdana','sans-serif'">Regards,</SPAN></P>
<P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal"><SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 'Verdana','sans-serif'">Thomas<o:p></o:p></SPAN></P>]]></content:encoded>
    </item>
    <item>
      <title>If you allocate from the Large Object Heap each time an ASPX page is requested, your performance will be dismal.</title>
      <link>https://docs.microsoft.com/archive/blogs/tmarq/if-you-allocate-from-the-large-object-heap-each-time-an-aspx-page-is-requested-your-performance-will-be-dismal</link>
      <pubDate>Tue, 29 Apr 2008 13:12:00 GMT</pubDate>
      <dc:creator><![CDATA[Thomas L. Marquardt]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/tmarq/2008/04/29/if-you-allocate-from-the-large-object-heap-each-time-an-aspx-page-is-requested-your-performance-will-be-dismal/</guid>
      <description><![CDATA[I've been giving this advice for years internally, and the problem pops up way too often.&nbsp; I'm...]]></description>
      <content:encoded><![CDATA[<P style="MARGIN: 0in 0in 0pt" class=MsoNormal><FONT size=3 face="Times New Roman">I've been giving this advice for years internally, and the problem pops up way too often.&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.&nbsp;&nbsp;If you allocate objects larger than 85000 bytes, they will be allocated in the Large Object Heap (LOH).&nbsp; The LOH is not compacted and collection only occurs after a full collection (so gen 2 must be collected in order to&nbsp;collect an object in the LOH).&nbsp; Short-lived and frequent LOH allocations will kill the performance of your application.&nbsp;&nbsp;</FONT><FONT size=3 face="Times New Roman">As an example, if you allocate from the LOH each time an ASPX page is requested, your performance will be dismal.</FONT></P>
<P style="MARGIN: 0in 0in 0pt" class=MsoNormal><FONT size=3 face="Times New Roman"></FONT>&nbsp;</P>
<P style="MARGIN: 0in 0in 0pt" class=MsoNormal><FONT size=3 face="Times New Roman">There are a few ways to determine if you're allocating from the Large Object Heap (LOH).&nbsp; The easiest is via the performance counter ".NET CLR Memory\Large Object Heap size".&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.</FONT></P>
<P style="MARGIN: 0in 0in 0pt" class=MsoNormal><FONT size=3 face="Times New Roman"></FONT>&nbsp;</P>
<P style="MARGIN: 0in 0in 0pt" class=MsoNormal><FONT size=3 face="Times New Roman">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.&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.&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).</FONT></P>
<P style="MARGIN: 0in 0in 0pt" class=MsoNormal><FONT size=3 face="Times New Roman"></FONT>&nbsp;</P>
<P style="MARGIN: 0in 0in 0pt" class=MsoNormal><FONT size=3 face="Times New Roman">Another way to determine&nbsp;</FONT><FONT size=3 face="Times New Roman">if you’re allocating from the Large Object Heap (LOH) is by setting break points.&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: </FONT></P>
<P style="MARGIN: 0in 0in 0pt" class=MsoNormal><?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" /><o:p><FONT size=3 face="Times New Roman">&nbsp;</FONT></o:p></P>
<P style="TEXT-INDENT: 0.5in; MARGIN: 0in 0in 0pt" class=MsoNormal><FONT size=3 face="Times New Roman">bp mscorwks!SVR::gc_heap::allocate_large_object ".echo size=; ? poi(@esp+4); k12; gc"</FONT></P>
<P style="MARGIN: 0in 0in 0pt" class=MsoNormal><o:p><FONT size=3 face="Times New Roman">&nbsp;</FONT></o:p></P>
<P style="MARGIN: 0in 0in 0pt" class=MsoNormal><FONT size=3 face="Times New Roman">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</FONT></P>
<P style="MARGIN: 0in 0in 0pt" class=MsoNormal><o:p><FONT size=3 face="Times New Roman">&nbsp;</FONT></o:p></P>
<P style="TEXT-INDENT: 0.5in; MARGIN: 0in 0in 0pt" class=MsoNormal><FONT size=3 face="Times New Roman">ntsd.exe -y %WINDIR%\symbols;srv*%WINDIR%\Symbols*https://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</FONT></P>
<P style="TEXT-INDENT: 0.5in; MARGIN: 0in 0in 0pt" class=MsoNormal><FONT size=3 face="Times New Roman"></FONT>&nbsp;</P>
<P style="MARGIN: 0in 0in 0pt" class=MsoNormal><FONT size=3 face="Times New Roman">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.&nbsp; ASP.NET itself does not do this, but perhaps&nbsp;your application does?</FONT></P>
<P style="MARGIN: 0in 0in 0pt" class=MsoNormal><o:p><FONT size=3 face="Times New Roman">&nbsp;</FONT></o:p></P>
<P style="MARGIN: 0in 0in 0pt" class=MsoNormal><FONT size=3 face="Times New Roman">Thanks,</FONT></P>
<P style="MARGIN: 0in 0in 0pt" class=MsoNormal><FONT size=3 face="Times New Roman">Thomas</FONT></P>]]></content:encoded>
    </item>
  </channel>
</rss>