tag:blogger.com,1999:blog-36807201083477881532024-03-14T21:50:39.935+08:00Steve ChanASP.NET and MVC Tips and TricksAnonymoushttp://www.blogger.com/profile/01367532666266117220noreply@blogger.comBlogger82125tag:blogger.com,1999:blog-3680720108347788153.post-44237684380684210002017-01-03T09:42:00.000+08:002017-02-15T01:11:18.283+08:00ASP.NET Tips #81 - Use fastest HTML DOM .textContent property to write text to any HTML element<p>Referring back to the <a href="http://www.steve.my/2016/11/aspnet-tips-79-use-fastest-html-dom.html">previous blog</a>, many were asking about the performance speed to write text to any HTML element.</p>
<p>Hence, I did some research and found that there are many ways to write text to any HTML element, but I'm concerned about the overall performance in terms of speed & browser compatibility, so, I created a JavaScript Benchmark Test based on 7 common methods and tested on 5 popular browsers.</p>
<h3>
Test - Environment</h3>
<ul>
<li>CPU: Intel i7-4770 @3.4GHz (8 cores)</li>
<li>Memory: 8GB DDR3</li>
<li>Hard Disk: Intel 535 120GB SSD (Read: 540MBps, Write: 450MBps)
</li>
<li>Network Card: Gigabit</li>
<li>Internet Bandwidth: 100Mbps (Download: 100Mbps, Upload: 100Mbps)</li>
<li>Platform: Windows 10 (64-bit)
</li>
</ul>
<h3>
Test - HTML DOM property/method (Pure JavaScript vs jQuery)</h3>
<ol>
<li>Pure JS .innerText</li>
<li>Pure JS .innerHTML</li>
<li>Pure JS .textContent</li>
<li>jQuery .text()</li>
<li>jQuery .html()</li>
<li>jQuery .find().text()</li>
<li>jQuery .find().html()</li>
</ol>
<h3>
Test - Modern Browsers</h3>
<ol>
<li>IE 11 on Windows 10</li>
<li>Edge 14 on Windows 10</li>
<li>Google Chrome 55 on Windows 10</li>
<li>Mozilla Firefox 50.1 on Windows 10</li>
<li>Opera 42 on Windows 10</li>
</ol>
<h3>Test - Modern Browsers (Updated on 15 Feb 2017)</h3>
<ol>
<li>Google Chrome 56 on Windows 10</li>
<li>Mozilla Firefox 51.0.1 on Windows 10</li>
<li>Opera 43 on Windows 10</li>
</ol>
<h3>
Test Result</h3>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhKPLZ-7Th_JQKb9GBRXRZp739SjlLttu3v72zoss0NPXjskxBsRExI6YMuVA3W0Wu1plFlvy93kvpUq7Ugm0BoeoazNXPaZ6FHWh1QDuYfWfpFCc5KbYu06GGX0CUQ46_ADO8apk6jrPM/s1600/JavaScript+Benchmark+SetText.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhKPLZ-7Th_JQKb9GBRXRZp739SjlLttu3v72zoss0NPXjskxBsRExI6YMuVA3W0Wu1plFlvy93kvpUq7Ugm0BoeoazNXPaZ6FHWh1QDuYfWfpFCc5KbYu06GGX0CUQ46_ADO8apk6jrPM/s1600/JavaScript+Benchmark+SetText.PNG" /></a></div>
<br />
<h3>Test Result (Updated on 15 Feb 2017)</h3>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjo_7SHWzYcbDX4onvDZYD4EXznqbFrt3c-EL5WJuRz6W_sfRB8Kv57qm8cZGGQlQXm_FNcU1YPCPVKKgBU64CM8ARPjNzmMqH6Sf-00ScTls4Ozmxuz_ULuIFODnlCo5_JT9Ew0dfu5D0/s1600/writeText_v2.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjo_7SHWzYcbDX4onvDZYD4EXznqbFrt3c-EL5WJuRz6W_sfRB8Kv57qm8cZGGQlQXm_FNcU1YPCPVKKgBU64CM8ARPjNzmMqH6Sf-00ScTls4Ozmxuz_ULuIFODnlCo5_JT9Ew0dfu5D0/s1600/writeText_v2.PNG" /></a></div>
<br />
<h3>Conclusion</h3>
<p>Based on the test result, Pure JavaScript <b>.textContent</b> property is proven to be the fastest HTML DOM text writer on the planet so far.<br />
You may perform the same test on your own environment at: <a href="https://www.measurethat.net/Benchmarks/Show/850" target="_blank">https://www.measurethat.net/Benchmarks/Show/850</a></p>
<h3>Browser Support</h3>
.textContent is fully compatible with all browsers except IE8 and below.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj2XgPgH-1auBntB1VGNIa74r8fv-N1WBFTWqwXRPqkNGz6PogY6K37HugTq8nm_ljWXDROGVY7WSp5gUKRRmcy_0skiXuzBxGIJEOjz13jfgQLEYvaltNl-DKKYy_HiyZbNvq_V3YxeFQ/s1600/textContent+Browser+Compatible.png" imageanchor="1" ><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj2XgPgH-1auBntB1VGNIa74r8fv-N1WBFTWqwXRPqkNGz6PogY6K37HugTq8nm_ljWXDROGVY7WSp5gUKRRmcy_0skiXuzBxGIJEOjz13jfgQLEYvaltNl-DKKYy_HiyZbNvq_V3YxeFQ/s1600/textContent+Browser+Compatible.png" /></a></div>
<p>To learn more about .textContent, kindly visit <a href="http://www.w3schools.com/jsref/prop_node_textcontent.asp" target="_blank">http://www.w3schools.com/jsref/prop_node_textcontent.asp</a></p>Anonymoushttp://www.blogger.com/profile/01367532666266117220noreply@blogger.com2tag:blogger.com,1999:blog-3680720108347788153.post-57894068763679212552016-12-29T09:32:00.001+08:002017-02-15T00:58:44.698+08:00ASP.NET Tips #80 - Use fastest HTML DOM .textContent property to read text from any HTML element<p>Referring back to the <a href="http://www.steve.my/2016/11/aspnet-tips-79-use-fastest-html-dom.html">previous blog</a>, many were asking about the performance speed to read text from any HTML element.</p>
<p>Hence, I did some research and found that there are many ways to read text from any HTML element, but I'm concerned about the overall performance in terms of speed & browser compatibility, so, I created a JavaScript Benchmark Test based on 7 common methods and tested on 5 popular browsers.</p>
<h3>
Test - Environment</h3>
<ul>
<li>CPU: Intel i7-4770 @3.4GHz (8 cores)</li>
<li>Memory: 8GB DDR3</li>
<li>Hard Disk: Intel 535 120GB SSD (Read: 540MBps, Write: 450MBps)
</li>
<li>Network Card: Gigabit</li>
<li>Internet Bandwidth: 100Mbps (Download: 100Mbps, Upload: 100Mbps)</li>
<li>Platform: Windows 10 (64-bit)
</li>
</ul>
<h3>
Test - HTML DOM property/method (Pure JavaScript vs jQuery)</h3>
<ol>
<li>Pure JS .innerText</li>
<li>Pure JS .innerHTML</li>
<li>Pure JS .textContent</li>
<li>jQuery .text()</li>
<li>jQuery .html()</li>
<li>jQuery .find().text()</li>
<li>jQuery .find().html()</li>
</ol>
<h3>
Test - Modern Browsers</h3>
<ol>
<li>IE 11 on Windows 10</li>
<li>Edge 14 on Windows 10</li>
<li>Google Chrome 55 on Windows 10</li>
<li>Mozilla Firefox 50.1 on Windows 10</li>
<li>Opera 42 on Windows 10</li>
</ol>
<h3>Test - Modern Browsers (Updated on 15 Feb 2017)</h3>
<ol>
<li>Google Chrome 56 on Windows 10</li>
<li>Mozilla Firefox 51.0.1 on Windows 10</li>
<li>Opera 43 on Windows 10</li>
</ol>
<h3>Test Result</h3>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjfNhe2tQdtYXZpPk8VgWTiltvxPMkVwbytz0T_hVTsUQuQAw71xGt4iJC7xV1hMDAwAf4YyB_mf1cbDdJGqoDq4GODL4ibwkJzL_7d1doncBzby2CvSThJrWEHF-ir21NQYN87jczMWiI/s1600/JavaScript+GetText+Benchmark.PNG" imageanchor="1"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjfNhe2tQdtYXZpPk8VgWTiltvxPMkVwbytz0T_hVTsUQuQAw71xGt4iJC7xV1hMDAwAf4YyB_mf1cbDdJGqoDq4GODL4ibwkJzL_7d1doncBzby2CvSThJrWEHF-ir21NQYN87jczMWiI/s1600/JavaScript+GetText+Benchmark.PNG" /></a></div>
<br />
<h3>Test Result (Updated on 15 Feb 2017)</h3>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjb89JlQTq8RUV-uYymcvBMiM5cU9Rp-GjeBU6x3xLksg94dWJoY3yKM4fHtJUP_jA-zljEqsOTfIblKRoJ5zJgpW52qUJRZBWBOXWXv3k2ySRpgcHJTapLAwlAPaOtBtde-oI0OWe33BY/s1600/readText_v2.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjb89JlQTq8RUV-uYymcvBMiM5cU9Rp-GjeBU6x3xLksg94dWJoY3yKM4fHtJUP_jA-zljEqsOTfIblKRoJ5zJgpW52qUJRZBWBOXWXv3k2ySRpgcHJTapLAwlAPaOtBtde-oI0OWe33BY/s1600/readText_v2.PNG" /></a></div>
<br />
<h3>Conclusion</h3>
<p>Based on the test result, Pure JavaScript <b>.textContent</b> property is proven to be the fastest HTML DOM text reader on the planet so far.<br />
You may perform the same test on your own environment at: <a href="https://www.measurethat.net/Benchmarks/Show/849" target="_blank">https://www.measurethat.net/Benchmarks/Show/849</a></p>
<h3>Browser Support</h3>
.textContent is fully compatible with all browsers except IE8 and below.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj2XgPgH-1auBntB1VGNIa74r8fv-N1WBFTWqwXRPqkNGz6PogY6K37HugTq8nm_ljWXDROGVY7WSp5gUKRRmcy_0skiXuzBxGIJEOjz13jfgQLEYvaltNl-DKKYy_HiyZbNvq_V3YxeFQ/s1600/textContent+Browser+Compatible.png" imageanchor="1" ><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj2XgPgH-1auBntB1VGNIa74r8fv-N1WBFTWqwXRPqkNGz6PogY6K37HugTq8nm_ljWXDROGVY7WSp5gUKRRmcy_0skiXuzBxGIJEOjz13jfgQLEYvaltNl-DKKYy_HiyZbNvq_V3YxeFQ/s1600/textContent+Browser+Compatible.png" /></a></div>
<p>To learn more about .textContent, kindly visit <a href="http://www.w3schools.com/jsref/prop_node_textcontent.asp" target="_blank">http://www.w3schools.com/jsref/prop_node_textcontent.asp</a></p>Anonymoushttp://www.blogger.com/profile/01367532666266117220noreply@blogger.com0tag:blogger.com,1999:blog-3680720108347788153.post-34497246393817444132016-11-01T16:03:00.001+08:002017-02-15T00:59:38.455+08:00ASP.NET Tips #79 - Use fastest HTML DOM getElementById() method to access any HTML element<p>The <b>document.getElementById()</b> or <b>jQuery(#id)</b> method returns the element that has the ID attribute with the specified value.</p>
<p>This method is one of the most common methods in the HTML DOM, and is used almost every time you want to manipulate, or get info from, an element on your document.<p>
<p>Recently I did some research and found that there are many ways to access HTML element, but I'm concerned about the overall performance in terms of speed & browser compatibility. Hence, I created a <a href="https://www.measurethat.net/Benchmarks/Show/554" target="_blank">JavaScript Benchmark Test</a> based on 13 common methods and tested on 5 popular browsers.</p>
<h3>
Test - Environment</h3>
<ul>
<li>CPU: Intel i7-4770 @3.4GHz (8 cores)</li>
<li>Memory: 8GB DDR3</li>
<li>Hard Disk: Intel 535 120GB SSD (Read: 540MBps, Write: 450MBps)
</li>
<li>Network Card: Gigabit</li>
<li>Internet Bandwidth: 100Mbps (Download: 100Mbps, Upload: 100Mbps)</li>
<li>Platform: Windows 10 (64-bit)
</li>
</ul>
<h3>Test - HTML DOM methods (Pure JavaScript vs jQuery)</h3>
<ol>
<li>getElementById()</li>
<li>getElementsByClassName()</li>
<li>getElementsByTagName()</li>
<li>querySelector(#id)</li>
<li>querySelector(.id)</li>
<li>querySelectorAll(#id)</li>
<li>querySelectorAll(#id)</li>
<li>jQuery(#id)</li>
<li>jQuery(.id)</li>
<li>jQuery(tag#id)</li>
<li>jQuery(tag.id)</li>
<li>jQuery + getElementById</li>
<li>jQuery + getElementByClassName</li>
</ol>
<h3>Test - Modern Browsers</h3>
<ol>
<li>IE 11 on Windows 10</li>
<li>Edge 14 on Windows 10</li>
<li>Google Chrome 54 on Windows 10</li>
<li>Mozilla Firefox 49.0.2 on Windows 10</li>
<li>Opera 41 on Windows 10</li>
</ol>
<b>Test - Modern Browsers (Updated on 27 Dec 2016)</b><br />
<ol>
<li>Google Chrome 55 on Windows 10</li>
<li>Mozilla Firefox 50.1 on Windows 10</li>
<li>Opera 42 on Windows 10</li>
</ol>
<b>Test - Modern Browsers (Updated on 15 Feb 2017)</b><br />
<ol>
<li>Google Chrome 56 on Windows 10</li>
<li>Mozilla Firefox 51.0.1 on Windows 10</li>
<li>Opera 43 on Windows 10</li>
</ol>
<h3>Test Result</h3>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjCagZXetN0vVCygRizSiCCHOS6lmDQwn5xNjqxGBbL8S0Hl5nJI5sf0Wh2jDxviSLjcFHJ0KZ6gdezvQYmXN_AtNo5Rlg4VolSZiGMi-oXBGIS4IDAMni7OM80e66gegeHbuTqtB9E7-E/s1600/Test+Result.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjCagZXetN0vVCygRizSiCCHOS6lmDQwn5xNjqxGBbL8S0Hl5nJI5sf0Wh2jDxviSLjcFHJ0KZ6gdezvQYmXN_AtNo5Rlg4VolSZiGMi-oXBGIS4IDAMni7OM80e66gegeHbuTqtB9E7-E/s1600/Test+Result.png" /></a></div>
<br />
<b>Test Result (Updated on 27 Dec 2016)</b><br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgl409z4GaHXvGbSKsWSFJz8x7KJ7Lu0FTM_IXIdM3QvDHyfJ62MvXF_yDu0-ksGcLSbuuZETq6NQQVkMOtwQ05q62jw4bSFIMgR3kAa1cR-Q8t_fT8FgYmO6PLSZWHjo_9yCiC7lhBo98/s1600/JavaScript+Speed+Test+v2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgl409z4GaHXvGbSKsWSFJz8x7KJ7Lu0FTM_IXIdM3QvDHyfJ62MvXF_yDu0-ksGcLSbuuZETq6NQQVkMOtwQ05q62jw4bSFIMgR3kAa1cR-Q8t_fT8FgYmO6PLSZWHjo_9yCiC7lhBo98/s1600/JavaScript+Speed+Test+v2.png" /></a></div>
<br />
<b>Test Result (Updated on 15 Feb 2017)</b><br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEixzbd7rpvbLzenODNFvF3yK_jI3kd9JslwDME4jMIGj1k5-ZAx7Uj0AEFxJIreJ6vI5QaKjx4b6kna9PIOOLx0PGigijQj-oy6o8i1dfanYXlHmpN1DFP8KcDpO9wV28JFU5c5i0jILo4/s1600/getElementById_v3.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEixzbd7rpvbLzenODNFvF3yK_jI3kd9JslwDME4jMIGj1k5-ZAx7Uj0AEFxJIreJ6vI5QaKjx4b6kna9PIOOLx0PGigijQj-oy6o8i1dfanYXlHmpN1DFP8KcDpO9wV28JFU5c5i0jILo4/s1600/getElementById_v3.PNG" /></a></div>
<br />
<h3>
Conclusion</h3>
Based on the test result, Pure JavaScript <b>document.getElementById()</b> is proven to be the fastest HTML DOM on the planet so far.<br />
You may perform the same test on your own environment at: <a href="https://www.measurethat.net/Benchmarks/Show/554" target="_blank">https://www.measurethat.net/Benchmarks/Show/554</a><br />
<br />
<h3>
Browser Support</h3>
document.getElementById() is fully compatible with all browsers.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj4PZmTCBNs5qRI8bbW3iAmWeTw9toepMW6qU-Erm3DmB_dDERkc2d-J3SasjdHgazUaQhiNjY_eUrS9zmAyuinuX6isTwAX80OC4s3H10E3X3om8JCYNL8sSVnaXVk6aBruw1v7tCp0eY/s1600/Browser+Support.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj4PZmTCBNs5qRI8bbW3iAmWeTw9toepMW6qU-Erm3DmB_dDERkc2d-J3SasjdHgazUaQhiNjY_eUrS9zmAyuinuX6isTwAX80OC4s3H10E3X3om8JCYNL8sSVnaXVk6aBruw1v7tCp0eY/s1600/Browser+Support.png" /></a></div>
<br />
To learn more about document.getElementById(), kindly visit <a href="http://www.w3schools.com/jsref/met_document_getelementbyid.asp" target="_blank">http://www.w3schools.com/jsref/met_document_getelementbyid.asp</a>Anonymoushttp://www.blogger.com/profile/01367532666266117220noreply@blogger.com0tag:blogger.com,1999:blog-3680720108347788153.post-43965481883090627562016-02-04T10:13:00.003+08:002016-02-04T10:13:54.414+08:00ASP.NET Tips #78 - Dispose of unmanaged resources<p>File I/O, network resources, database connections, etc, should be disposed of once their usage is complete, rather than waiting for the Garbage Collector to handle them. This can take anything from milliseconds to minutes, depending on the rate you consume memory in your code. If you don't use a lot of, it will take a long time.</p>
<p>These types of resources typically implement the IDisposable interface so that unmanaged resources can be released once use of the object is complete.</p>
<p>Ideally, use a 'using' {...} block to automatically dispose the object once out of scope, or ensure you manually call Dispose().</p>
<p>For more information, see: <a href="https://msdn.microsoft.com/en-us/library/498928w2.aspx" target="_blank">https://msdn.microsoft.com/en-us/library/498928w2.aspx</a></p>Anonymoushttp://www.blogger.com/profile/01367532666266117220noreply@blogger.com1tag:blogger.com,1999:blog-3680720108347788153.post-1183902657995674532016-02-02T16:27:00.000+08:002016-02-02T16:27:05.598+08:00ASP.NET Tips #77 - Avoid duplicate string reference issues<p>String referencing duplication is one of the major memory hogging performance issues. String interning is a useful solution if you're generating a lot of strings at runtime that are likely to be the same. It calls IsInterned to see if an interned string exists as follows:</p>
<pre class="prettyprint cs">
class Program
{
static void Main()
{
// A.
// String generated at runtime.
// Is not unique in string pool
string s1 = new StringBuilder().Append("cat").Append(" and dog").ToString();
// B.
// Interned string added at runtime.
// Is unique in string pool.
string s2 = string.Intern(s1);
}
}
</pre>
<p>My own benchmarking showed that string interning can improve performance by more than four times when the string comparison is always true.</p>Anonymoushttp://www.blogger.com/profile/01367532666266117220noreply@blogger.com0tag:blogger.com,1999:blog-3680720108347788153.post-51314789146356980102016-01-29T15:51:00.005+08:002016-01-29T15:51:48.301+08:00ASP.NET Tips #76 - Preallocate sizes on things if you can <p>Many objects like memory stream, list, and dictionary will double their size as needed causing wasteful copying and allocations. If you know your list will have 100,000 items, initialize it as such to avoid problems in the future.</p>Anonymoushttp://www.blogger.com/profile/01367532666266117220noreply@blogger.com0tag:blogger.com,1999:blog-3680720108347788153.post-18509499960254299822016-01-28T10:34:00.003+08:002016-01-28T10:34:48.244+08:00ASP.NET Tips #75 - Learn about the .NET Garbage Collector (GC) and when it can cause pauses that slow your application down<p>Over time the .NET GC has become more advanced (most notably the Background Server GC Mode in .NET 4.5), but there are still situations where the GC can have an adverse effect on your application's performance.</p>
<p>Understanding how to detect these situations and more importantly how to fix them is a useful skill. For example, in many applications there are some actions which are more performance-critical than others, and it would be preferable for Garbage Collection to run during the less critical periods. Setting a GCLatencyMode is a useful way of asking the Garbage Collector to be more conservative about choosing to run during these times.</p>Anonymoushttp://www.blogger.com/profile/01367532666266117220noreply@blogger.com1tag:blogger.com,1999:blog-3680720108347788153.post-84193910865903174042016-01-21T14:24:00.000+08:002016-01-21T14:24:02.677+08:00ASP.NET Tips #74 - Don't call GC.Collect() explicitly<p>The Garbage Collector is very good at working out appropriate times to run, influenced by factors like memory usage in the application and OS memory pressure. It's almost never necessary to call it explicitly.</p>
<p>Worse, running a Garbage Collection has an impact on application performance. The performance hit is proportional to the number of objects in memory which survive Garbage Collection, so running the Garbage Collector earlier or more frequently than necessary can seriously harm performance.</p>Anonymoushttp://www.blogger.com/profile/01367532666266117220noreply@blogger.com0tag:blogger.com,1999:blog-3680720108347788153.post-80922181653915803402016-01-20T13:54:00.000+08:002016-01-20T13:54:01.459+08:00ASP.NET Tips #73 - Use lists instead of arrays when the size is not known in advance<p>When you want to add or remove data, use lists instead of arrays. Lists grow dynamically and don't need to reserve more space than is needed, whereas resizing arrays is expensive. This is particularly useful when you know what the pattern of growth is going to be, and what your future pattern of access will be.</p>Anonymoushttp://www.blogger.com/profile/01367532666266117220noreply@blogger.com0tag:blogger.com,1999:blog-3680720108347788153.post-89123451470591896622016-01-18T17:18:00.002+08:002016-01-18T17:18:45.035+08:00ASP.NET Tips #72 - If you don't need all the columns from your table, don't select them<p>Entity Framework is a great productivity booster for developers. Instead of writing tedious SQL statements, you just write code like this:</p>
<pre class="prettyprint cs">
var products = db.Products.AsNoTracking().ToList();
</pre>
<p>A line like this is great if you only have a few columns in your products table or if you don't care much about performance or memory consumption. With this code, Entity Framework selects all the columns of the products table, but if your product table has 25 columns and you only need two of them, your database, network, and PC all run slower.</p>
<p>Do your users a favor and retrieve only the columns you need. If, for example, you just need the 'Id' and 'Name' fields, you could rewrite your statement like this:</p>
<pre class="prettyprint cs">
var db.Products.Select(p => new {p.Id, p.Name}).AsNoTracking().ToList();
</pre>Anonymoushttp://www.blogger.com/profile/01367532666266117220noreply@blogger.com0tag:blogger.com,1999:blog-3680720108347788153.post-38926725757968619422016-01-15T09:15:00.002+08:002016-01-15T09:15:47.840+08:00ASP.NET Tips #71 - Use caching to reduce load on the database<p>Accessing the database is often the slowest aspect of an application due to the physical nature of accessing the data from disk (database query caching not withstanding). Developing your application with an efficient caching mechanism in mind can relieve the need for your database to perform requests and let it devote its time where required.</p>
<p>Simple things like caching reference data or data that changes very infrequently can make easy gains and reduce load on your database. As you cache more and more, it's important to ensure you invalidate cached data when it is updated using a common key, and this needs to be factored into the design.</p>
<p>For a headstart on caching and supporting multiple cache engines easily, try this library <a href="https://bitbucket.org/glav/cacheadapter" target="_blank">https://bitbucket.org/glav/cacheadapter</a>. This allows you to support ASP.NET web cache,memcached, Redis and the now defunct AppFabric on the same codebase via configuration only.</p>Anonymoushttp://www.blogger.com/profile/01367532666266117220noreply@blogger.com0tag:blogger.com,1999:blog-3680720108347788153.post-76636107089138634552016-01-14T14:15:00.001+08:002016-01-14T14:15:09.202+08:00ASP.NET Tips #70 - Indexing tables is not an exact science<p>Indexing tables requires some trial and error combined with lots of testing to get things right. Even then, performance metrics change over time as more and more data is added or becomes aged.</p>
<p>When you're using SQL Server, it's a good idea to regularly run and analyze the standard reports SQL Server provides that show index usage (such as top queries by total IO, top queries by average IO, etc). This can highlight unused indexes, and can also show queries that are using excessive IO which may require further indexing.</p>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhHz8o6E3CAMFRXs5Rd3AhZA_4mQy7M5HX8-CdzDaya1RGN4qIM3g4ZwCMwCxIPIMx-KIvbcRifpw1hveOMN0BCesdKdWAGwaregTRomSQu-Dinf9AKJ8yCncarVyd1wInJq1tTAwhwXu8/s1600/SQL+-+Performance+Report.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhHz8o6E3CAMFRXs5Rd3AhZA_4mQy7M5HX8-CdzDaya1RGN4qIM3g4ZwCMwCxIPIMx-KIvbcRifpw1hveOMN0BCesdKdWAGwaregTRomSQu-Dinf9AKJ8yCncarVyd1wInJq1tTAwhwXu8/s1600/SQL+-+Performance+Report.png" /></a></div>
<p>For a deeper analysis of queries, you can also use a tool like ANTS Performance Profiler or Telerik JustTrace.</p>Anonymoushttp://www.blogger.com/profile/01367532666266117220noreply@blogger.com0tag:blogger.com,1999:blog-3680720108347788153.post-53503629449029457152016-01-13T20:37:00.000+08:002016-01-13T20:37:37.339+08:00ASP.NET Tips #69 - Use AsNoTracking when retrieving data for reading with Entity Framework<p>In a lot of cases, data retrieved by the Entity Framework will not be modified within the scope of the same DBContext. Typical examples of this are ASP.NET MVC or ASP.NET MVC API action methods that just answer a get request. However, if you're using the default way of retrieving data, Entity Framework will prepare everything to be able to detect changes on your retrieved entities in order to persist those changes later in the database. This doesn’t only add a performance penalty, but costs some memory, too.</p>
<p>A typical method of retrieving data is:</p>
<pre class="prettyprint cs">
var products = db.Products.Where(p => p.InStock).ToList();
</pre>
<p>A better way uses the extension method AsNoTracking from the System.Data.Entity Namespace:</p>
<pre class="prettyprint cs">
var products = db.Products.Where(p => p.InStock).AsNoTracking().ToList();
</pre>Anonymoushttp://www.blogger.com/profile/01367532666266117220noreply@blogger.com0tag:blogger.com,1999:blog-3680720108347788153.post-80547001251547762132016-01-12T10:29:00.000+08:002016-01-12T10:29:43.261+08:00ASP.NET Tips #68 - Use the SQLBulkCopy class to load data into SQL Server from .NET<p>Using <b>SQLBulkCopy</b> can dramatically decrease the time it takes to load data into SQL Server. A test using SQL Server 2012 on a local machine loading a 100,000 row file had the following results:</p>
<ul>
<li>Using a stored procedure: <b>37</b> seconds</li>
<li>Using concatenated inline SQL: <b>45</b> seconds</li>
<li>Using Entity Framework: <b>45</b> minutes</li>
<li>Using the SQLBulkCopy class: <b>4.5</b> seconds</li>
</ul>
<p>Let's say you need to load a web server log into SQL Server. You would still need to load a file, read the file, parse the file, and load the data into objects. Then you would create a DataTable (you could also use the DataReader or an array of DataRow too):</p>
<pre class="prettyprint cs">
DataTable table = new DataTable();
table.TableName = "LogBulkLoad";
table.Columns.Add("IpAddress", typeof(string));
table.Columns.Add("Identd", typeof(string));
table.Columns.Add("RemoteUser", typeof(string));
table.Columns.Add("LogDateTime", typeof(System.DateTimeOffset));
table.Columns.Add("Method", typeof(string));
table.Columns.Add("Resource", typeof(string));
table.Columns.Add("Protocol", typeof(string));
table.Columns.Add("QueryString", typeof(string));
table.Columns.Add("StatusCode", typeof(int));
table.Columns.Add("Size", typeof(long));
table.Columns.Add("Referer", typeof(string));
table.Columns.Add("UserAgent", typeof(string));
</pre>
<p>Next step would be to load the DataTable with data that you've parsed:</p>
<pre class="prettyprint cs">
foreach (var log in logData)
{
DataRow row = table.NewRow();
row["IpAddress"] = log.IpAddress;
row["Identd"] = log.Identd;
row["RemoteUser"] = log.RemoteUser;
row["LogDateTime"] = log.LogDateTime;
row["Method"] = log.Method;
row["Resource"] = log.Resource;
row["Protocol"] = log.Protocol;
row["QueryString"] = log.QueryString;
row["StatusCode"] = log.StatusCode;
row["Size"] = log.Size;
row["Referer"] = log.Referer;
row["UserAgent"] = log.UserAgent;
table.Rows.Add(row);
}
</pre>
<p>Now you're ready to use the SqlBulkCopy class. You will need an open SqlConnection object (this example pulls the connection string from the config file). Once you've created the SqlBulkCopy object you need to do two things: set the destination table name (the name of the table you will be loading); and call the WriteToServer function passing the DataTable.</p>
<p>This example also provides the column mappings from the DataTable to the table in SQL Server. If your DataTable columns and SQL server columns are in the same positions, then there will be no need to provide the mapping, but in this case the SQL Server table has an ID column and the DataTable does not need to explicitly map them:</p>
<pre class="prettyprint cs">
using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["LogParserContext"].ConnectionString))
{
conn.Open();
using (SqlBulkCopy s = new SqlBulkCopy(conn))
{
s.DestinationTableName = "LogBulkLoad";
s.ColumnMappings.Add("IpAddress", "IpAddress");
s.ColumnMappings.Add("Identd", "Identd");
s.ColumnMappings.Add("RemoteUser", "RemoteUser");
s.ColumnMappings.Add("LogDateTime", "LogDateTime");
s.ColumnMappings.Add("Method", "Method");
s.ColumnMappings.Add("Resource", "Resource");
s.ColumnMappings.Add("Protocol", "Protocol");
s.ColumnMappings.Add("QueryString", "QueryString");
s.ColumnMappings.Add("StatusCode", "StatusCode");
s.ColumnMappings.Add("Size", "Size");
s.ColumnMappings.Add("Referer", "Referer");
s.ColumnMappings.Add("UserAgent", "UserAgent");
s.WriteToServer((DataTable)table);
}
}
</pre>
<p>There are other features of the SqlBulkCopy class that are useful. The BatchSize property can control the number of rows in each batch sent to the server, and the NotifyAfter property allows an event to be fired after a specified number of rows, which is useful for updating the user on the progress of the load.</p>Anonymoushttp://www.blogger.com/profile/01367532666266117220noreply@blogger.com0tag:blogger.com,1999:blog-3680720108347788153.post-4954987221080493162016-01-11T10:13:00.002+08:002016-01-11T10:13:30.034+08:00ASP.NET Tips #67 - Use LINQ's 'let' keyword to tune emitted SQL<p>Projecting a row into an object with a nested object has a big impact on the generated SQL. For example, here is an original query:</p>
<pre class="prettyprint cs">
from s in Scholars
where s.ID == 2764
select new
{
s.School.Address1,
s.School.Address2,
s.School.City,
s.School.State,
s.School.ZIP,
s.School.PhoneNo,
s.School.Email,
Principal_FirstName = s.School.Leader.FirstName,
Principal_LastName = s.School.Leader.LastName,
Principal_Email = s.School.Leader.Email
}
</pre>
<p>This generates the following SQL:</p>
<pre class="prettyprint cs">
SELECT
1 AS [C1],
[Extent2].[Address1] AS [Address1],
[Extent2].[Address2] AS [Address2],
[Extent2].[City] AS [City],
[Extent2].[State] AS [State],
[Extent2].[ZIP] AS [ZIP],
[Extent2].[PhoneNo] AS [PhoneNo],
[Extent2].[Email] AS [Email],
[Join2].[FirstName] AS [FirstName],
[Join4].[LastName] AS [LastName],
[Join6].[Email] AS [Email1]
FROM [dbo].[Scholar] AS [Extent1]
LEFT OUTER JOIN [dbo].[School] AS [Extent2] ON [Extent1].
[SchoolID] = [Extent2].[ID]
LEFT OUTER JOIN (SELECT [Extent3].[ID] AS [ID1],
[Extent4].[FirstName] AS [FirstName]
FROM [dbo].[Staff] AS [Extent3]
INNER JOIN [dbo].[Person] AS [Extent4] ON [Extent3].
[ID] = [Extent4].[ID] ) AS [Join2] ON [Extent2].
[LeaderStaffID] = [Join2].[ID1]
LEFT OUTER JOIN (SELECT [Extent5].[ID] AS [ID2],
[Extent6].[LastName] AS [LastName]
FROM [dbo].[Staff] AS [Extent5]
INNER JOIN [dbo].[Person] AS [Extent6] ON [Extent5].
[ID] = [Extent6].[ID] ) AS [Join4] ON [Extent2].
[LeaderStaffID] = [Join4].[ID2]
LEFT OUTER JOIN (SELECT [Extent7].[ID] AS [ID3],
[Extent8].[Email] AS [Email]
FROM [dbo].[Staff] AS [Extent7]
INNER JOIN [dbo].[Person] AS [Extent8] ON [Extent7].
[ID] = [Extent8].[ID] ) AS [Join6] ON [Extent2].
[LeaderStaffID] = [Join6].[ID3]
WHERE 2764 = [Extent1].[ID]
</pre>
<p>Using the 'let' keyword allows us to define the navigation as an alias:</p>
<pre class="prettyprint cs">
from s in Scholars
where s.ID == 2764
let leader = s.School.Leader
select new
{
s.School.Address1,
s.School.Address2,
s.School.City,
s.School.State,
s.School.ZIP,
s.School.PhoneNo,
s.School.Email,
Principal = new {
leader.FirstName,
leader.LastName,
leader.Email
}
}
</pre>
<p>This results in a much smaller query:</p>
<pre class="prettyprint cs">
SELECT
1 AS [C1],
[Extent2].[Address1] AS [Address1],
[Extent2].[Address2] AS [Address2],
[Extent2].[City] AS [City],
[Extent2].[State] AS [State],
[Extent2].[ZIP] AS [ZIP],
[Extent2].[PhoneNo] AS [PhoneNo],
[Extent2].[Email] AS [Email],
[Join2].[FirstName] AS [FirstName],
[Join2].[LastName] AS [LastName],
[Join2].[Email] AS [Email1]
FROM [dbo].[Scholar] AS [Extent1]
LEFT OUTER JOIN [dbo].[School] AS [Extent2] ON [Extent1].
[SchoolID] = [Extent2].[ID]
LEFT OUTER JOIN (SELECT [Extent3].[ID] AS [ID1],
[Extent4].[FirstName] AS [FirstName], [Extent4].
[LastName] AS [LastName], [Extent4].[Email] AS [Email]
FROM [dbo].[Staff] AS [Extent3]
INNER JOIN [dbo].[Person] AS [Extent4] ON [Extent3].
[ID] = [Extent4].[ID] ) AS [Join2] ON [Extent2].
[LeaderStaffID] = [Join2].[ID1]
WHERE 2764 = [Extent1].[ID]
</pre>
<p>Looking at the query execution plan in SSMS, the first query is roughly twice as expensive as the second, so not only is the query cleaner, but it performs and scales better as well.</p>
<p>A tool like Telerik JustTrace and ANTS Performance Profiler can be used here to discover the performance improvement because it lets you see the generated SQL from a LINQ to SQL/EF query.</p>Anonymoushttp://www.blogger.com/profile/01367532666266117220noreply@blogger.com0tag:blogger.com,1999:blog-3680720108347788153.post-74140173529024536052016-01-09T17:21:00.001+08:002016-01-09T17:21:44.051+08:00ASP.NET Tips #66 - Beware hidden cursors<p>If you're using an ORM like Entity Framework, when you declare an object mapped to another table with foreign keys, you automatically get references of those related entities. Unfortunately, there is a significant hidden cost when accessing the related entities, as separate queries can be run to retrieve details of each referenced row. This is commonly called the n+1 select problem.</p>
<p>For example, consider the following code where we fetch a list of schools from a database then filter that list. On line 1, a query
is run to retrieve a list of n schools. On line 2, for every item in the schools list, a query is run to retrieve the number of pupils at
that school, so in total n+1 queries are run.</p>
<pre class="prettyprint cs">
List<School> schools = context.Schools.ToList();
List<School> filteredSchools = schools.Where(s => s.Pupils.Count > 1000).ToList();
</pre>
<p>Consider using Eager Loading to avoid this scenario if you need to access properties of Pupils later:</p>
<pre class="prettyprint cs">
List<School> schools = context.Schools.Include(s => s.Pupils).ToList();
</pre>
<p>Or in this scenario, simply replace line 1 with:</p>
<pre class="prettyprint cs">
List<School> schools = context.Schools.Where(s => s.Pupils.Count > 1000).ToList();
</pre>Anonymoushttp://www.blogger.com/profile/01367532666266117220noreply@blogger.com0tag:blogger.com,1999:blog-3680720108347788153.post-14088579847519106712016-01-08T18:54:00.000+08:002016-01-08T18:59:05.125+08:00ASP.NET Tips #65 - Don't overlook 'WHERE IN' style LINQ to SQL Queries<p>Entity Framework is smart enough to convert the Contains() operator on LINQ queries to WHERE IN (...) in SQL. But there is a hidden problem: Giving a data set length of greater than around 10,000 records in a WHERE IN (...) clause will significantly degrade the performance of the query generation and query execution:</p>
<pre class="prettyprint cs">
var ids = new int[]{0,1, 2,3,4,5,6,7,8,9,10........99995, 99996,99997,99998,99999};
var matches = (from person in people
where ids.Contains(person.Id)
select person).ToArray();
</pre>
<p>The above statement generates the following fat SQL query:</p>
<pre class="prettyprint cs">
SELECT * FROM PERSON WHERE ID IN
(0,1,2,3,4,5,6,7,8,9,10.....,99995,99996,99997,99998,99999)
</pre>
<p>It is advisable therefore to send data in batches. 500 records per batch, for example, would yield a significant improvement in performance, but you should do benchmarking to see what works for you.</p>Anonymoushttp://www.blogger.com/profile/01367532666266117220noreply@blogger.com0tag:blogger.com,1999:blog-3680720108347788153.post-51403031397477465122016-01-07T10:29:00.001+08:002016-01-07T10:29:24.636+08:00ASP.NET Tips #64 - Avoid issues with string queries<p>Instructing Entity Framework to use the right kind of string when generating queries can resolve datatype conversion issues. A simple solution is to introduce Column Annotation:</p>
<pre class="prettyprint cs">
public class MyTable
{
[Column(TypeName="varchar")]
public string Property1 { get; set; }
}
</pre>
<p>This is particularly valid for string datatypes where .NET Strings are Unicode by default and are not the same in SQL Server.</p>
<p>For more information, see: <a href="http://www.simple-talk.com/dotnet/.net-tools/catching-performance-issues-in-development/" target="_blank">http://www.simple-talk.com/dotnet/.net-tools/catching-performance-issues-in-development/</a></p>Anonymoushttp://www.blogger.com/profile/01367532666266117220noreply@blogger.com0tag:blogger.com,1999:blog-3680720108347788153.post-76884793492663726072016-01-06T14:36:00.004+08:002016-01-06T14:36:58.588+08:00ASP.NET Tips #63 - Listen to generated SQL<p>Entity Framework greatly simplifies database access for the developer, but it can also introduce problems when you are writing more complex LINQ to SQL queries.</p>
</p>This may generate poorly-performing SQL, so troubleshooting the generated SQL with tools like SQL Server Profiler or ANTS Performance Profiler is very important.</p>Anonymoushttp://www.blogger.com/profile/01367532666266117220noreply@blogger.com0tag:blogger.com,1999:blog-3680720108347788153.post-51435248839907143942015-12-18T16:58:00.004+08:002015-12-18T16:58:20.858+08:00ASP.NET Tips #62 - Delayed execution in EF can trip you up<p>If your Model exposes an IQueryable object, your View may actually run a query multiple times when it tries to read the object's data.</p>
<p>List properties in the Model should be of type IList to force the query to be executed once and only once.</p>
<p>It’s easy to spot these additional queries with a tool like ANTS Performance Profiler, which shows you what queries are being run by your application.</p>Anonymoushttp://www.blogger.com/profile/01367532666266117220noreply@blogger.com0tag:blogger.com,1999:blog-3680720108347788153.post-80993462167859718852015-12-16T12:00:00.000+08:002015-12-17T09:34:27.253+08:00ASP.NET Tips #61 - Confirm you are retrieving only the records you need<p>If you are calling the database using Entity Framework, you may write this command:</p>
<pre class="prettyprint lang-cs">
return context.Products.ToList().FirstOrDefault();
</pre>
<p>While this essentially returns one record, it retrieves the entire Products table from the database and then returns you one record. A better command would be:</p>
<pre class="prettyprint lang-cs">
return context.Products.FirstOrDefault();
</pre>
<p>This will produce a SQL command equivalent to grabbing the TOP 1 record instead of the entire contents of the table.</p>Anonymoushttp://www.blogger.com/profile/01367532666266117220noreply@blogger.com0tag:blogger.com,1999:blog-3680720108347788153.post-26127255586133119472015-12-14T12:00:00.000+08:002015-12-15T12:26:53.568+08:00ASP.NET Tips #60 - Minimize your database calls<p>While Entity Framework can be a fantastic ORM tool, it can also be a major pain when you are executing a number of calls that are constantly hitting the database.</p>
<p>For simple database activities (SELECT, INSERT, UPDATE, or DELETE), use Entity Framework's standard Add, SaveChanges,and Delete methods. For complex result sets, it can sometimes be more efficient to place them in a stored procedure and leave the heavy lifting to the database.</p>Anonymoushttp://www.blogger.com/profile/01367532666266117220noreply@blogger.com0tag:blogger.com,1999:blog-3680720108347788153.post-41487427553142059572015-12-14T11:12:00.001+08:002015-12-14T11:12:27.560+08:00ASP.NET Tips #59 - If your ORM is capable of handling multi-dataset returns, use this to reduce your database round-trips<p>This is often called a 'none-chatty' design, where a single larger call is preferred over multiple smaller calls. Each call involves a degree of negotiation and overhead and the more calls you make, the more overhead is incurred. A single larger call reduces the overhead.</p>Anonymoushttp://www.blogger.com/profile/01367532666266117220noreply@blogger.com0tag:blogger.com,1999:blog-3680720108347788153.post-86696780064299510142015-12-09T12:14:00.003+08:002015-12-09T12:14:58.088+08:00ASP.NET Tips #58 - Spot potential issues in your code with Concurrency Visualizer<p>From Visual Studio 2013, Concurrency Visualizer became a plugin rather than a standard feature. It is still a tremendously useful performance analysis tool, however, particularly when you use the SDK to instrument your code with flags, messages, alerts, and spans.</p>
<p>Concurrency Visualizer provides several views: utilization, threads, and cores. For me, the threads view is most useful. MSDN describes the threads view <a href="https://msdn.microsoft.com/en-us/library/dd627193%28v=vs.120%29.aspx" target="_blank">here</a>, but that page does not do justice to the power of what you can see and find (however, they do offer another page within the docs that gives a bigger glimpse as to the power lurking beneath your fingertips).</p>
<p>The following Concurrency Visualizer timeline is a good example:</p>
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj5XRnIDF9yVzZpr7FlfhmIIPeSWqIcbzjJw0gM7CggSAQ1uLOIXuUJS2yVskSIuB9pCzRiTeIfdL2CLjAz49hXsdwdM0JtA6M_DotA54Aek8VPjDAFOcoVJDGs-SAXigCsO5QeSvEXfPI/s1600/Concurrency+Visualizer.PNG" imageanchor="1" ><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj5XRnIDF9yVzZpr7FlfhmIIPeSWqIcbzjJw0gM7CggSAQ1uLOIXuUJS2yVskSIuB9pCzRiTeIfdL2CLjAz49hXsdwdM0JtA6M_DotA54Aek8VPjDAFOcoVJDGs-SAXigCsO5QeSvEXfPI/s320/Concurrency+Visualizer.PNG" /></a>
<p>This is from a recent project where a multithreaded system has a large number of users making requests via a web interface and expecting prompt responses back. The system allows an administrator to hot swap pieces of the back end supposedly without interfering with the horde of eager users. But there was a delay of a few seconds now and then, and the source was not clear.</p>
<p>After instrumenting the likely sections of code for Concurrency Visualizer, starting a CV data collection, then running thousands of automated user inputs in parallel with dozens of automated admin inputs, a pattern quickly emerged in the graph.</p>
<p>There was a synchronization lock being held in the admin chunk of code (the "Clean" span) that blocked the end-user processing (the "converse" span) significantly. You can see in the illustration that most of the end-user time was waiting for that lock (the red bar).</p>
<p>I had only to visually scan the timeline to find my markers. Concurrency Visualizer revealed what was being blocked; when the lock was released on one thread and – via the vertical black line at the end of the red block – how that tied back to the other thread and allowed it to continue; and provided stack threads that told me exactly where in the code all this was happening.</p>
<p>For more details, kindly visit <a href="https://msdn.microsoft.com/en-us/library/dd627193%28v=vs.120%29.aspx" target="_blank">https://msdn.microsoft.com/en-us/library/dd627193%28v=vs.120%29.aspx</a></p>Anonymoushttp://www.blogger.com/profile/01367532666266117220noreply@blogger.com0tag:blogger.com,1999:blog-3680720108347788153.post-18637271336530997242015-12-08T17:46:00.002+08:002015-12-08T17:46:21.673+08:00ASP.NET Tips #57 - Keep an eye on your server-side code<p>Practice good performance hygiene by keeping an eye on your server-side code with a tool like <a href="http://getglimpse.com/" target="_blank">Glimpse</a>. Glimpse inspects web requests as they happen, providing insights and tooling that reduce debugging time and empower every developer to improve their web applications.</p>Anonymoushttp://www.blogger.com/profile/01367532666266117220noreply@blogger.com0