<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet type="text/xsl" href="http://codebetter.com/utility/FeedStylesheets/rss.xsl" media="screen"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:wfw="http://wellformedweb.org/CommentAPI/"><channel><title>Karl Seguin</title><link>http://codebetter.com/blogs/karlseguin/default.aspx</link><description>.NET From Ottawa, Ontario - http://twitter.com/karlseguin/</description><dc:language>en</dc:language><generator>CommunityServer 2008.5 SP1 (Build: 31106.3070)</generator><item><title>ASP.NET Performance Framework </title><link>http://codebetter.com/blogs/karlseguin/archive/2010/03/19/asp-net-performance-framework.aspx</link><pubDate>Fri, 19 Mar 2010 15:10:00 GMT</pubDate><guid isPermaLink="false">d21fbbc9-c112-4f32-ad14-95939a2c53d4:688506</guid><dc:creator>karl</dc:creator><slash:comments>4</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://codebetter.com/blogs/karlseguin/rsscomments.aspx?PostID=688506</wfw:commentRss><comments>http://codebetter.com/blogs/karlseguin/archive/2010/03/19/asp-net-performance-framework.aspx#comments</comments><description>&lt;p&gt;At the start of the year, I finished a 5 part series on ASP.NET performance - focusing on largely generic ways to improve website performance rather than specific ASP.NET performance tricks. The series focused on a number of topics, including &lt;a href="http://codebetter.com/blogs/karlseguin/archive/2008/12/29/compressing-js-files-as-part-of-your-build-process.aspx"&gt;merging and shrinking files&lt;/a&gt;, &lt;a href="http://codebetter.com/blogs/karlseguin/archive/2010/01/08/asp-net-performance-part-2-yslow.aspx"&gt;using modules to remove unecessary headers and setting caching headers&lt;/a&gt;, &lt;a href="http://codebetter.com/blogs/karlseguin/archive/2010/01/11/asp-net-performance-part-3-cache-busting.aspx"&gt;enabling cache busting&lt;/a&gt; and &lt;a href="http://codebetter.com/blogs/karlseguin/archive/2010/01/15/asp-net-performance-part-3a-better-cache-busting.aspx"&gt;automatically generating cache busted referneces in css&lt;/a&gt;, as well as an &lt;a href="http://codebetter.com/blogs/karlseguin/archive/2010/01/13/asp-net-performance-part-5-nginx.aspx"&gt;introduction to nginx&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Yesterday I managed to put a number of those things together into a framework which I hope will make it easier for developer to leverage these best practices. The project is called Metsys.WebOp, and you can download it from &lt;a href="http://github.com/kseg/Metsys.WebOp"&gt;github&lt;/a&gt;. It comes with a sample application, which not only shows how to use the framework, but also has all the documentation you&amp;#39;ll hopefully need.&lt;/p&gt;
&lt;p&gt;The first part of the framework is a console application which is meant to be run as part of your build process. This is driven by a text file which supports 4 commands - merging files together, shrinking them, generating cache busting hashes and pre-zipping files. Here&amp;#39;s what the sample app&amp;#39;s command file looks like:&lt;/p&gt;
&lt;pre&gt;#combine JS files
combine: js\all.js: js\jquery.js, js\jquery.rollover.js, js\web.js
 
#combine css files
combine: css\merged.css: css\reset.css, css\main.css
 
#shrink the generated files
shrink: js\all.js, css\merged.css
 
#generate cache-busting resource hashes
busting: hashes.dat
 
#pre-zip our files
zip: js\all.js, css\merged.css
&lt;/pre&gt;
&lt;p&gt;The next part is meant to be used from within a MVC application (it wouldn&amp;#39;t take too much effort for someone to make it work with WebForms) - first by allowing you to configure the runtime component, and then by providing extension methods to HtmlHelper. Essentially this gives you 4 methods, &lt;code&gt;Html.IncludeJs&lt;/code&gt;, &lt;code&gt;Html.IncludeCss&lt;/code&gt;, &lt;code&gt;Html.Image&lt;/code&gt; and &lt;code&gt;Html.ImageOver&lt;/code&gt;. You can also toggle debug mode, which&amp;#39;ll make all of this transparent during development (nothing worse than dealing with merged and shrank files in development).&lt;/p&gt;
&lt;p&gt;The last part are a couple HttpModule which make everthing possible. The Zip module will returned the pre-zipped files (generated by the build process) should the browser accept zip files. The WebOp module will remove unecessary headers and add caching headers to js, css and images - only really practical if you are also using the cache busting featuers.&lt;/p&gt;
&lt;p&gt;You can download the project from &lt;a href="http://github.com/kseg/Metsys.WebOp"&gt;http://github.com/kseg/Metsys.WebOp&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;You might also be interested in checking out &lt;a href="http://mscd.codeplex.com/"&gt; the mscd project&lt;/a&gt;, which does a lot of the same stuff, but is probably more mature.&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://codebetter.com/aggbug.aspx?PostID=688506" width="1" height="1"&gt;</description></item><item><title>WebForms vs MVC (again)</title><link>http://codebetter.com/blogs/karlseguin/archive/2010/03/11/webforms-vs-mvc-again.aspx</link><pubDate>Thu, 11 Mar 2010 16:12:00 GMT</pubDate><guid isPermaLink="false">d21fbbc9-c112-4f32-ad14-95939a2c53d4:669981</guid><dc:creator>karl</dc:creator><slash:comments>31</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://codebetter.com/blogs/karlseguin/rsscomments.aspx?PostID=669981</wfw:commentRss><comments>http://codebetter.com/blogs/karlseguin/archive/2010/03/11/webforms-vs-mvc-again.aspx#comments</comments><description>&lt;p&gt;There&amp;#39;s a &lt;a href="http://www.asp.net/learn/videos/video-9639.aspx"&gt;new video&lt;/a&gt; up on www.asp.net which aims to help developers pick between ASP.NET WebForms and ASP.NET MVC. The video boils down to 5 benefits per technology which Microsoft thinks you should consider.&lt;/p&gt;
&lt;p&gt;Let&amp;#39;s go over the points, shall we? First, ASP.NET WebForms:&lt;/p&gt;
&lt;h4 style="margin-bottom:0px;padding-bottom:0px;"&gt;1 - Familiar control and event base programming model&lt;/h4&gt;
&lt;p&gt;The claim here is that the ASP.NET model is comfortable for WinForm programmers (thankfully this unbiased analysis left out &lt;b&gt;who&lt;/b&gt; it&amp;#39;s more familiar for). This is largely accurate, but disingenuous. The differences between web and desktop cannot be overstated nor can one overstate how bad ASP.NET&amp;#39;s (or any other framework) is at hiding the difference. &amp;quot;Familiar&amp;quot; is probably the right word to use so long as you recognize that, in this case, at best it means: superficial; at worst: a serious pain in the ass. Your knowledge of building a VB6 app will allow you to write a &amp;quot;Hello World&amp;quot; web application - great. &lt;/p&gt;
&lt;p&gt;Familiarity can be a liability when it tries to force a square peg into a round hole.&lt;/p&gt;
&lt;p&gt;It also largely relies on your inability (or unwillingness) to learn. Today, next month or even next year may not be the right time for you to learn something new - that&amp;#39;s fine. Eventually, sticking with what you know, only because you know it, will kill your career and possibility part of your spirit.&lt;/p&gt;
&lt;h4 style="margin-bottom:0px;padding-bottom:0px;"&gt;2 - Controls encapsulate HTML, JS and CSS&lt;/h4&gt;
&lt;p&gt;It&amp;#39;s true that in ASP.NET WebForms controls can, and frequently do, encapsulate HTML, JS and CSS. How this ads &amp;quot;value&amp;quot; is beyond me. You can&amp;#39;t, and shouldn&amp;#39;t, be trying to build website without a solid command of HTML, JS and CSS. Whatever programming language and framework you use, the ultimate output of any website is HTML, CSS and JavaScript. Your server code essentially generates a stream of characters, which a browser loads and renders. To suggest, or think, that generating HTML, CSS or JavaScript in C# has any advantage is insane. It&amp;#39;ll be more complicated to learn, do and maintain - and the end result will be inferior. Its like saying we should write C# in VB.NET; or drive cars by bolting planes to the roof and getting in the cockpit.&lt;/p&gt;
&lt;h4 style="margin-bottom:0px;padding-bottom:0px;"&gt;3 - Rich UI controls included - datagrids, charts, AJAX.&lt;/h4&gt;
&lt;p&gt;Point 3 is a different perspective on point 2, which is a different way of saying point 1. However, it is the most interesting and important perspective. Fancy tables and charts, as well as client-side behavior, shouldn&amp;#39;t be a server-side concern. This is fundamental to what we all know about good and bad design. Classic ASP was a mess because it intermingled presentation code with server side code. The value of WebForms is that presentation logic is now a server side concern. Do you really believe this? Would you consider generating your HTML from a stored procedure? &lt;/p&gt;
&lt;p&gt;The claim also implies that by using ASP.NET MVC, you won&amp;#39;t be able to have a rich UI. In truth, you won&amp;#39;t only have access to a wider range of controls; you&amp;#39;ll also avoid a bunch of poor abstractions, and generate JavaScript by writing JavaScript, css by writing css and html by writing html.&lt;/p&gt;
&lt;h4 style="margin-bottom:0px;padding-bottom:0px;"&gt;4 - Browser differences handled for you&lt;/h4&gt;
&lt;p&gt;I&amp;#39;m guessing that the claim is that some of the controls mentioned in point 3 might render different HTML based on the requesting browser. Guess what, most jQuery (or any other js framework) plug-ins are fully compatible with all relevant browsers because they too can generate different HTML. In fact, doing this on the client side is almost always better - since you can tell the exact capabilities of a browser.&lt;/p&gt;
&lt;p&gt;Also, it would probably be better if you generated correct HTML, CSS and JS in the first place - something you can&amp;#39;t normally control using ASP.NET WebForms. So not only is this really just a benefit because IE is a pain, but its only worth mentioning because points #1, #2 and #3 mean that you&amp;#39;ve lost complete control over doing it right in the first place.&lt;/p&gt;
&lt;h4 style="margin-bottom:0px;padding-bottom:0px;"&gt;5 - SharePoint builds on WebForms&lt;/h4&gt;
&lt;p&gt;Yes, if you use SharePoint, you&amp;#39;ll have to use WebForms. &lt;/p&gt;
&lt;p&gt;Now on to ASP.NET MVC:&lt;/p&gt;
&lt;h4 style="margin-bottom:0px;padding-bottom:0px;"&gt;1 - Feels comfortable for many traditional web developers&lt;/h4&gt;
&lt;p&gt;ASP.NET WebForms is familiar while ASP.NET MVC is comfortable - that&amp;#39;s helpful. Nonetheless, when I see &amp;quot;traditional&amp;quot; I think &amp;quot;not-modern&amp;quot;. A more honest counterpoint to the WebForms claim would be: &amp;quot;A more natural way to build web applications&amp;quot;. WebForms tried to help WinForm developer&amp;#39;s transition to the web. ASP.NET MVC is a model that better reflect the realities of programming on the web. It&amp;#39;s more than just comfort, and has nothing to do with tradition.&lt;/p&gt;
&lt;h4 style="margin-bottom:0px;padding-bottom:0px;"&gt;2 - Total control of HTML markup&lt;/h4&gt;
&lt;p&gt;HTML, JS and CSS are yours to command. That doesn&amp;#39;t mean you can&amp;#39;t use controls to speed up development and improve your applications. The way this is worded sure makes it sound like MVC is a lot more work than WebForms though. It isn&amp;#39;t.&lt;/p&gt;
&lt;h4 style="margin-bottom:0px;padding-bottom:0px;"&gt;3 - Supports Unit Testing, TDD and Agile methodologies&lt;/h4&gt;
&lt;p&gt;I&amp;#39;m not sure what the technology stack has to do with the development methodology, so we&amp;#39;ll just ignore the last part. That aside, its true that MVC makes it possible to unit test your code. The counter point to that is that WebForms is essentially impossible to unit test. This also understates the architectural superiority and design of MVC - its doesn&amp;#39;t only allow you to leverage a number of best practices, it itself is actually built around those same practices. Code that can be unit tested, regardless of whether it is or not, is almost always superior to code that cannot.&lt;/p&gt;
&lt;h4 style="margin-bottom:0px;padding-bottom:0px;"&gt;4 - Encourages more prescriptive applications&lt;/h4&gt;
&lt;p&gt;So if ASP.NET MVC lets you build you application the way you should be building it, should you infer that ASP.NET WebForm forces you to build applications the wrong way? Yes, you should.&lt;/p&gt;
&lt;h4 style="margin-bottom:0px;padding-bottom:0px;"&gt;5 - Extremely flexible and extensible&lt;/h4&gt;
&lt;p&gt;Both frameworks share this value - but ASP.NET MVC is more about building on top of existing code, while ASP.NET WebForms is more about hacking things until they work. If you think this means that ASP.NET MVC can only be useful once you&amp;#39;ve extended it, then you are wrong. It works great out of the box is is feature rich.&lt;/p&gt;
&lt;h4 style="margin-bottom:0px;padding-bottom:0px;"&gt;Other Stuff&lt;/h4&gt;
&lt;p&gt;The video goes on to make weird assertions, like the possibility of turning back and picking a different stack if you feel you&amp;#39;ve made the wrong choice because of how similar and how much infrastructure they share. The better solution is to pick the right technology because going back months or years into your project doesn&amp;#39;t sound like good advice to me.&lt;/p&gt;
&lt;p&gt;It also mentions that it&amp;#39;s common to have some pages handled by MVC and others by WebForms. It&amp;#39;s good to know that you can do this - especially since it&amp;#39;s a good way to upgrade from WebForms to MVC. However, I&amp;#39;d hardly call it common or even recommended. It&amp;#39;s a useful transitional tool which you should aim to get out of as quickly as possible.&lt;/p&gt;
&lt;p&gt;Ultimately, the first 4 values of WebForms all boil down to the same thing: there&amp;#39;s a &lt;code&gt;System.Web.UI&lt;/code&gt; namespace which represents the wrong way to build a web app. There are good reasons to pick WebForms - but they all come down to time and practicalities of learning new things (and SharePoint).  I won&amp;#39;t tell you that you have to learn MVC, because that may not be practical for you. I&amp;#39;ll repeat what I&amp;#39;ve said before, ASP.NET MVC and WebForms DO NOT serve different purposes and one is not better suited for a particular type of application than the other(except SharePoint). They are completely overlapping technologies, and ASP.NET MVC is superior to WebForms.&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://codebetter.com/aggbug.aspx?PostID=669981" width="1" height="1"&gt;</description></item><item><title>The 8th Phase</title><link>http://codebetter.com/blogs/karlseguin/archive/2010/03/09/the-8th-phase.aspx</link><pubDate>Tue, 09 Mar 2010 15:27:00 GMT</pubDate><guid isPermaLink="false">d21fbbc9-c112-4f32-ad14-95939a2c53d4:669689</guid><dc:creator>karl</dc:creator><slash:comments>11</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://codebetter.com/blogs/karlseguin/rsscomments.aspx?PostID=669689</wfw:commentRss><comments>http://codebetter.com/blogs/karlseguin/archive/2010/03/09/the-8th-phase.aspx#comments</comments><description>&lt;p&gt;I once posted a semi-serious post entitled &lt;a href="http://codebetter.com/blogs/karlseguin/archive/2009/04/27/the-7-phases-of-unit-testing.aspx"&gt;The 7 Phases of Unit Testing&lt;/a&gt;. The phases are:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Refuse to unit test because &amp;quot;you don&amp;#39;t have enough time&amp;quot;&lt;/li&gt;
&lt;li&gt;Start unit testing and immediately start blogging about unit testing and TDD and how great they are and how everyone should do it&lt;/li&gt;
&lt;li&gt;Unit test everything - make private methods internal and abuse the InternalsVisibleTo attribute. Test getters and setters or else you won&amp;#39;t get 100% code coverage&lt;/li&gt;
&lt;li&gt;Get fed with how brittle your unit tests are and start writing integration tests without realizing it.&lt;/li&gt;
&lt;li&gt;Discover a mocking framework and make heavy use of strict semantics&lt;/li&gt;
&lt;li&gt;Mock absolutely everything that can possibly be mocked&lt;/li&gt;
&lt;li&gt;Start writing effective unit tests&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I think the cycle I went through is extremely healthy - unit testing is something best learnt from practice and is something you refine over time. Judging by the comments on the original post, a lot of you agree.&lt;/p&gt;
&lt;p&gt;Recently though, I&amp;#39;ve felt like adding another stage:&lt;/p&gt;
&lt;blockquote&gt;8 . Sometimes the best tests aren&amp;#39;t unit tests&lt;/blockquote&gt;
&lt;p&gt;After awhile, it becomes obvious that some tests are significantly more meaningful when you expand your scope - say to include hitting an actual database. Narrow unit tests and wider integration tests can always work together; but I&amp;#39;ve found that, in some cases, more comprehensive tests can replace corresponding unit tests. This may not be a proper, but it is practical. &lt;/p&gt;
&lt;p&gt;When I say unit test, I mean the smallest possible unit of code - generally a behavior. Most methods are made up of 1 or more behavior. I&amp;#39;d say that you shouldn&amp;#39;t have too many methods with more than 6 behaviors (as a rough goal). As an obvious example, in NoRM this method helps identify the type of the items in a collection:&lt;/p&gt;
&lt;pre name="code" class="c-sharp"&gt;public static Type GetListItemType(Type enumerableType)
{
    if (enumerableType.IsArray)
    {
        return enumerableType.GetElementType();
    }
    if (enumerableType.IsGenericType)
    {
        return enumerableType.GetGenericArguments()[0];
    }
    return typeof(object);
}&lt;/pre&gt;
&lt;p&gt;Clearly, this method is a good candidate for 3 or 4 unit tests (one when the type is an array, one when it&amp;#39;s a generic, one when its something else, and maybe one when its null).&lt;/p&gt;
&lt;p&gt;As your code moves closer to the boundaries of 3rd party components, the value of unit testing may suffer. You&amp;#39;ll still get the benefits of flushing out coupling and enabling safe refactoring (which shouldn&amp;#39;t be underestimated), but you&amp;#39;ll likely miss out on making sure things will work like they should in production. The solution can be to expand the scope of your tests to include the 3rd party component.&lt;/p&gt;
&lt;p&gt;The most common example is database code. Testing a &lt;code&gt;Save&lt;/code&gt; method by mocking the underlying layer might work, but there&amp;#39;s value in making sure that the object actually does get saved. That isn&amp;#39;t to say that a single test that hits the database is good enough - your &lt;code&gt;Save&lt;/code&gt; method might be made up of multiple behaviors, some which are better validated with one form of testing than another.&lt;/p&gt;
&lt;p&gt;Really, that&amp;#39;s one of the key things to remember as you walk down this path - don&amp;#39;t think that just because your method is actually saving an object that your job is done. There are likely other behaviors that aren&amp;#39;t being tested at all. It&amp;#39;s easy to abuse these types of tests and get a false sense of security. The other key is to make sure that it runs like a unit test - namely, that its fast, doesn&amp;#39;t require any manual setup, and isn&amp;#39;t dependent or doesn&amp;#39;t break any other test.&lt;/p&gt;
&lt;p&gt;Lately, I&amp;#39;ve seen interest in using in-memory databases for this type of thing. The benefit is that they are super fast and don&amp;#39;t leave stale data. They also don&amp;#39;t require special setup. On the downside you still aren&amp;#39;t truly testing the most fundamental behavior of your method - that an object will be saved to the database in production. Even with the best O/R tool I&amp;#39;ve seen code work against one database but not work against another - due to a bug on my part. Writing a script that can automatically and quickly setup and teardown against the final database, and having your team members set up a local database, may or may not work for you (it&amp;#39;ll depend on the nature of your team and your system).&lt;/p&gt;
&lt;p&gt;Ultimately, the most important thing is that you have automated tests which aren&amp;#39;t a nightmare to setup, maintain or run. Integration tests have more dependency and thus are more fragile, but can be an efficient way to verify correctness.&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://codebetter.com/aggbug.aspx?PostID=669689" width="1" height="1"&gt;</description></item><item><title>BSON Serialization</title><link>http://codebetter.com/blogs/karlseguin/archive/2010/03/05/bson-serialization.aspx</link><pubDate>Fri, 05 Mar 2010 18:56:00 GMT</pubDate><guid isPermaLink="false">d21fbbc9-c112-4f32-ad14-95939a2c53d4:664108</guid><dc:creator>karl</dc:creator><slash:comments>3</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://codebetter.com/blogs/karlseguin/rsscomments.aspx?PostID=664108</wfw:commentRss><comments>http://codebetter.com/blogs/karlseguin/archive/2010/03/05/bson-serialization.aspx#comments</comments><description>&lt;p&gt;BSON is a binary-encoded serialization of JSON-like documents, which essentially means its an efficient way of transfering information. Part of my work on the MongoDB &lt;a href="http://github.com/atheken/NoRM"&gt;NoRM drivers&lt;/a&gt;, &lt;a href="http://blog.wekeroad.com/2010/03/04/using-mongo-with-linq"&gt;discussed in more details by Rob Conery&lt;/a&gt;, is to write an efficient and maintainable BSON serializer and deserializer. The goal of the serializer is that you give it a .NET object and you get a byte array out of it which represents valid BSON. The deserializer does the opposite - give it a byte array and out pops your object. Of course, there are limits to what they can do - they are mostly meant to be used against POCO/domain entities.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Grammar&lt;/b&gt;&lt;br /&gt;
The first thing to understand when building serializers is how to read grammar. In programming languages, grammar is a way to express the valid keywords and values a parser might run into. Both the JSON and BSON grammars are great to learn, given how simplistic yet powerful they are. The JSON grammar, available on the homepage of &lt;a href="http://json.org"&gt;json.org&lt;/a&gt; gives a nice representation of what valid JSON should look like. The BSON grammar, available at &lt;a href="http://bsonspec.org"&gt;bsonspec.org&lt;/a&gt; under the specification button, follows a more traditional dialect. Essentially, you have symbols on the left and expressions on the right. The expressions can, and often will be, made up of additional symbols and or actual values. Eventually though, you&amp;#39;ll end up with a symbol which is only made up of values - which means you can stop going down the rabbit hole. Its also very common for a child symbol to reference a parent symbol - but eventually something breaks this cycle.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;An Example&lt;/b&gt;&lt;br /&gt;
So, say we wanted to serialize the following json:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;{&amp;quot;valid&amp;quot;: true}&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Everything in BSON starts with a &lt;code&gt;document&lt;/code&gt;. From the bson specification, we can see that a &lt;code&gt;document&lt;/code&gt; is made up of a 32bit integer (representing the total size of the document, including the integer itself), another symbol called an &lt;code&gt;e_list&lt;/code&gt;, and finally a termination character. As a start, we&amp;#39;d have something like:&lt;/p&gt;
&lt;p&gt;&lt;img src="http://codebetter.com/resized-image.ashx/__size/518x50/__key/CommunityServer.Blogs.Components.WeblogFiles/karlseguin/bson1.gif" border="0" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;Now, an e_list itself is made up of a symbol called an &lt;code&gt;element&lt;/code&gt; followed by another &lt;code&gt;e_list&lt;/code&gt; or an blank string. An &lt;code&gt;element&lt;/code&gt; is made up of a single byte type (with &lt;code&gt;\x08&lt;/code&gt; representing a boolean), a symbol called &lt;code&gt;e_name&lt;/code&gt; and a byte value for true or false. So now we have:&lt;/p&gt;
&lt;p&gt;&lt;img src="http://codebetter.com/resized-image.ashx/__size/376x49/__key/CommunityServer.Blogs.Components.WeblogFiles/karlseguin/bson2.gif" border="0" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;The only thing missing now is our &lt;code&gt;e_name&lt;/code&gt; (which represents the word &amp;quot;valid&amp;quot; in the original JSON). An &lt;code&gt;e_name&lt;/code&gt; is really just a &lt;code&gt;cstring&lt;/code&gt; which is our value UTF8 encoded into an array of bytes with a trailing byte of &lt;code&gt;\x00&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img src="http://codebetter.com/resized-image.ashx/__size/283x61/__key/CommunityServer.Blogs.Components.WeblogFiles/karlseguin/bson3.gif" border="0" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;Our final byte array looks something like:&lt;/p&gt;
&lt;p&gt;&lt;a href="http://codebetter.com/cfs-file.ashx/__key/CommunityServer.Blogs.Components.WeblogFiles/karlseguin/bson4.gif"&gt;&lt;img src="http://codebetter.com/resized-image.ashx/__size/550x49/__key/CommunityServer.Blogs.Components.WeblogFiles/karlseguin/bson4.gif" border="0" alt="" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Serializing a single bool value might be the simplest of cases, but once you understand that, you&amp;#39;re well on your way to being able to serialize anything. Sure, serializing an array might be a bit trickier, since each element within the array is its own document - but the challenge is mostly implementation versus conceptual.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;What&amp;#39;s the Length?&lt;/b&gt;&lt;br /&gt;
It may surprise you at first, but the most difficult part to implement is actually determining the length of a document (or various other symbols which have a length). The problem is that we don&amp;#39;t know the length until after we&amp;#39;ve serialized it. Some implementations will essentially serialize the object graph twice, first to calculate lengths, then to write out the array. In NoRM we do things more efficiently. We keep a linked list of documents, and a pointer to the current document. A document is a very simple object - it keeps track of where it started, who its parent is (null in the case of the root document) and how much data was written. When a new document is needed - say when we start serialization, or when the grammar dictates that we need a new document (arrays or nested objects), we mark where we are and write out a placeholder length. Then, when the document ends, we seek to our placeholder, and write out the length. The relevant code looks like:&lt;/p&gt;
&lt;pre class="c-sharp" name="code"&gt;  private void NewDocument()
  {
      var old = _current;
      //we start the Written at 4 because of the length itself
      _current = new Document { Parent = old, Start = (int)_writer.BaseStream.Position, Written  = 4};      
      _writer.Write(0); //length placeholder
  }
  private void EndDocument(bool includeEeo)
  {
      var old = _current;
      if (includeEeo)
      {
          Written(1);
          _writer.Write((byte)0);
      }            
      _writer.Seek(_current.Start, SeekOrigin.Begin);
      _writer.Write(_current.Written); //override the document length placeholder
      _writer.Seek(0, SeekOrigin.End); //back to the end
      _current = _current.Parent;
      if (_current != null)
      {
          Written(old.Written);
      }

  }
  private void Written(int length)
  {
      _current.Written += length;
  }&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;EndDocument&lt;/code&gt; is pretty interesting. Since the length of a nested document contributes to the length of the parent document, we need to make sure to update the parent (now current) document with the length of the nested one.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Conclusion&lt;/b&gt;&lt;br /&gt;
Everything else is pretty straightforward in terms of serialization - largely reliant on reflection and reflection helpers. We use Jon Skeets &lt;a href="http://msmvps.com/blogs/jon_skeet/archive/2008/08/09/making-reflection-fly-and-exploring-delegates.aspx"&gt;reflection to delegate approach&lt;/a&gt; to make things even faster (something I truthfully don&amp;#39;t fully understand). Currently our implementation has some coupling to other NoRM components. Hopefully one day the BSON stuff will be stand-alone. If you can&amp;#39;t wait, you can either use another library like &lt;a href="http://www.codeplex.com/Json"&gt;JSON.NET&lt;/a&gt; (which more mature anyways), or spend a few minutes (it shouldn&amp;#39;t take more than that) pulling out our serializer/deserializer.&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://codebetter.com/aggbug.aspx?PostID=664108" width="1" height="1"&gt;</description></item><item><title>I don't like MSDN</title><link>http://codebetter.com/blogs/karlseguin/archive/2010/02/11/i-don-t-like-msdn.aspx</link><pubDate>Thu, 11 Feb 2010 20:53:00 GMT</pubDate><guid isPermaLink="false">d21fbbc9-c112-4f32-ad14-95939a2c53d4:629483</guid><dc:creator>karl</dc:creator><slash:comments>30</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://codebetter.com/blogs/karlseguin/rsscomments.aspx?PostID=629483</wfw:commentRss><comments>http://codebetter.com/blogs/karlseguin/archive/2010/02/11/i-don-t-like-msdn.aspx#comments</comments><description>&lt;p&gt;I&amp;#39;ve long held that Microsoft could do better with its developer-focused sites. It isn&amp;#39;t a topic I&amp;#39;m likely to let up on, but today I wanted to look at a specific example.&lt;/p&gt;
&lt;p&gt;I was reading the PostgreSQL documentation on the ALTER INDEX command, and was generally pleased with everything about it. Its easy to read, informative, quick to load, and usable. Here&amp;#39;s a screenshot of the page as it first appears (or you can go there yourself at &lt;a href="http://www.postgresql.org/docs/8.1/interactive/sql-alterindex.html"&gt;http://www.postgresql.org/docs/8.1/interactive/sql-alterindex.html&lt;/a&gt;):&lt;/p&gt;
&lt;p&gt;

&lt;a href="http://www.flickr.com/photos/kawe/4349765574/sizes/l/"&gt;&lt;img src="http://farm5.static.flickr.com/4032/4349765574_98b43c3cc4.jpg" border="0" height="313" width="500" alt="" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The MSDN page for ALTER INDEX loads up looking like (or you can go there yourself at &lt;a href="http://msdn.microsoft.com/en-us/library/ms188388.aspx"&gt;http://msdn.microsoft.com/en-us/library/ms188388.aspx&lt;/a&gt;):&lt;/p&gt;
&lt;p&gt;
&lt;a href="http://www.flickr.com/photos/kawe/4349765568/sizes/l/"&gt;&lt;img src="http://farm3.static.flickr.com/2728/4349765568_b7e899f2b4.jpg" border="0" height="313" width="500" alt="" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Maybe its just me (is it?) but for me, the difference is night and day. I can put aside the poor color choice, and weird (and branded) search bar, and the overwhelming amount of navigation (actually, I find this quite annoying), but there are some fundamental things that make MSDN a pain to use.&lt;/p&gt;
&lt;p&gt;Some observations:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The MSDN breadcrumb is 12 levels deep, two lines, and each item brings up its own popup navigation. The PostgreSQL one is 4 levels deep and is plain.&lt;/li&gt;
&lt;li&gt;The MSDN has a Printer Friendly Version, the PostgreSQL one IS printer friendly.&lt;/li&gt;
&lt;li&gt;MSDN&amp;#39;s left-hand navigation is useless - it&amp;#39;s too big, too nested and uses up too much room&lt;/li&gt;
&lt;li&gt;The MSDN page took 46 requests, 226.5KB and took 5.44seconds to load for me. The PostgreSQL page took 16 requests, 21.3KB and took 1.46seconds to load&lt;/li&gt;
&lt;li&gt;The MSDN page hits 8 domains (including webtrends). The PostgreSQL page hits 2 domains (including google analytics)&lt;/li&gt;
&lt;li&gt;The MSDN page has a cryptic URL. The PostgreSQL has a human-readable url&lt;/li&gt;
&lt;li&gt;The MSDN page allows you to lookup other versions via a box in the middle. The PostgreSQL lets you change the URL.&lt;/li&gt;
&lt;li&gt;The MSDN page scores 67 on Yslow, PostgreSQL scores 76.&lt;/li&gt;
&lt;li&gt;The MSDN page is inconsistent about cross linking other topics, sometimes CREATE TABLE is linked and sometimes it isn&amp;#39;t. CREATE INDEX though is never linked. The PostgreSQL documentation is more consistent.&lt;/li&gt;
&lt;li&gt;Searching &amp;quot;Create Table&amp;quot; on MSDN brings up the SQL Server 2000 documentation (with no way to get to 2008). Searching &amp;quot;Create Table&amp;quot; on PostgreSQL brings up exactly what you want. (Both sites display search results poorly)&lt;/li&gt;
&lt;li&gt;MSDN&amp;#39;s Titles don&amp;#39;t include the version, so the 2005 and 2008 titles are the same (which means your bookmarks will be the same). PostgreSQL has stupid long titles, but at least the version is there&lt;/li&gt;
&lt;li&gt;The MSDN text is small and tight and ends up looking like a wall of text. The PostgreSQL looks like its meant for a human.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A note about the version selection. The MSDN approach may be more intuitive, but it appears limited to going from SQL Server 2008 to 2005, there&amp;#39;s no way to go the other way around, and no way to pick an older database. With PostgreSQL you can change the url for any minor version starting at 7.1. &lt;/p&gt;
&lt;p&gt;Of course, when everything&amp;#39;s said and done, despite all the small things (like # of dns lookups), the end result is that one is a mess, while the other isn&amp;#39;t. &lt;/p&gt;
&lt;p&gt;MSDN&amp;#39;s Lightweight and ScriptFree are definitely a step in the right direction - but this choice clearly falls in Barry Schwartz&amp;#39;s Choice is Bad For Us point of view - something a lot of people at Microsoft need to desperately be made aware of. I actually like ScriptFree, and it addresses a number of the above problems, but not enough and in the end it still lags behind PostgreSQL&amp;#39;s usability. I mean, why is the ridiculous syntax the first thing even displayed? Why do I still see a wall of text, and who actually thinks that because I&amp;#39;m reading up on ALTER INDEX, I&amp;#39;m interested in a bunch of truncated links to other ALTER statements?&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://codebetter.com/aggbug.aspx?PostID=629483" width="1" height="1"&gt;</description></item><item><title>An introduction to iptables</title><link>http://codebetter.com/blogs/karlseguin/archive/2010/02/03/an-introduction-to-iptables.aspx</link><pubDate>Wed, 03 Feb 2010 14:36:00 GMT</pubDate><guid isPermaLink="false">d21fbbc9-c112-4f32-ad14-95939a2c53d4:618149</guid><dc:creator>karl</dc:creator><slash:comments>5</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://codebetter.com/blogs/karlseguin/rsscomments.aspx?PostID=618149</wfw:commentRss><comments>http://codebetter.com/blogs/karlseguin/archive/2010/02/03/an-introduction-to-iptables.aspx#comments</comments><description>&lt;p&gt;iptables is a linux application that allows you to configure the built-in linux firewall. There are abstraction layers built on top of it, including nice GUIs, but I recently had some very basic rules I wanted to implement, and figured I&amp;#39;d learn how to do it directly in iptables. It turned out to be easy to configure, and I thought I&amp;#39;d give a very basic introduction.&lt;/p&gt;
&lt;p&gt;First though, I relied on the ubuntu documentation to get up and running (&lt;a href="https://help.ubuntu.com/community/IptablesHowTo"&gt;https://help.ubuntu.com/community/IptablesHowTo&lt;/a&gt;). I&amp;#39;ll pretty much just regurgitate what&amp;#39;s in there - I&amp;#39;ve said it before, stop believing that linux documentation is written only for people who already know linux. Also, depending on your setup, most of these commands might require elevation (sudo).&lt;/p&gt;
&lt;p&gt;By default, iptables defines three rule chains: &lt;code&gt;INPUT&lt;/code&gt;, &lt;code&gt;FORWARD&lt;/code&gt; and &lt;code&gt;OUTPUT&lt;/code&gt;. We&amp;#39;ll only concern ourselves with &lt;code&gt;INPUT&lt;/code&gt; - namely the chain used for incoming packets directed at this particular machine. At any point, you can list the rules by entering &lt;code&gt;iptables -L&lt;/code&gt;. If listing the rules turns out to be really slow, try using &lt;code&gt;iptables -L -n&lt;/code&gt;. The &lt;code&gt;-n&lt;/code&gt; option means that the display will output IP addresses and ports in numeric format, without trying to do a DNS lookup. If the &lt;code&gt;-n&lt;/code&gt; option makes it fast, then you&amp;#39;re system likely has incorrect DNS settings (you can check out &lt;a href="http://www.cyberciti.biz/tips/linux-how-to-setup-as-dns-client.html"&gt;http://www.cyberciti.biz/tips/linux-how-to-setup-as-dns-client.html&lt;/a&gt; for help with that).&lt;/p&gt;
&lt;p&gt;In my particular case, the server is has two network interfaces - a public facing one (on &lt;code&gt;eth0&lt;/code&gt;) and a private one (on &lt;code&gt;eth1&lt;/code&gt;). For my purposes, I want to remotely (&lt;code&gt;eth0&lt;/code&gt;) access SSH from my static IP, and locally (&lt;code&gt;eth1&lt;/code&gt;) access postgresql on port 5432.&lt;/p&gt;
&lt;p&gt;To accomplish this we simply append two rules to the INPUT chain:&lt;/p&gt;
&lt;pre&gt;iptables -A INPUT -p tcp --dport ssh -s 205.38.19.199 -i eth0 -j ACCEPT
iptables -A INPUT -p tcp --dport 5432 -i eth1 -j ACCEPT&lt;/pre&gt;
&lt;p&gt;The first rule appends itself to the &lt;code&gt;INPUT&lt;/code&gt; chain. Its limited to the &lt;code&gt;TCP&lt;/code&gt; protocol targeting the &lt;code&gt;SSH&lt;/code&gt; port (22) with a source originating from &lt;code&gt;205.38.19.199&lt;/code&gt; on the &lt;code&gt;eth0&lt;/code&gt; network interface. It jumps to an &lt;code&gt;ACCEPT&lt;/code&gt; status (all other processing stops). The next rule is similar, except it allows any source so long as they are connected to &lt;code&gt;eth0&lt;/code&gt; and targeting port &lt;code&gt;5432&lt;/code&gt; (over &lt;code&gt;tcp&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;We can now add a third rule to block all other access:&lt;/p&gt;
&lt;pre&gt;iptables -A INPUT -j DROP&lt;/pre&gt;
&lt;p&gt;if incoming traffic doesn&amp;#39;t mean the first two criteria, we&amp;#39;ll hit this third rule and the firewall will reject the connection.&lt;/p&gt;
&lt;p&gt;We&amp;#39;ll run into 1 issue with our above configuration though - we&amp;#39;ve blocked access to the loopback interface (127.0.0.1). We need to add a special rule to allow loopback connections (since a lot of programs rely on it). However, we can&amp;#39;t simply append it as it&amp;#39;ll never be reached. Instead of using &lt;code&gt;-a&lt;/code&gt; to append, we&amp;#39;ll use &lt;code&gt;-i&lt;/code&gt; to insert:&lt;/p&gt;
&lt;pre&gt;iptables -I INPUT 1 -i lo -j ACCEPT&lt;/pre&gt;
&lt;p&gt;Here we are inserting our rule in the 1st position, and accepting all connections made on the special lo (loopback) interface.&lt;/p&gt;
&lt;p&gt;A really neat thing about iptables is that they aren&amp;#39;t persisted by default. So, if you happen to mess up and cut yourself off from your server, a simple reboot will reset everything (hopefully you have access to do a remote reboot from a web interface, or maybe you can just walk up to the machine and reboot it).&lt;/p&gt;
&lt;p&gt;At some point though, you&amp;#39;ll want to make sure your rules are persisted. Achieving this is a two step process. First, you save your rules to a file by using the &lt;code&gt;iptables-save&lt;/code&gt; and redirecting STDOUT to a file:&lt;/p&gt;
&lt;pre&gt;iptables-save &amp;gt; /etc/iptables.rules&lt;/pre&gt;
&lt;p&gt;Second, and this depends on which distro you have but on debian/ubuntu, you make sure your rules are loaded on startup. Simply edit &lt;code&gt;/etc/network/interfaces&lt;/code&gt;, find your interface, and add:&lt;/p&gt;
&lt;pre&gt;pre-up iptables-restore &amp;lt; /etc/iptables.rules &lt;/pre&gt;
&lt;p&gt;to the list, so that it looks something like:&lt;/p&gt;
&lt;pre&gt;auto eth0
iface eth0 inet static
address xxx
netmask yyy
gateway ddd
pre-up iptables-restore &amp;lt; /etc/iptables.rules&lt;/pre&gt;
&lt;p&gt;That&amp;#39;s it!&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://codebetter.com/aggbug.aspx?PostID=618149" width="1" height="1"&gt;</description></item><item><title>Beyond web.config</title><link>http://codebetter.com/blogs/karlseguin/archive/2010/01/28/beyond-web-config.aspx</link><pubDate>Thu, 28 Jan 2010 14:43:00 GMT</pubDate><guid isPermaLink="false">d21fbbc9-c112-4f32-ad14-95939a2c53d4:611421</guid><dc:creator>karl</dc:creator><slash:comments>23</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://codebetter.com/blogs/karlseguin/rsscomments.aspx?PostID=611421</wfw:commentRss><comments>http://codebetter.com/blogs/karlseguin/archive/2010/01/28/beyond-web-config.aspx#comments</comments><description>&lt;p&gt;I recently needed a configuration mechanism which would detect changes without requiring an application domain restart. I also wanted to move away from XML. This is what I came up with (and hopefully I&amp;#39;ll get some helpful feedback).&lt;/p&gt;
&lt;p&gt;First, we declare a ConfigurationData object which holds our actual configuration values:&lt;/p&gt;
&lt;pre class="c-sharp" name="code"&gt;public interface IConfigurationData
{
   bool LogAll{ get; }
   string CdnUrl{ get; }
}

public class ConfigurationData : IConfigurationData
{
   private bool _logAll;
   private int _cdnUrl;
   
   public bool LogAll
   {
      get { return _logAll; }
      set{ _logAll = value; }
   }
   public string CdnUrl
   {
      get { return _cdnUrl; }
      set { _cdnUrl = value; }
   }
}&lt;/pre&gt;
&lt;p&gt;There isn&amp;#39;t much to explain here, so let&amp;#39;s move on to the class that does the heavy lifting:&lt;/p&gt;
&lt;pre class="c-sharp" name="code"&gt;public static class Configuration
{
   private static readonly string _applicationPath = HttpRuntime.AppDomainAppPath;
   private static ConfigurationData _instance = LoadInitialConfiguration();

   public static IConfigurationData GetInstance
   {
      get { return _instance; }
   }

   private static ConfigurationData LoadInitialConfiguration()
   {
      var watcher = new FileSystemWatcher(_applicationPath, &amp;quot;settings.config&amp;quot;);
      watcher.NotifyFilter = NotifyFilters.LastWrite;
      watcher.Changed += (s, e) =&amp;gt; _instance = LoadConfiguration();
      watcher.EnableRaisingEvents = true;
      return LoadConfiguration();
   }
   private static ConfigurationData LoadConfiguration()
   {
      return Converter.DeserializeFromFile&amp;lt;ConfigurationData&amp;gt;(_applicationPath + &amp;quot;settings.config&amp;quot;, &amp;quot;_&amp;quot;);
   }        
}&lt;/pre&gt;
&lt;p&gt;The class essentially loads the &lt;code&gt;~settings.config&lt;/code&gt; file, and sets up a watch to reload it whenever it changes. Here I&amp;#39;m using JSON (and my &lt;a href="http://codeplex.com/jsoncf"&gt;own JSON library&lt;/a&gt;) to read the file, but you could use anything, such as yaml or xml. The file might look something like:&lt;/p&gt;
&lt;pre&gt;{
   &amp;quot;logAll&amp;quot;: true,
   &amp;quot;_cdnUrl&amp;quot;: &amp;quot;http://cdn.mysite.com/&amp;quot;
}&lt;/pre&gt;
&lt;p&gt;If you are using a DI framework, such as ninject, you can hook it via:&lt;/p&gt;
&lt;pre class="c-sharp" name="code"&gt;Bind&amp;lt;IConfigurationData&amp;gt;().ToMethod(c =&amp;gt; Configuration.GetInstance);&lt;/pre&gt;
&lt;p&gt;otherwise, you can call it directly from code with:&lt;/p&gt;
&lt;pre class="c-sharp" name="code"&gt;if (Configuration.GetInstance.LogAll) {...}&lt;/pre&gt;
&lt;p&gt;There are two things you&amp;#39;ll need to be careful with. First you&amp;#39;ll want to make sure to avoid caching values within a class. For example, if you did something like:&lt;/p&gt;
&lt;pre class="c-sharp" name="code"&gt;public static class HtmlExtensions
{
   private static readonly string _cdnUrl = Configuration.GetInstance.CdnUrl;
}&lt;/pre&gt;
&lt;p&gt;then a copy of &lt;code&gt;CdnUrl&lt;/code&gt; would be stored in _cdnUrl and changes to your &lt;code&gt;&lt;/code&gt; file wouldn&amp;#39;t be available to this class.&lt;/p&gt;
&lt;p&gt;Secondly, the &lt;code&gt;Changed&lt;/code&gt; event is known to fire twice. This shouldn&amp;#39;t cause any threading issues, but if your LoadConfiguration is particularly intensive, or if you happen to be debugging something, then you&amp;#39;ll at least know to expect it.&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://codebetter.com/aggbug.aspx?PostID=611421" width="1" height="1"&gt;</description></item><item><title>Don't Use Try/Catch</title><link>http://codebetter.com/blogs/karlseguin/archive/2010/01/25/don-t-use-try-catch.aspx</link><pubDate>Mon, 25 Jan 2010 15:02:00 GMT</pubDate><guid isPermaLink="false">d21fbbc9-c112-4f32-ad14-95939a2c53d4:605747</guid><dc:creator>karl</dc:creator><slash:comments>41</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://codebetter.com/blogs/karlseguin/rsscomments.aspx?PostID=605747</wfw:commentRss><comments>http://codebetter.com/blogs/karlseguin/archive/2010/01/25/don-t-use-try-catch.aspx#comments</comments><description>&lt;p&gt;Exception handling has been talked about a lot over the last decade and a half. However, despite a general consensus on how to properly handle exceptions, a divide on usage continues to exist. Improper exception handling is easy to spot, easy to avoid, and is a simple code (and developer) quality metric. I know absolute rules come off as close minded or exaggerated, but as a &lt;b&gt;general&lt;/b&gt; rule you shouldn&amp;#39;t be using try/catch.&lt;/p&gt;
&lt;p&gt;If an exception happens, you need to know about it. If a truly unexpected exception happens, you&amp;#39;re better off (most of the time) crashing than letting the application continue. This is particularly true in a web application where you&amp;#39;ll only crash 1 thread for 1 user while your site remains accessible. The best way to achieve both is let the exception go unhandled and log the exception in a global exception handler. Too often I see developer writing code which masks errors and make it impossible to build a quality system. Users complain about system instability, and developers are left scratching their heads with no leads on what the problems are.&lt;/p&gt;
&lt;p&gt;What&amp;#39;s even more frustrating is when framework code swallows exceptions. Take a look at this example, written by Microsoft:&lt;/p&gt;
&lt;pre class="c-sharp" name="code"&gt;private void LoadImage()
{
    if (this.image != null)
    {
        this.image.Dispose();              
    }
    

    if (source != null)
    {                
        try
        {                  
            source = Path.Combine(Canvas.ApplicationPath, source);                    
            image = new Bitmap(source);                  

           IImagingFactory factory = ImagingFactory.GetImaging();
           factory.CreateImageFromFile(source, out imagingImage);
        }
        catch{}                                                      
    }
}&lt;/pre&gt;
&lt;p&gt;This is a fundamental piece of code which is part of Microsoft&amp;#39;s UI Framework for .NET Compact Framework. The above code is buried within their framework, but through various key APIs is exposed and used extensively. This is simply horrible code. Why would they swallow any exceptions that come out of this code? The above code means that if you try to load an image &lt;code&gt;&amp;quot;images/critical_warning.png&amp;quot;&lt;/code&gt; which doesn&amp;#39;t exist, the application will continue to work normally. There may be cases where this is the behavior that you want, but those would be edge cases, and they should be decided at the application level, not within the framework.&lt;/p&gt;
&lt;p&gt;The only time you should swallow exceptions (&lt;code&gt;catch{..} or catch(Exception){...}&lt;/code&gt;) is in your global exception handler to avoid truly fundamental issues (and recursive global exception handling). You should catch &lt;b&gt;specific&lt;/b&gt; exception (&lt;code&gt;catch(XmlException){...}&lt;/code&gt;) when an exception isn&amp;#39;t unexpected and you know that you can safely proceed. &lt;/p&gt;
&lt;p&gt;A system that swallows and masks exceptions isn&amp;#39;t a healthy system.&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://codebetter.com/aggbug.aspx?PostID=605747" width="1" height="1"&gt;</description></item><item><title>The WebForms Rant</title><link>http://codebetter.com/blogs/karlseguin/archive/2010/01/22/the-webforms-rant.aspx</link><pubDate>Fri, 22 Jan 2010 19:15:00 GMT</pubDate><guid isPermaLink="false">d21fbbc9-c112-4f32-ad14-95939a2c53d4:605438</guid><dc:creator>karl</dc:creator><slash:comments>58</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://codebetter.com/blogs/karlseguin/rsscomments.aspx?PostID=605438</wfw:commentRss><comments>http://codebetter.com/blogs/karlseguin/archive/2010/01/22/the-webforms-rant.aspx#comments</comments><description>&lt;p&gt;Ever since ASP.NET MVC was announced/released, there&amp;#39;s been a lot of talk about it with respect to ASP.NET WebForms. People want to know which technology they should use, and are likely confused by the fact that there isn&amp;#39;t a consensus amongst respected developers with respect to which of the two is better.&lt;br /&gt;&lt;br /&gt;Ideally every developer would take the time to truly understand ASP.NET MVC and compare that with their WebForms experience to come up with their own opion (and even more ideally we&amp;#39;d all largely agree on the pros and cons). The reality though is that some developers don&amp;#39;t have the time or opportunity to take the necessary time to learn a new framework. If we are being completely honest, many developers don&amp;#39;t have the necessary skill to accurately judge two competing technologies - maybe they are casual developers, maybe they are new to developing; maybe they come from a different technology. There&amp;#39;s nothing shameful about that - in fact, that&amp;#39;s how most of us learn.&lt;br /&gt;&lt;br /&gt;With that said, should you be using ASP.NET MVC instead of WebForms? The short is: continue using WebForms for any existing projects that you have that are working well (you can define for yourself what &amp;quot;working well&amp;quot; means). Use ASP.NET MVC for any other project.&lt;br /&gt;&lt;br /&gt;The long answer is the same, except it uses the word &amp;quot;stupid&amp;quot; and &amp;quot;idiots&amp;quot; a lot more. Here goes...&lt;br /&gt;&lt;br /&gt;So-called experts who claim that ASP.NET MVC and WebForms serve two different purposes are stupid idiots. Seriously. They are both web frameworks, capable of producing the exact same systems. You could build a digg, facebook, twitter, joomla or any other website using either technology. What makes one (ASP.NET MVC) better than the other (ASP.NET WebForms) isn&amp;#39;t where you can go, but rather how you get there. ASP.NET WebForms is an ugly and messy framework that complicates an otherwise simple thing. ViewState, codebehind, postback, page lifecycle and databinding are things that you have to constantly program against. They don&amp;#39;t help you; they just slow you down, slow your site down, and make it harder to change things in the future.&lt;br /&gt;&lt;br /&gt;I&amp;#39;ve known programmers who thought that complex frameworks were required to build complex systems. I also know programmers who think you can just duct tape anything together. The truth is so much simpler, and so much better, that both those groups of programmers are idiots, and in fact can&amp;#39;t really program. Simplicity is the only way to build simple or complex systems in a clean and timely manner. Build a system too complex, and it&amp;#39;ll cost a fortune and be too rigid. Build something too quickly and shitty, and it&amp;#39;ll break and won&amp;#39;t be fixable. ASP.NET WebForms, along with ASP.NET Ajax, was clearly built by people who believe that complexity can solve all problems. Postback with server side events is such a poor solution to what&amp;rsquo;s a really simple problem. ViewState is a solution to a problem that doesn&amp;#39;t even exist (which makes it infinitely complex).&lt;br /&gt;&lt;br /&gt;When WebForms first came out, developers were overjoyed that their tangled code and html were a thing of the past. We were so stupid not to see what mess we were getting involved with (I&amp;#39;m sure some did). Get it through your head people, having ASPX pages loaded with non-html or javascript is an equally horrible situation to be in. Take your most complicated ASPX page and give it to someone who knows HTML (but not ASP.NET) - if they can&amp;#39;t understand it (which they won&amp;#39;t) or think it looks ridiculous (which they will) then you&amp;#39;re doing something wrong. Just because you are coding with angle brackets doesn&amp;#39;t make it clean code.&lt;br /&gt;&lt;br /&gt;Pro-WebFormers like to use a scare tactic to make sure developers don&amp;#39;t switch to ASP.NET MVC, they claim that ASP.NET MVC is suited for Test Driven Developer (TDD) specifically, and unit testing in general. This is a scare tactic that relies on the growing divide between those who do and those who don&amp;#39;t unit tests.&amp;nbsp; You can use ASP.NET MVC without writing a single unit tests, or knowing how to write a unit test, or knowing what a unit test is. It is true that ASP.NET MVC is easier to unit test than ASP.NET WebForms (which is next to impossible). Even if you don&amp;#39;t write unit tests, or know how to write unit tests, or know what a unit test is, believe me that the simple fact that ASP.NET MVC can be unit tested and WebForms can&amp;#39;t, is a very compelling reason to forget about WebForms. It means that ASP.NET MVC is better designed, is more flexible, has less coupling, and less magic shit which will break.&lt;br /&gt;&lt;br /&gt;If someone tells you that ASP.NET MVC is more complicated, requires more heavy lifting or is less productive, they are trying to tell you a lie. Of course there&amp;#39;ll be the lost productivity of having to learn something new. But I promise you that mastering ASP.NET MVC will take a fraction of the time its taken you to master the clusterfuck known as ASP.NET WebForms (just like learning jQuery will take you less time than learning ASP.NET Ajax). Maybe you don&amp;#39;t have time to learn something new, that&amp;#39;s fine. Just don&amp;#39;t believe what you&amp;#39;ve been reading. ASP.NET MVC is simpler - unless you&amp;#39;re the type of developer who doesn&amp;#39;t like to learn new things.&lt;br /&gt;&lt;br /&gt;Now, I have my problems with ASP.NET MVC, which I&amp;#39;ve shared in the past. But those are problems with ASP.NET MVC with respect to other MVC frameworks.&lt;br /&gt;&lt;br /&gt;A framework that accepts that HTTP is stateless will always be simpler, cleaner and more powerful that a framework that doesn&amp;#39;t. A framework that allows unit testing, regardless of whehter you are going to unit tests, is better than one that doesn&amp;#39;t. WebForms is nasty.&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://codebetter.com/aggbug.aspx?PostID=605438" width="1" height="1"&gt;</description></item><item><title>ASP.NET Performance - Part 3a - Better Cache Busting</title><link>http://codebetter.com/blogs/karlseguin/archive/2010/01/15/asp-net-performance-part-3a-better-cache-busting.aspx</link><pubDate>Fri, 15 Jan 2010 14:53:00 GMT</pubDate><guid isPermaLink="false">d21fbbc9-c112-4f32-ad14-95939a2c53d4:592901</guid><dc:creator>karl</dc:creator><slash:comments>3</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://codebetter.com/blogs/karlseguin/rsscomments.aspx?PostID=592901</wfw:commentRss><comments>http://codebetter.com/blogs/karlseguin/archive/2010/01/15/asp-net-performance-part-3a-better-cache-busting.aspx#comments</comments><description>&lt;p&gt;There have been some good, informative, comments to this series. One in particular, from &lt;a href="http://www.onemoretake.com/"&gt;Dan Sargeant&lt;/a&gt;, deserves to be addressed directly - in large part because its something I overlooked but need to implement, so I might as well share the code.&lt;/p&gt;
&lt;p&gt;Dan points out that my cache busting technique has a fatal flaw - it doesn&amp;#39;t address images referenced from stylesheet. He&amp;#39;s right, and it&amp;#39;s a pretty important issue to fix for two reasons. First, a lot of images are references from stylesheets, secondly, we&amp;#39;re returning a long expiry headers for all of those assets, so we really need a way to bust the cache.&lt;/p&gt;
&lt;p&gt;The solution is simple, we&amp;#39;ll take the code introduced in &lt;a href="http://codebetter.com/blogs/karlseguin/archive/2010/01/11/asp-net-performance-part-3-cache-busting.aspx"&gt;Part 3&lt;/a&gt; which calculates the hashes and generates our data file, and add a 3rd step which relies on regular expressions to update images referenced in stylesheets. First we define our regular expressions:&lt;/p&gt;
&lt;pre name="code" class="c-sharp"&gt;private static readonly Regex _stylesheetPattern = new Regex(&amp;quot;&amp;#39;\\.\\./images/(.*?)(\\?.*?)?&amp;#39;&amp;quot;, RegexOptions.Compiled);&lt;/pre&gt;
&lt;p&gt;This assumes that your structure looks something like:&lt;/p&gt;
&lt;pre&gt;assets/css/main.css
assets/images/background.gif&lt;/pre&gt;
&lt;p&gt;meaning that your image references will look something like:&lt;/p&gt;
&lt;pre name="code" class="css"&gt;#main{background:url(&amp;#39;../images/background.gif&amp;#39;);}&lt;/pre&gt;
&lt;p&gt;Next we run the code which&amp;#39;ll find all .css files and do a search and replace:&lt;/p&gt;
&lt;pre name="code" class="c-sharp"&gt;private static void ProcessStyleSheets(string root)
{
   foreach(var directory in Directory.GetDirectories(root))
   {
      //recurse
      ProcessStyleSheets(directory);
   }
   foreach(var file in Directory.GetFiles(root, &amp;quot;*.css&amp;quot;))
   {
      var content = File.ReadAllText(file);
      content = _stylesheetPattern.Replace(content, new MatchEvaluator(ApplyHashToMatchedStyle));
      File.WriteAllText(file, content);
   }
}
 
private static string ApplyHashToMatchedStyle(Match match)
{
   var key = string.Concat(&amp;quot;/assets/images/&amp;quot;, match.Groups[1].Value);
   if (_hashes.ContainsKey(key))
   {
      return string.Format(&amp;quot;&amp;#39;../images/{0}?{1}&amp;#39;&amp;quot;, match.Groups[1].Value, _hashes[key]);
   }
   return match.Groups[0].Value;
}&lt;/pre&gt;
&lt;p&gt;Finally we change our main program to call the ProcessStyleSheets method:&lt;/p&gt;
&lt;pre name="code" class="c-sharp"&gt;...
_hashes = new Dictionary&amp;lt;string, string&amp;gt;();  
ProcessDirectory(root, assetPath);  
WriteFile(root + outputFile);  
ProcessStyleSheets(root + assetPath);&lt;/pre&gt;
&lt;p&gt;I also want to mention that &lt;a href="http://www.bugwriter.me/"&gt;Herman&lt;/a&gt;, who had a number of valuable comments, pointed out that, by default, some proxies won&amp;#39;t cache based on querystring (Squid being the best known). Instead, we need to make our hash part of filename. This is something I&amp;#39;ll leave to you to play with, but I will point out that this is something Nginx&amp;#39;s rewriteurl module would be very well suited for.&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://codebetter.com/aggbug.aspx?PostID=592901" width="1" height="1"&gt;</description></item><item><title>You should know Linux</title><link>http://codebetter.com/blogs/karlseguin/archive/2010/01/14/you-should-know-linux.aspx</link><pubDate>Thu, 14 Jan 2010 13:00:00 GMT</pubDate><guid isPermaLink="false">d21fbbc9-c112-4f32-ad14-95939a2c53d4:587234</guid><dc:creator>karl</dc:creator><slash:comments>18</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://codebetter.com/blogs/karlseguin/rsscomments.aspx?PostID=587234</wfw:commentRss><comments>http://codebetter.com/blogs/karlseguin/archive/2010/01/14/you-should-know-linux.aspx#comments</comments><description>&lt;p&gt;Its hard to get everyone to agree on what core competencies developers should have.  I&amp;#39;ve long believed that knowledge of C is fundamental to becoming a great developer. Actually, C in general, memory management, pointer arithmetic, and basic hardware (interupts, scheduling, ...) more specifically. Up until recently, when someone asked me what they should learn to be a better programmers, I&amp;#39;d always suggest C. As someone else put it &amp;quot;Not &amp;#39;getting&amp;#39; algebra is not acceptable for a mathematician, as not &amp;#39;getting&amp;#39; pointers is not acceptable for programmers. Too fundamental.&amp;quot;&lt;/p&gt;
&lt;p&gt;I&amp;#39;m now feeling pretty comfortable in including Linux as a must-know skills. I&amp;#39;d go as far as saying that for certain type of work, like web development, knowing Linux is more important than knowing C.&lt;/p&gt;
&lt;p&gt;I&amp;#39;m not arguing that you need to move your development to Linux, I&amp;#39;m saying that you need to be comfortable enough with Linux to be able to leverage it. Linux is the platform of choice for a massive list of tools that you should have in your toolbox. Not having access to those tools puts you and your company at a disadvantage. It isn&amp;#39;t just about having access to programs either, its about having knowledge about alternative ways to do things. I speak to a lot of ASP.NET developers who don&amp;#39;t know what in-memory distributed caching is or who don&amp;#39;t understand non-relational persistence options. This ignorance is a major liability which will be hard to address as long as developer remain uncertain and fearful of Linux.&lt;/p&gt;
&lt;p&gt;The other nice thing about Linux is that its dead easy to setup and configure. The documentation no longer solely consists of cryptic man pages, and the community is broader than ever. If you can&amp;#39;t get LAMP installed, then you either picked an uncommon distro, or you haven&amp;#39;t tried very hard.&lt;/p&gt;
&lt;p&gt;If you are really serious about diving in, I offer you some choices. First, convert an old PC to a linux server. An single core P4 with 1gb of RAM is more than enough to run a linux server. You can also get a Mac, since Mac OS X is based on BSD (close enough to Linux for our purposes), and has great documentation for developers to install, configure and learn all types of useful things. You can get a nettop. I&amp;#39;m currently running an ASRock 330 (non-ION or blu-ray -  unless you need/want them), which has a dual core Atom 330 + 4gb of ram (added afterwards by me). Finally, you can rent an unmanaged VPS. Probably the most popular provider, due to its performance, price, and the fact that you have full root access, is &lt;a href="http://www.linode.com/"&gt;Linode&lt;/a&gt; - however there&amp;#39;s plenty of choice.&lt;/p&gt;
&lt;p&gt;Which option you pick should largely be based on your needs. If you&amp;#39;ll want something that you can actually use to host something, then a VPS is clearly the way to go. Otherwise, reuse an old machine if you can, or pick between a cheap low-end dedicated nettop or a more expensive (but also more useful) mac.&lt;/p&gt;
&lt;p&gt;As for distributions, I&amp;#39;d recommend &lt;a href="http://www.ubuntu.com/"&gt;Ubuntu&lt;/a&gt; - in large part because of the excellent documentation and community (though &lt;a href="http://www.centos.org/"&gt;CentOS&lt;/a&gt; is a commone server of choice in production). You can pick the Desktop or Server edition. I picked the server since I only ever SSH into the machine and have no need for a GUI.&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://codebetter.com/aggbug.aspx?PostID=587234" width="1" height="1"&gt;</description></item><item><title>ASP.NET Performance - Part 5 - Nginx</title><link>http://codebetter.com/blogs/karlseguin/archive/2010/01/13/asp-net-performance-part-5-nginx.aspx</link><pubDate>Wed, 13 Jan 2010 13:48:00 GMT</pubDate><guid isPermaLink="false">d21fbbc9-c112-4f32-ad14-95939a2c53d4:586739</guid><dc:creator>karl</dc:creator><slash:comments>16</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://codebetter.com/blogs/karlseguin/rsscomments.aspx?PostID=586739</wfw:commentRss><comments>http://codebetter.com/blogs/karlseguin/archive/2010/01/13/asp-net-performance-part-5-nginx.aspx#comments</comments><description>&lt;p&gt;The previous parts of this series focused on small pieces of code we could use to maximize real-world performance - largely based off of YSlow recommendations. In this final installment we&amp;#39;ll think outside the box (literally) by placing a reverse proxy infront of IIS to get the most bang possible. Our technology of choice is &lt;a href="http://nginx.org/"&gt;Nginx&lt;/a&gt; running on Linux (while Nginx runs on windows, it doesn&amp;#39;t seem to support shared memory - which is critical to a number of features).&lt;/p&gt;
&lt;p&gt;Nginx (pronounced engine-x) is an event-driven asynchronous server. Such servers are specialized at handling extreme loads while consuming limited resources. On top of this foundations are a number of modules which enable various feature. Such features include load balancing, compression, reverse proxy (which is mostly what we&amp;#39;ll look at), fastcgi, memcached, url rewriting and so on. Nginx is used by a number of high profile sites, including Wordpress, GitHub and SourceForge. Oh, and its completely free. (Another equally popular and equally free alternative is &lt;a href="http://www.lighttpd.net/"&gt;lighttpd&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;Nginx is prettty easy to configure and the configuration file is pretty easy to read. However, if you prefer to learn by doing but don&amp;#39;t have linux setup, then you can use the windows version (that&amp;#39;s how I started). You&amp;#39;ll be able to get a feel for it and do most of the things we&amp;#39;ll be looking at.&lt;/p&gt;
&lt;p&gt;For the most part we&amp;#39;ll focus on Nginx&amp;#39;s reverse proxy module. A reverse proxy [typically] sits infront of your web server, accepting all connections and deciding on a course of action. In its simplest form, our nginx.conf file will have the following reverse proxy settings:&lt;/p&gt;
&lt;pre&gt;user www-data;
worker_processes  5;

error_log  /var/log/nginx/error.log;
pid        /var/run/nginx.pid;

events {
    worker_connections  1024;
}

http {
    server {
        listen       80;
        server_name  oursite.com www.oursite.com;
        location / {
            proxy_set_header  x-real-IP        $remote_addr;
            proxy_set_header  x-forwarded-for  $proxy_add_x_forwarded_for;
            proxy_set_header  host             $http_host;
    		proxy_pass        http://10.10.1.1/;
        }	
    }
}&lt;/pre&gt;
&lt;p&gt;We&amp;#39;ve configured Nginx to listen on port 80 for connections with a host header of &lt;code&gt;oursite.com&lt;/code&gt; or &lt;code&gt;www.oursite.com&lt;/code&gt;. When Nginx receives a request, it will look for a matching server, and a matching location (here we are matching all locations), and then issue a request to our imaginary windows machine at &lt;code&gt;10.10.1.1&lt;/code&gt;. Since Nginx is actually issueing the request to our web server, we are adding headers to the request so that the web server has all necessary informaton.
&lt;/p&gt;
&lt;p&gt;The above configuration gets us up and running, but doesn&amp;#39;t really do anything (except for add another point of failure). However, by adding just two more lines of code, things can really get awesome:&lt;/p&gt;
&lt;pre&gt;#define a shared memory named cache (don&amp;#39;t think this will work in windows)
proxy_cache_path /usr/local/nginx/proxy_temp/ levels=1:2 keys_zone=cache:10m inactive=10m max_size=1000M;

server {
    listen       80;
    server_name  oursite.com www.oursite.com;
    location / {
        proxy_set_header  x-real-IP        $remote_addr;
        proxy_set_header  x-forwarded-for  $proxy_add_x_forwarded_for;
        proxy_set_header  host             $http_host;
        proxy_pass http://10.10.1.1/;
        proxy_cache cache #enables the cache
    }	
}&lt;/pre&gt;
&lt;p&gt;First we&amp;#39;ve defined a zone where cached data is to be stored (called &lt;code&gt;cache&lt;/code&gt;), as well as how much room to allocate in memory (for cache lookup metadata) and on disk (for the actual cached data). Then, we&amp;#39;ve modified our location and simply told it to allow caching using the zone called &lt;code&gt;cache&lt;/code&gt;. This means that once &lt;code&gt;oursite.com/assets/images/logon.png?23188a4&lt;/code&gt; has been requested, Nginx will keep a copy on disk and use its local copy instead of forwarding the request. We are still serving the same total number of requests, but the load for our static/plain files has been moved from our Windows machine which should focus on executing our code (and which is suceptible to &lt;a href="http://www.kegel.com/c10k.html"&gt;the C10K problem&lt;/a&gt;) to a highly specialized, low-resource system. There are actually a lot of benchmarks (as well as real life case studies) which show Nginx&amp;#39;s destroying Apache (which is thread-based like IIS) when it comes to high concurrency, low resources and response time.&lt;/p&gt;
&lt;p&gt;Note that we aren&amp;#39;t just limited to caching requests for images, css and javascript file - Nginx will cache any response that has the appropriate headers. You can take your homepage, add an &lt;code&gt;OutputCache&lt;/code&gt; (directive or MVC attribute) with the location to &lt;code&gt;Downstream&lt;/code&gt; and watch your your site fly.&lt;/p&gt;
&lt;p&gt;We can even add a new location to our server (locations support regular expression matching too, incase you need it), and do something like:&lt;/p&gt;
&lt;pre&gt;location /favicon.ico {
    proxy_pass	http://10.10.1.1/favicon.ico;
    proxy_cache cache;	
    expires 30d;
}&lt;/pre&gt;
&lt;p&gt;This showcases Nginx&amp;#39;s &lt;code&gt;expires&lt;/code&gt; directive which can be used to set an expiry header.&lt;/p&gt;
&lt;p&gt;Moving on, we can also use Nginx&amp;#39;s Gzip module to compress responses. Again, this is something IIS can do, but there&amp;#39;s something reassuring about letting our application machine worry exclusively on running our code. With the following lines, applied globally (so that it relates to any server definition), nginx will compress all responses greater than 500 bytes for a number of different filetypes, providing the browser isn&amp;#39;t &amp;lt; IE 7:&lt;/p&gt;
&lt;pre&gt;gzip  on;
gzip_buffers 16 8k;
gzip_comp_level 6;
gzip_proxied any;
gzip_types text/css text/plain application/x-javascript image/x-icon;
gzip_min_length 500;
gzip_disable &amp;quot;MSIE [1-6]\.&amp;quot;&lt;/pre&gt;
&lt;p&gt;All of this is just the tip of the iceberg of what&amp;#39;s possible. Other interesting modules protect against denial of service attacks, help harden the system, support load balancing (with sticky sessions), logging and memcached integration. We can also set headers - removing the need for some of the code we wrote in Part 2. Best of all, the Nginx &lt;a href="http://wiki.nginx.org/NginxModules"&gt;documentation is quite good&lt;/a&gt;, and the &lt;a href="http://wiki.nginx.org/NginxCommunity"&gt;community is thriving&lt;/a&gt;.&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://codebetter.com/aggbug.aspx?PostID=586739" width="1" height="1"&gt;</description></item><item><title>ASP.NET Performance - Part 4 - CDN</title><link>http://codebetter.com/blogs/karlseguin/archive/2010/01/12/asp-net-performance-part-4-cdn.aspx</link><pubDate>Tue, 12 Jan 2010 13:31:00 GMT</pubDate><guid isPermaLink="false">d21fbbc9-c112-4f32-ad14-95939a2c53d4:586181</guid><dc:creator>karl</dc:creator><slash:comments>9</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://codebetter.com/blogs/karlseguin/rsscomments.aspx?PostID=586181</wfw:commentRss><comments>http://codebetter.com/blogs/karlseguin/archive/2010/01/12/asp-net-performance-part-4-cdn.aspx#comments</comments><description>&lt;p&gt;In &lt;a href="http://codebetter.com/blogs/karlseguin/archive/2010/01/11/asp-net-performance-part-3-cache-busting.aspx"&gt;part 3&lt;/a&gt; we created three helper functions to use when including css, js and images in our pages. As you might expect, &lt;a href="http://developer.yahoo.com/yslow/"&gt;YSlow&lt;/a&gt; has something to say about how to maximize the performance of such files beyond caching. Specifically, YSlow makes the following three recommendations:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Use a Content Delivery Network (CDN), &lt;/li&gt;
&lt;li&gt;Use cookie-free domains, and&lt;/li&gt;
&lt;li&gt;Split Components Across Domains&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All three are super-easy to implement, and are all &amp;quot;fixed&amp;quot; by the same code (we won&amp;#39;t actually use a CDN, but we&amp;#39;ll make it so that its easy to use if the need ever arises). To be honest, I hadn&amp;#39;t even considered the possibility of using separate domains to minimize the overhead associated with sending cookie information. Some may say that this is a microoptimization, but it takes less than five minutes to implement, so why not? (if you aren&amp;#39;t using cookies, you won&amp;#39;t see any benefits of this, except for the parallel downloading and flexibility of using a cdn in the future).&lt;/p&gt;
&lt;p&gt;If you recall, the initial version of our helper functions, without our cache busting, looked like:&lt;/p&gt;
&lt;pre name="code" class="c-sharp"&gt;public static string IncludeCss(this HtmlHelper html, string name)
{            
    return string.Format(&amp;quot;&amp;lt;link href=\&amp;quot;/assets/styles/{0}.css\&amp;quot; rel=\&amp;quot;stylesheet\&amp;quot; type=\&amp;quot;text/css\&amp;quot;&amp;gt;&amp;lt;/link&amp;gt;&amp;quot;, name);
}
public static string IncludeJs(this HtmlHelper html, string name)
{
    return string.Format(&amp;quot;&amp;lt;script src=\&amp;quot;/assets/js/{0}.js\&amp;quot; type=\&amp;quot;text/javascript\&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&amp;quot;, name);
}
public static string Image(this HtmlHelper html, string name, int width, int height, string alt)
{    
    return string.Format(&amp;quot;&amp;lt;img src=\&amp;quot;/assets/mages/{0}\&amp;quot; width=\&amp;quot;{1}\&amp;quot; height=\&amp;quot;{2}\&amp;quot; alt=\&amp;quot;{3}\&amp;quot; /&amp;gt;&amp;quot;, name, width, height, alt);
}&lt;/pre&gt;
&lt;p&gt;(I&amp;#39;m doing this against the initial version just to keep things simple, but integrating our Part 3 and Part 4 end-results should be trivial).&lt;/p&gt;
&lt;p&gt;All we need to do is a little change:&lt;/p&gt;
&lt;pre name="code" class="c-sharp"&gt;private static readonly string _assetUrl = ConfigurationManager.AppSettings[&amp;quot;assetUrl&amp;quot;];
public static string IncludeCss(this HtmlHelper html, string name)
{            
    return string.Format(&amp;quot;&amp;lt;link href=\&amp;quot;{0}assets/styles/{1}.css\&amp;quot; rel=\&amp;quot;stylesheet\&amp;quot; type=\&amp;quot;text/css\&amp;quot;&amp;gt;&amp;lt;/link&amp;gt;&amp;quot;, _assetUrl, name);
}
//...similar changes the for js and image method.&lt;/pre&gt;
&lt;p&gt;and change our &lt;code&gt;web.config&lt;/code&gt; to include the value:&lt;/p&gt;
&lt;pre name="code" class="c-sharp"&gt;&amp;lt;appSettings&amp;gt;
    &amp;lt;add key=&amp;quot;assetUrl&amp;quot; value=&amp;quot;http://assets.mydomain.com/&amp;quot;/&amp;gt;
&amp;lt;/appSettings&amp;gt;&lt;/pre&gt;
&lt;p&gt;Finally you&amp;#39;ll need to setup the subdomain.&lt;/p&gt;
&lt;p&gt;While we aren&amp;#39;t using a CDN, we&amp;#39;ve made it very easy to start using a CDN by making the root URL path for our assets configurable. We&amp;#39;ve also ensured that cookies defined for &lt;code&gt;mydomain.com &lt;/code&gt;aren&amp;#39;t going to add overhead when requesting assets sitting on &lt;code&gt;assets.mydomain.com&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;For the curious, YSlow doesn&amp;#39;t know whether the domain is a CDN or not, you have to tell it that it is by &lt;a href="http://developer.yahoo.com/yslow/faq.html#faq_cdn"&gt;adding it to a list&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;A final word of caution, YSlow also warns against too many DNS lookups - so while its important to use a cdn, use non-cookie domains and split up our content to allow for greater parallel downloads, don&amp;#39;t over do it. They generally recommend less than 4, but their own website uses 6. We&amp;#39;ve added 1, which, in my opinion is worth it, but try not to add too many (consider that you&amp;#39;ll likely add one for google analytics and possible for ads...).&lt;/p&gt;
&lt;p&gt;Part 5 will be our last part in the series, and will be very different than what we&amp;#39;ve looked at so far.&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://codebetter.com/aggbug.aspx?PostID=586181" width="1" height="1"&gt;</description></item><item><title>ASP.NET Performance - Part 3 - Cache Busting</title><link>http://codebetter.com/blogs/karlseguin/archive/2010/01/11/asp-net-performance-part-3-cache-busting.aspx</link><pubDate>Mon, 11 Jan 2010 13:51:00 GMT</pubDate><guid isPermaLink="false">d21fbbc9-c112-4f32-ad14-95939a2c53d4:581508</guid><dc:creator>karl</dc:creator><slash:comments>14</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://codebetter.com/blogs/karlseguin/rsscomments.aspx?PostID=581508</wfw:commentRss><comments>http://codebetter.com/blogs/karlseguin/archive/2010/01/11/asp-net-performance-part-3-cache-busting.aspx#comments</comments><description>&lt;p&gt;In &lt;a href="http://codebetter.com/blogs/karlseguin/archive/2010/01/08/asp-net-performance-part-2-yslow.aspx"&gt;part 2&lt;/a&gt; of the series we looked at ways to tweak our headers to maximize performance. One of those tweaks was the addition of far-reaching expiry headers for static files (images, css, js). What happens though when you need to change those files? We can make a change to the file on the server, but clients that got the previous version won&amp;#39;t request the new file since the headers that came with the initial one told them not to.&lt;/p&gt;
&lt;p&gt;You may be thinking that a simple solution would be to set a shorter expiration time. But that only masks the problem while negating the benefits we&amp;#39;re after. What we&amp;#39;re after is a solution that&amp;#39;ll let us maximize caching yet allow us to update content on demand.&lt;/p&gt;
&lt;p&gt;HTTP caching works off the requested URL. The browser makes a request for a URL, and gets a response, which contains headers as well as the body. Any subsequent request to the same URL should follow any caching headers that were returned in the original request. To make matters better (or worse depending on your point of view), any proxy between you and the client can also follow this same logic. Thus, a user that visits your site might get original content, but subsequent visits by him or anyone else behind the same proxy requesting the same file, will get a cached version from the proxy. This is great because a far reaching expiry can really turn tends of thousands of hits into 1, but also means that we really do need to come up with a good cache-busting solution.&lt;/p&gt;
&lt;p&gt;(As a side note, since the cache is based off of the requested URL, you should really consider leveraging things like google&amp;#39;s javascript hosting. Instead of linking to jquery.js in your /js folder, you can link to &lt;a href="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"&gt;http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js&lt;/a&gt;. By doing so, if a user has previously browsed a site which linked to the google version, then it&amp;#39;ll be in their cache when they visit your site and get faster loading. Google sets the expiry header to 1 year. On the down site, it decreases your sites availability by introducing another system that can break - google).&lt;/p&gt;
&lt;p&gt;You may be thinking that a good solution is to simply change the name of our file. For example, if you&amp;#39;re currently linking to &lt;code&gt;logo.png&lt;/code&gt; and need to make some changes, you could switch it to &lt;code&gt;logo2.png&lt;/code&gt;. This would work, but its a very manual and thus risky. What if you forget that a particular page is linking to your logo (like that HTML email template that gets sent out every now and again)?&lt;/p&gt;
&lt;p&gt;Instead, what&amp;#39;s typically done, and what we&amp;#39;ll do here, is to include some type of version in the querystring. And, instead of incrementing integers (which can be tricky to manage, even if we automate it), we&amp;#39;ll use a hash of the files content. So, instead of linking to &lt;code&gt;/images/logo.png&lt;/code&gt;, we&amp;#39;ll actually link to &lt;code&gt;/images/logo.png?asdaslkj3918&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;To achieve this, we&amp;#39;ll need to do two things. First we&amp;#39;ll have to generate a file hash for all of our assets and make that available to our system. Second we&amp;#39;ll need to read the list of hashes and use it whenever we create a link to an asset.&lt;/p&gt;
&lt;p&gt;Exactly how you implement the first part is up to you.  What I&amp;#39;ll show here is doing it as part of the build process - much like we saw in &lt;a href="http://codebetter.com/blogs/karlseguin/archive/2008/12/29/compressing-js-files-as-part-of-your-build-process.aspx"&gt;Part 1&lt;/a&gt;. The exact output of our first step is simply a text file that looks something like:&lt;/p&gt;
&lt;pre&gt;/Content/css/main.css|a5feab8f
/Content/images/logo.png|fa80c7bc
/Content/images/modal_close.png|a5722737
/Content/images/modal_close_o.png|0ddd517d
/Content/js/dd_roundies.js|955e9bc5&lt;/pre&gt;
&lt;p&gt;Our program will take three arguments, the root directory of our site, the asset folder, and the output file name. So, given the following parameters:&lt;/p&gt;
&lt;pre&gt;c:\projects\coolsite\ assets\ assethash.dat&lt;/pre&gt;
&lt;p&gt;our application will recurse through the &lt;code&gt;c:\projects\coolsite\assets\&lt;/code&gt; folder and generate a file in the above format in &lt;code&gt;c:\projects\coolsite\assethash.dat&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;

Here&amp;#39;s the code:

&lt;/p&gt;
&lt;pre name="code" class="c-sharp"&gt;internal class Program
    {
        private static IDictionary&amp;lt;string, string&amp;gt; _hashes;
        private static void Main(string[] args)
        {
            var root = args[0];
            var assetPath = args[1];
            var outputFile = args[2];

            _hashes = new Dictionary&amp;lt;string, string&amp;gt;();
            ProcessDirectory(root, assetPath);
            WriteFile(root + outputFile);
        }

        private static void ProcessDirectory(string root, string assetContainer)
        {            
            foreach (var directory in Directory.GetDirectories(root + assetContainer))
            {
                if (directory.Contains(&amp;quot;.svn&amp;quot;))  { continue; }
                ProcessDirectory(root, directory.Remove(0, root.Length));
            }
            foreach (var file in Directory.GetFiles(root + assetContainer))
            {
                var assetName = file.Remove(0, root.Length - 1).Replace(&amp;#39;\\&amp;#39;, &amp;#39;/&amp;#39;);
                _hashes.Add(assetName, CreateSignature(file));
            }            
        }

        private static string CreateSignature(string file)
        {
            byte[] bytes;
            using (var hash = new Crc32())
            {
                bytes = hash.ComputeHash(Encoding.ASCII.GetBytes(File.ReadAllText(file)));
            }

            var data = new StringBuilder();
            Array.ForEach(bytes, b =&amp;gt; data.Append(b.ToString(&amp;quot;x2&amp;quot;)));
            return data.ToString();
        }

        private static void WriteFile(string path)
        {
            using (var sw = new StreamWriter(path))
            {
                foreach (var kvp in _hashes)
                {
                    sw.WriteLine(&amp;quot;{0}|{1}&amp;quot;, kvp.Key, kvp.Value);
                }
            }            
        }
    }&lt;/pre&gt;
&lt;p&gt;First we build up a dictionary of paths (relative to the root of our asset folder) and hash values. For my &amp;quot;hash&amp;quot; I&amp;#39;m using a CRC32, specifically &lt;a href="http://damieng.com/blog/2006/08/08/calculating_crc32_in_c_and_net"&gt;Damien Guard&amp;#39;s C# implementation&lt;/a&gt;. The nice thing about CRC32 is that we end up with pretty small values. The downside is that collisions are far more likely (but still rather unlikely - I haven&amp;#39;t really tested this tough, so maybe using a true-collision resistant hash, like md5 (which can collide but only in extremely rare occasions) makes more sense). &lt;/p&gt;
&lt;p&gt;Next we dump the dictionary to our file - nothing much to explain there. &lt;/p&gt;
&lt;p&gt;This is our postbuild line:&lt;/p&gt;
&lt;pre name="code" class="html"&gt;$(SolutionDir)..\Tools\ContentSigner\CodeBetter.Web.ContentSigner.exe $(ProjectDir) assets\ assethash.dat&lt;/pre&gt;
&lt;p&gt;Now we need to use the generated file. First we&amp;#39;ll start off with three vanilla helper functions:&lt;/p&gt;
&lt;pre name="code" class="c-sharp"&gt;public static class HtmlAssetExtensions
{
    public static string IncludeCss(this HtmlHelper html, string name)
    {            
        return string.Format(&amp;quot;&amp;lt;link href=\&amp;quot;/assets/styles/{0}.css\&amp;quot; rel=\&amp;quot;stylesheet\&amp;quot; type=\&amp;quot;text/css\&amp;quot;&amp;gt;&amp;lt;/link&amp;gt;&amp;quot;, name);
    }
    public static string IncludeJs(this HtmlHelper html, string name)
    {
        return string.Format(&amp;quot;&amp;lt;script src=\&amp;quot;/assets/js/{0}.js\&amp;quot; type=\&amp;quot;text/javascript\&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&amp;quot;, name);
    }
    public static string Image(this HtmlHelper html, string name, int width, int height, string alt)
    {    
        return string.Format(&amp;quot;&amp;lt;img src=\&amp;quot;/assets/mages/{0}\&amp;quot; width=\&amp;quot;{1}\&amp;quot; height=\&amp;quot;{2}\&amp;quot; alt=\&amp;quot;{3}\&amp;quot; /&amp;gt;&amp;quot;, name, width, height, alt);
    }
}&lt;/pre&gt;
&lt;p&gt;First we&amp;#39;ll modify our helper to load the text file back into a dictionary (this could be handled in a separate class, but for simplicity, we&amp;#39;ll just stuff all the logic in here):&lt;/p&gt;
&lt;pre name="code" class="c-sharp"&gt;public static class HtmlAssetExtensions
{
    private static IDictionary&amp;lt;string, string&amp;gt; _assetHashes = LoadHashes();

    private static IDictionary&amp;lt;string, string&amp;gt; LoadHashes()
    {
        var hashes = new Dictionary&amp;lt;string, string&amp;gt;(StringComparer.InvariantCultureIgnoreCase);
        using (var sr = new StreamReader(HttpRuntime.AppDomainAppPath + &amp;quot;assethash.dat&amp;quot;))
        {
            while (sr.Peek() &amp;gt;= 0)
            {
                var parts = sr.ReadLine().Split(&amp;#39;|&amp;#39;);
                hashes.Add(parts[0], parts[1]);
            }
        }
        return hashes;
    }

    ....
}&lt;/pre&gt;
&lt;p&gt;Next we need to use the data:&lt;/p&gt;
&lt;pre name="code" class="c-sharp"&gt;public static string IncludeCss(this HtmlHelper html, string name)
{
    name = GetVersionedName(string.Format(&amp;quot;/assets/styles/{0}.css&amp;quot;, name));
    return string.Format(&amp;quot;&amp;lt;link href=\&amp;quot;{0}\&amp;quot; rel=\&amp;quot;stylesheet\&amp;quot; type=\&amp;quot;text/css\&amp;quot;&amp;gt;&amp;lt;/link&amp;gt;&amp;quot;, name);
}
//...similar changes the for js and image method.

private static string GetVersionedName(string name)
{
    string hash;
    if (!_assetHashes.TryGetValue(name, out hash))
    {
        return name;
    }
    return string.Concat(name, &amp;#39;?&amp;#39;, hash);
}&lt;/pre&gt;
&lt;p&gt;And that, my dear reader, is all it takes. There is one problem with our implementation, changing our &lt;code&gt;assethash.dat&lt;/code&gt; isn&amp;#39;t going to noticed by our application. That&amp;#39;s something I leave to you to figure out - though it isn&amp;#39;t too hard to implement (hint: you could just store the dictionary in the &lt;code&gt;HttpRuntime.Cache&lt;/code&gt; with a file dependency).&lt;/p&gt;
&lt;p&gt;In the next [very short] part, we&amp;#39;ll make a single small tweak to our helper functions that&amp;#39;ll help address three other &lt;a href="http://developer.yahoo.com/yslow/"&gt;YSlow&lt;/a&gt; recommendations. Stay tuned.&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://codebetter.com/aggbug.aspx?PostID=581508" width="1" height="1"&gt;</description></item><item><title>ASP.NET Performance - Part 2 - YSlow</title><link>http://codebetter.com/blogs/karlseguin/archive/2010/01/08/asp-net-performance-part-2-yslow.aspx</link><pubDate>Fri, 08 Jan 2010 15:10:00 GMT</pubDate><guid isPermaLink="false">d21fbbc9-c112-4f32-ad14-95939a2c53d4:579528</guid><dc:creator>karl</dc:creator><slash:comments>18</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://codebetter.com/blogs/karlseguin/rsscomments.aspx?PostID=579528</wfw:commentRss><comments>http://codebetter.com/blogs/karlseguin/archive/2010/01/08/asp-net-performance-part-2-yslow.aspx#comments</comments><description>&lt;p&gt;In &lt;a href="http://codebetter.com/blogs/karlseguin/archive/2008/12/29/compressing-js-files-as-part-of-your-build-process.aspx"&gt;part 1&lt;/a&gt; we looked at integrating a &lt;a href="http://www.codeplex.com/YUICompressor"&gt;javascript compressor&lt;/a&gt; as part of our build process to shrink javascript files for production (at the time, I didn&amp;#39;t realize that it would lead to a series). Its worth mentioning that if you go down that path, you should really also merge all your files together so that they can be pulled down from a single request - but that&amp;#39;s up to you to figure out how (come on, merging x files together isn&amp;#39;t hard now!)&lt;/p&gt;
&lt;p&gt;Today we&amp;#39;ll write a simple HttpModule that&amp;#39;ll help us meet a number of YSlow recommendations. &lt;a href="http://developer.yahoo.com/yslow/"&gt;YSlow&lt;/a&gt;, created by Yahoo, is a &lt;a href="http://getfirebug.com/"&gt;Firebug&lt;/a&gt; addon (which itself is a [must have] addon to Firefox)) which identifies potential performance issues with your site. Here&amp;#39;s a screenshot that shows the sort of things YSlow can help identify.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://codebetter.com/cfs-file.ashx/__key/CommunityServer.Blogs.Components.WeblogFiles/karlseguin/yslow.png"&gt;&lt;img src="http://codebetter.com/resized-image.ashx/__size/550x755/__key/CommunityServer.Blogs.Components.WeblogFiles/karlseguin/yslow.png" border="0" alt="" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;With just a few lines of code, 2 of those F&amp;#39;s can be solved - removing ETags from the server response and adding expiry headers.&lt;/p&gt;
&lt;pre name="code" class="c-sharp"&gt;public namespace CodeBetter.Web
{
    public class YSlowHttpModule : IHttpModule
    {
        private static readonly List&amp;lt;string&amp;gt; _headersToRemove = new List&amp;lt;string&amp;gt; { &amp;quot;X-AspNet-Version&amp;quot;, &amp;quot;X-AspNetMvc-Version&amp;quot;, &amp;quot;Etag&amp;quot;, &amp;quot;Server&amp;quot;,}; 
        private static readonly List&amp;lt;string&amp;gt; _longCacheExtensions = new List&amp;lt;string&amp;gt; {&amp;quot;.js&amp;quot;, &amp;quot;.css&amp;quot;, &amp;quot;.png&amp;quot;, &amp;quot;.jpg&amp;quot;, &amp;quot;.gif&amp;quot;,};
     
        public void Init(HttpApplication context)
        {
            context.EndRequest += new EventHandler(context_EndRequest);
        }
     
        private void context_EndRequest(object sender, EventArgs e)
        {
            var context = HttpContext.Current;
            _headersToRemove.ForEach(h =&amp;gt; context.Response.Headers.Remove(h));
            var extension = Path.GetExtension(context.Request.Url.AbsolutePath);
            if (_longCacheExtensions.Contains(extension))
            {
                context.Response.CacheControl = &amp;quot;Public&amp;quot;;
                context.Response.Expires = 44000; //slightly over 1 month
            }
        } 
        public void Dispose() {}
    }
}&lt;/pre&gt;
&lt;p&gt;Simply register the module in your web.config and you&amp;#39;re good to go:&lt;/p&gt;
&lt;pre name="code" class="xml"&gt;&amp;lt;add name=&amp;quot;YSlowHttpModule&amp;quot; type=&amp;quot;CodeBetter.Web.YSlowHttpModule, YourAssemglyName&amp;quot;/&amp;gt;&lt;/pre&gt;
&lt;p&gt;Its worth pointing out that this module will only work with IIS 7.x&amp;#39;s integrated pipeline.&lt;/p&gt;
&lt;p&gt;The code does two things. First, it strips out extra headers - we only really need to strip out the Etag header from YSlow&amp;#39;s perspective, but I like to remove the other useless stuff as well. The second is that it adds a very long cache control policy to our assets.&lt;/p&gt;
&lt;p&gt;You may be wondering why ETags should be removed beyond mindlessly following YSlow&amp;#39;s recommendation. ETags are inferior to a long lasting cache control policy, and as such, it does nothing more than add overhead to each request. Unlike far-reaching expiry headers, ETags still require an open connection to your server to see if the content has changed.&lt;/p&gt;
&lt;p&gt;A problem with our YSlowHttpModule is that we&amp;#39;ve told anyone in the request chain that they can cache our static files for a month. What if we need to change an image, or update our css? In part 3 we&amp;#39;ll solve that issue by placing file hashes in our querystrings.&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://codebetter.com/aggbug.aspx?PostID=579528" width="1" height="1"&gt;</description></item></channel></rss>