<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:copyright="http://blogs.law.harvard.edu/tech/rss" xmlns:image="http://purl.org/rss/1.0/modules/image/">
    <channel>
        <title>Rob's Blog</title>
        <link>http://robrich.org/Default.aspx</link>
        <description>Rob's Blog</description>
        <language>en-US</language>
        <copyright>Rob Richardson</copyright>
        <generator>Subtext Version 2.5.0.3</generator>
        <image>
            <title>Rob's Blog</title>
            <url>http://robrich.org/images/RSS2Image.gif</url>
            <link>http://robrich.org/Default.aspx</link>
            <width>77</width>
            <height>60</height>
        </image>
        <item>
            <title>iPhone to Android via Droid X</title>
            <category>User Notes</category>
            <category>Rants</category>
            <link>http://robrich.org/archive/2010/09/07/iPhone-to-Android-via-Droid-X.aspx</link>
            <description>&lt;p&gt;I've really enjoyed my Droid X. A friend looking to make a similar move from an iPhone to an Android phone asked me about my experience. Shortly after I finished, I realized it was blog-post size, so here it is. Thus, in no particular order, here's my first impressions of the transition (still in progress) from iPhone to a Droid X on Verizon:&lt;/p&gt;
&lt;p&gt;- The first time I assigned a category to a calendar event, and noticed I didn't need to sync it, I was very excited and danced around the house. (I use gSyncIt to get from Outlook to Google Calendar, so it just popped up in Outlook too.)&lt;/p&gt;
&lt;p&gt;- The Gmail stuff is built by Google and part of Android. The Email (POP/IMAP/SMTP) is built by Motorola, and is less polished. I use multiple accounts on each, and though it isn't overly confusing, it is different enough to note. For example, there's an "Accounts" button in the menu of Gmail, but no "Accounts" menu in Email. Hitting back enough times in Gmail takes you to the accounts list, going back the same amount of times in Email takes you home. If you get into Email via the notifications, you have to go out of the app, and click on the Messaging app to pick a new email account. (Oops.)&lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;- Home screen widgets are awesome. I really liked the jailbreak winterboard calendar icon I had that always showed the current date. I now have a calendar widget that shows a full month.&lt;/p&gt;
&lt;p&gt;- It's not all about the apps. Though many things are available via apps, some things are just "online". E.g. Google Docs integration is just "go to the website". (Um, oh yeah, duh. That was my response when my wife explained it to me. :D) &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;- Check the "load apps from elsewhere" global setting, download an *.apk from any website, and install it. No jailbreaking required.&lt;/p&gt;
&lt;p&gt;- I rooted my device. That was easy. I downloaded the one-click root, installed the Motorola drivers for my X, pushed the button, and it just worked. It was soooo nice. I haven't explored too much of the root app list much. That's on my to-do list.&lt;/p&gt;
- The voice command feature has never worked for me, but typing isn't that bad. I've not tried the Swype keyboard yet, wanting to get comfortable with stock first.
&lt;p&gt;- The Motorola mods seemed ok. I didn't link my Facebook account or flicker or twitter or etc account in the carefully unbranded Moto-blur "accounts" dialog, choosing the apps instead. Linking via "accounts" pulls down pics and statuses into the PIM (sound familiar? :P), but I read somewhere the Facebook app and the Facebook Moto-blur collided sometimes. (I don't recall where or how old it was or if it was resolved.)&lt;/p&gt;
&lt;p&gt;- I downloaded Launcher Pro (home screen replacement) and enjoy their take on the world. It isn't all that different, but I found the features in it compelling, and the lack of moto-stuff intriguing. (I chose the X over the Incredible because of the larger screen and no Sense UI. The Sense stuff seemed cool sometimes, but overly weird other times. For example, I think the stock "slide to one side to unlock, the other way to mute" is much cooler than the rounded "slide up to unlock" that comes with sense. Same with the toolbar section of the home screen. The curve at the bottom of Sense UI's home screen just seemed too "let's play with Photoshop" to me.)&lt;/p&gt;
&lt;p&gt;- The FM radio app is cool, though it only works with headphones, and no headphones come in the box. The iPhone headphones work just fine though. :D My 3-year-old Motorola Bluetooth ear-dangly thing doesn't though.&lt;/p&gt;
&lt;p&gt;- There are a few apps that don't come stock. Most notably is a notepad app. There are scores of them in the app store though -- many that sync with Google Docs ... and / or my wife's brilliant flash of the obvious for me: just use Google Docs's website. Stocks and eBook reader also aren't stock, but easily downloadable in dozens of varieties.&lt;/p&gt;
&lt;p&gt;- The "set an alarm" is a bit less versatile in that you can set a time, but not a day of the week. (For instance, I had a weekly alarm to get us to church on time.) My wife said "set a reoccurring calendar item". (Oh, duh. :D)&lt;/p&gt;
&lt;p&gt;- The create calendar item dialog is much more powerful. Categories are awesome, but reoccurring is also very powerful. I can now choose options like "once a week on Sunday" or "every weekday" or "monthly on day 6" rather than just "weekly" or "daily". &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;- Haven't really taken the camera / video camera out for more than a passing spin, but they seem to do fine for all I (don't) do with them. :D&lt;/p&gt;
&lt;p&gt;- Playing with the Droid 2 and Droid X and Original Droid (want to try it on an Incredible too) is that the X has physical buttons for the 4 functions: Home, Menu, Back, Search. The others (excepting maybe the Incredible?) have virtual buttons -- as if the touch screen just extended into them. My guess is this means the button presses on the non-X are just screen clicks, and while the screen is locked, they do nothing. On the X, when the screen is off, I click the home button, and the screen lights up. On the Droid 2 and Original Droid, the only way to light up the screen is hit the power button on the top. The power button still works for the X, but it's on the top, and not overly convenient. (I always pushed the "home" button on my iPhone to wake it up too.)&lt;/p&gt;
&lt;p&gt;- I like the asymmetrical (top to bottom) feel of the Droid X. While not looking, I can tell which way is up. On my iPhone, I don't know how many times I wipe smudges off the top earphone because that wasn't the home button. :P &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;- It took me a bit to realize the options I had at every step. There's click, long-click, hit the menu button, hit the search button. All are valid at various places, and all usually get you to a different place.&lt;/p&gt;
&lt;p&gt;- The brilliant flash of the obvious a friend of mine made was that Android is 'feature by easter egg'. Because of the many ways you can run things -- some not all that discoverable -- it's frequent to discover a really cool feature by clicking a wrong button. It's also easy to not discover features at all just because you didn't happen to click the weird buttons. The take-home for me was that it generally required more brain power on my part to remember where things were instead of just looking and knowing what to do. (I'm not good at "memorize these steps", but I am good at "look at it and tell me how it works".)&lt;/p&gt;
&lt;p&gt;All in all, I'm very pleased with my Droid X. I'm still learning my way around the more obscure parts of the system, but I'm really enjoying it.&lt;/p&gt;
&lt;p&gt;Rob&lt;/p&gt;&lt;img src="http://robrich.org/aggbug/69.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Rob Richardson</dc:creator>
            <guid>http://robrich.org/archive/2010/09/07/iPhone-to-Android-via-Droid-X.aspx</guid>
            <pubDate>Tue, 07 Sep 2010 18:30:02 GMT</pubDate>
            <comments>http://robrich.org/archive/2010/09/07/iPhone-to-Android-via-Droid-X.aspx#feedback</comments>
            <wfw:commentRss>http://robrich.org/comments/commentRss/69.aspx</wfw:commentRss>
        </item>
        <item>
            <title>My current CI setup</title>
            <category>Tools</category>
            <link>http://robrich.org/archive/2010/05/20/my-current-ci-setup.aspx</link>
            <description>I was asked by email what my current CI setup is, and did I have a blog post about it.  Um, actually, no.  Oops.  So, here it is.  As always, it's a work in process, and there are lots of unfinished rough edges.  It's also got some phenomenally cool stuff too  Thus, without further ado, my current setup for Continuous Integration:&lt;br /&gt;
&lt;br /&gt;
CruiseControl.NET which runs on SVN commit that runs an NAnt build script which runs:
&lt;p&gt;- MSBuild on one or more solutions&lt;/p&gt;
&lt;p&gt;- aspnet_compiler.exe on all the websites (to validate the code in the markup)&lt;/p&gt;
&lt;p&gt;- Use YUI Compressor to compress CSS files and compress and combine JavaScript files (yes, the irony isn't lost here)&lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;- NUnit tests including:&lt;/p&gt;
&lt;p&gt; &lt;span style=""&gt;      &lt;/span&gt;- Fire up Cassini on each site and insure a carefully selected page doesn't blow chunks. &lt;span style=""&gt; &lt;/span&gt;(e.g. no configuration or initialization errors on each site.)&lt;/p&gt;
&lt;p&gt; &lt;span style=""&gt;      &lt;/span&gt;- Database / code integrity checks like do the enum values match the lookup table content&lt;span style=""&gt;.  &lt;/span&gt;(I realize they're mostly integration tests, and cheesy at that,&lt;span style=""&gt; &lt;/span&gt;but it's a far cry better than the previous state of&lt;span style=""&gt; &lt;/span&gt;zero tests and "hope it works out" deployment.)&lt;/p&gt;
&lt;p&gt;- Deploy content to test server(s), calling iisreset and stopping / restarting services as necessary.&lt;/p&gt;
&lt;p&gt;- Label the CI build via &lt;a href="javascript:void(0);/*1274408532740*/"&gt;svnrevisionlabeler&lt;/a&gt; (so the build number in CCTray matches the SVN version number).&lt;/p&gt;
&lt;p&gt;- Email out to those who want the spam how the build did.  (Personally I prefer data pull mechanisms like CCTray.)&lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;There's also an SVN commit trigger that generates a commit email and sends it out.&lt;/p&gt;
&lt;p&gt;That's what I've got now.&lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;What I want to add to this (given another &lt;a href="javascript:void(0);/*1274408185115*/"&gt;257 hours&lt;/a&gt; in the day):&lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;- JSLint validation of .js files and hopefully script tags in html&lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;- CSS validation of .css files and hopefully style tags in html&lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;- HTML validation to match the page's doctype of .aspx and .html pages&lt;/p&gt;
&lt;p&gt;- SEO evaluation of .aspx and .html pages by crawling the site&lt;/p&gt;
&lt;p&gt;- Database migration via Tarantino or RedGate's Sql Compare Pro &amp;amp; Sql Data Compare Pro&lt;/p&gt;
&lt;p&gt;Once I've got these in place, I'll be confident that the code functions and is of descent quality before I deploy it to the test servers.&lt;span style=""&gt;  &lt;/span&gt;Granted, I haven't validated that it functions correctly, only that it functions completely.&lt;span style=""&gt;  &lt;/span&gt;The next step will be to look to Selenium Grid to validate JS works cross-browser and that various pages function as expected.&lt;span style=""&gt;  &lt;/span&gt;I hope by then I can also kick-start the the idea that writing unit tests to validate the code functions as expected is also a good idea.&lt;/p&gt;
&lt;p&gt;Add a bit of duct tape, a sprinkle of insanity, and that's my CI setup.  Cheers.&lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Rob&lt;br /&gt;
&lt;/p&gt;&lt;img src="http://robrich.org/aggbug/68.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Rob Richardson</dc:creator>
            <guid>http://robrich.org/archive/2010/05/20/my-current-ci-setup.aspx</guid>
            <pubDate>Fri, 21 May 2010 02:50:23 GMT</pubDate>
            <comments>http://robrich.org/archive/2010/05/20/my-current-ci-setup.aspx#feedback</comments>
            <wfw:commentRss>http://robrich.org/comments/commentRss/68.aspx</wfw:commentRss>
        </item>
        <item>
            <title>.IsNullOrEmpty() for List and Dictionary</title>
            <category>Code</category>
            <link>http://robrich.org/archive/2010/05/18/.isnullorempty-for-list-and-dictionary.aspx</link>
            <description>string.IsNullOrEmpty() in C# for strings is awesome.  I pass in a string, it tells me if it was null or blank.  Pre-trim it with something like this :&lt;br /&gt;
&lt;br /&gt;
&lt;span style="font-family: Courier New;"&gt;string.IsNullOrEmpty( ( var ?? "" ).Trim() )&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
and I know if I'm toast or not before the null reference exception or blank screen.&lt;br /&gt;
&lt;br /&gt;
Well, what if I have a List&amp;lt;T&amp;gt;?  Or a Dictionary&amp;lt;T,U&amp;gt;?  Here's extension methods I wrote for checking blank-ness:&lt;br /&gt;
&lt;br /&gt;
&lt;span style="font-family: Courier New;"&gt;    public static bool IsNullOrEmpty&amp;lt;T&amp;gt;( this IList&amp;lt;T&amp;gt; List ) {&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;         return ( List == null || List.Count &amp;lt; 1 );&lt;/span&gt;&lt;span style="font-family: Courier New;" /&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;     }&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;     public static bool IsNullOrEmpty&amp;lt;T,U&amp;gt;( this IDictionary&amp;lt;T,U&amp;gt; Dictionary ) {&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;         return ( Dictionary == null || Dictionary.Count &amp;lt; 1 );&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt; &lt;/span&gt;&lt;span style="font-family: Courier New;"&gt;    }&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
The added benefit of this is I can say:&lt;br /&gt;
&lt;br /&gt;
&lt;span style="font-family: Courier New;"&gt;    myDict.IsNullOrEmpty()&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br /&gt;
which is usually more like the thought I had when I started writing the code.  So I also add this method:&lt;br /&gt;
&lt;br /&gt;
&lt;span style="font-family: Courier New;"&gt;    public static bool  IsNullOrEmpty( this string String ) {&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;         return string.IsNullOrEmpty( ( String ?? "" ).Trim() );&lt;br /&gt;
&lt;/span&gt;&lt;span style="font-family: Courier New;"&gt;    }&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
so I can call it like this:&lt;br /&gt;
&lt;br /&gt;
&lt;span style="font-family: Courier New;"&gt;    myString.IsNullOrEmpty()&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br /&gt;
but since I'm coalescing to empty string before trimming, I can just as easily say:&lt;br /&gt;
&lt;br /&gt;
&lt;span style="font-family: Courier New;"&gt;    public static bool  IsNullOrEmpty( this string String ) {&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;         return ( (String ?? "").Trim() != "" );&lt;br /&gt;
&lt;/span&gt;&lt;span style="font-family: Courier New;"&gt;    }&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
And for good measure, here's a similar JavaScript function I wrote to check for blank-ness:&lt;br /&gt;
&lt;br /&gt;
&lt;span style="font-family: Courier New;"&gt;    function isNullOrEmpty(val) {&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;        var empty = true,&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;            name = null;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;        if ( typeof(val) === 'undefined' || val === null ) {&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;            return true; // It's null or undefined&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;        }&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;        if ( typeof(val) === 'string' ) {&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;            return (val === ''); // It's a string that may or may not be blank&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;        }&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;        if ( typeof(val) === 'object' ) {&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;            if (value.constructor === Array &amp;amp;&amp;amp; val.length === 0) {&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;                return true; // It's an empty array&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;            }&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;            for ( name in val ) {&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;                if ( val.hasOwnProperty(name) ) {&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;                    empty = false;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;                    break;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;                }&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;            }&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;            return empty; // It's an object that has or doesn't have data in it&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;        }&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;        // It's not null or empty&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;        return false;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    }&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
And that, as we say, is null ... or empty.  :D&lt;img src="http://robrich.org/aggbug/67.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Rob Richardson</dc:creator>
            <guid>http://robrich.org/archive/2010/05/18/.isnullorempty-for-list-and-dictionary.aspx</guid>
            <pubDate>Tue, 18 May 2010 23:43:06 GMT</pubDate>
            <comments>http://robrich.org/archive/2010/05/18/.isnullorempty-for-list-and-dictionary.aspx#feedback</comments>
            <slash:comments>1</slash:comments>
            <wfw:commentRss>http://robrich.org/comments/commentRss/67.aspx</wfw:commentRss>
        </item>
        <item>
            <title>Validating web content in CI</title>
            <category>Code</category>
            <category>Tools</category>
            <link>http://robrich.org/archive/2010/05/18/validating-web-content-in-ci.aspx</link>
            <description>If I had another 257 hours in the day, I'd love to build the ultimate web content validator into the &lt;a href="http://ccnet.thoughtworks.com/"&gt;continuous integration&lt;/a&gt; process I now have.  After a successful build, I'd start by kicking off a &lt;a href="http://codeka.com/blogs/index.php/2007/06/18/64_bit_webdev_webserver_exe"&gt;WebDev.WebServer&lt;/a&gt; instance of the site, then fire the &lt;a href="http://weblogs.asp.net/scottgu/archive/2009/06/03/iis-search-engine-optimization-toolkit.aspx"&gt;SEO toolkit&lt;/a&gt; by wrapping it into a &lt;a href="http://blogs.msdn.com/carlosag/archive/2009/11/18/iis-seo-toolkit-start-new-analysis-automatically-through-code.aspx"&gt;.net library&lt;/a&gt;.  Then extend it with &lt;a href="http://blogs.msdn.com/carlosag/archive/2009/11/23/iis-seo-toolkit-crawler-module-extensibility.aspx"&gt;custom tasks&lt;/a&gt; run on each page download: like validating the &lt;a href="http://damianedwards.wordpress.com/2008/10/06/adding-html-validity-checking-to-your-aspnet-web-site-via-unit-tests/"&gt;HTML&lt;/a&gt; and &lt;a href="http://www.emacswiki.org/emacs/FlymakeCSS"&gt;CSS&lt;/a&gt; via W3C, validating the JavaScript via &lt;a href="http://www.jslint.com/"&gt;JSLint&lt;/a&gt;, and for html content, I'd regex out script and style tag content, padding the top of a temp file with whitespace to keep the line numbers right, then validate that as CSS and JavaScript as well.  (I'd rather find an &lt;a href="http://www.w3.org/People/Raggett/tidy/"&gt;offline&lt;/a&gt; &lt;a href="http://onemorebug.com/blog/2007/10/28/html-validation/"&gt;way&lt;/a&gt; to do HTML validation that doesn't involve Cygwin, as I have enough emulation going on here.)  Perhaps I'd wrap it all in an &lt;a href="http://blogs.geekdojo.net/rcase/archive/2005/01/06/5971.aspx"&gt;NAnt&lt;/a&gt; task, or just an NUnit test suite that either reflected through the solution for web.configs or took in a list of projects via &lt;a href="http://nunit.org/?p=testCase&amp;amp;r=2.5"&gt;TestCase&lt;/a&gt; or the project's AppSettings.  (I'd like to be able to authenticate certain requests too, so I can validate the user profile content.  &lt;a href="http://msdn.microsoft.com/en-us/library/microsoft.web.management.seo.crawler.webcrawler.settings%28v=VS.90%29.aspx"&gt;WebCrawler.Settings&lt;/a&gt; exposes a &lt;a href="http://msdn.microsoft.com/en-us/library/microsoft.web.management.seo.crawler.crawlersettings.credentials%28v=VS.90%29.aspx"&gt;Credentials&lt;/a&gt; property, though I've had more success setting an &lt;a href="http://devproj20.blogspot.com/2008/02/assigning-basic-authorization-http.html"&gt;Authorization header&lt;/a&gt; than using HttpWebRequest.Credentials.  Neither gets me &lt;a href="http://www.codeproject.com/Articles/39062/Salient-Web-Security-AccessControlModule.aspx"&gt;through&lt;/a&gt; the &lt;a href="http://www.4guysfromrolla.com/articles/120705-1.aspx"&gt;forms authentication cookie&lt;/a&gt; though as UrlDownloader.WebRequestCreate() has no settings.Cookies.  I'd love a per-url dictionary of "use credentials or don't", though I realize that's totally overkill for the stock use of the SEO toolkit, and more than likely I can just rescan with a &lt;a href="http://msdn.microsoft.com/en-us/library/microsoft.web.management.seo.crawler.crawlersettings.starturl%28v=VS.90%29.aspx"&gt;StartUrl&lt;/a&gt; inside the profile, and an &lt;a href="http://msdn.microsoft.com/en-us/library/microsoft.web.management.seo.crawler.crawlersettings.externallinkcriteria%28v=VS.90%29.aspx"&gt;ExternalLinkCriteria&lt;/a&gt; of SameFolderAndDeeper.  If all else fails, I'd &lt;a href="http://www.red-gate.com/products/reflector/"&gt;Reflector&lt;/a&gt; out the &lt;a href="http://msdn.microsoft.com/en-us/library/microsoft.web.management.seo.crawler.webcrawler%28VS.90%29.aspx"&gt;WebCrawler&lt;/a&gt;, or inject an override into UrlDownloader.OnGetContent() in the same dll.)  More than likely, the report from each of the validators is too big for the build log, so I'd save off each report, named by download url and module, and build an index page dynamically to navigate through them all.  After the report was run, I'd RoboCopy the report tree to a deployment url or find a way to include it into &lt;a href="http://ccnetlive.thoughtworks.com/ccnet/doc/CCNET/Using%20CruiseControl.NET%20with%20Ant.html"&gt;CC.NET's document list&lt;/a&gt;.  Wrap all that up in a bow, and we've got the uber-web validator CI engine.  Now where'd I put that other 257 hours in the day?  And will condensing a few weeks worth of random thoughts and links this tightly get me into the Google Dungeon?&lt;img src="http://robrich.org/aggbug/66.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Rob Richardson</dc:creator>
            <guid>http://robrich.org/archive/2010/05/18/validating-web-content-in-ci.aspx</guid>
            <pubDate>Tue, 18 May 2010 22:27:20 GMT</pubDate>
            <comments>http://robrich.org/archive/2010/05/18/validating-web-content-in-ci.aspx#feedback</comments>
            <wfw:commentRss>http://robrich.org/comments/commentRss/66.aspx</wfw:commentRss>
        </item>
        <item>
            <title>Working with both VS 2010 and 2008 on the team</title>
            <category>Code</category>
            <category>Tools</category>
            <link>http://robrich.org/archive/2010/04/20/working-with-both-vs-2010-and-2008-on-the-team.aspx</link>
            <description>With any software version upgrade, there are those that upgrade on day 1, and can't wait to get the beta bits of v.next, and there are those who wait-n-see, wait for a plugin to get upgraded, or just generally need some time to get comfortable with the change.  Upgrading to Visual Studio 2010 is no exception.&lt;br /&gt;
&lt;br /&gt;
When you've got a team of people and certain people fall into each category, things can get interesting.  In this case, there are some specific steps we can take to facilitate both groups of users simultaneously working on the same code base.&lt;br /&gt;
&lt;br /&gt;
Note that upgrading a solution from .net 3.5 to .net 4.0 is also a step to take, but you can't use VS 2008 with .net 4.0, so you'll need to wait for the entire team to upgrade for that.&lt;br /&gt;
&lt;br /&gt;
Towards the end of using .net 3.5 simultaneouslyin both VS 2008 and VS 2010, the steps are pretty straight-forward.  Much like the adjustments for VS 2008 / VS 2005 compatibility, this is a pretty straight shot.  Here's the steps I took.  I give it the official "&lt;a href="javascript:void(0);/*1271784686303*/"&gt;works on my machine&lt;/a&gt;" seal of approval.&lt;br /&gt;
&lt;br /&gt;
1. Check in any changes to source control.  Make a backup.  Archive the machine.  Get comfortable with it.  If this fails, you'll need it.  :)&lt;br /&gt;
&lt;br /&gt;
2. Fire up Visual Studio 2010, and run the project conversion wizard, insuring you keep the solution in .net 3.5.  It'll add tons of gunk to each .csproj file and to the .sln file, and that's fine.  Getting VS 2010 comfortable with the solution is the biggest leap, and it's also usually the most automatic.&lt;br /&gt;
&lt;br /&gt;
With those done, the rest unfortunately is manual.&lt;br /&gt;
&lt;br /&gt;
3. The first major difference is at the top of the sln file.  A VS 2008 file has these 2 lines at the top:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px; font-family: Courier New;"&gt;Microsoft Visual Studio Solution File, Format Version 10.00&lt;br /&gt;
# Visual Studio 2008&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
A VS 2010 sln file has these 2 lines at the top:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px; font-family: Courier New;"&gt;Microsoft Visual Studio Solution File, Format Version 11.00&lt;br /&gt;
# Visual Studio 2010&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
That's the only difference.  Unfortunately, you can't exactly switch it back and forth locally.  Well, I guess you could, but someone someday will check it in the wrong way, and the build will go boom.  Bad news.&lt;br /&gt;
&lt;br /&gt;
Instead, my chosen solution is to copy the sln file and name the new one with a catchy suffix.  For example, if I have "mysln.sln", I'll name the new one "mysln-10.sln".  Since we ran the project conversion wizard previously, we can copy the sln file to it's new name, add that to source control, and revert the changes to the original (VS 2008-compatable) sln file.&lt;br /&gt;
&lt;br /&gt;
Periodically diff the files and/or invite the team to add / remove projects from both.  This happens so rarely that it doesn't seem an issue.&lt;br /&gt;
&lt;br /&gt;
4. The next major difference is in Web Application Project .csproj files.  At the bottom, in the &amp;lt;Import ...&amp;gt; section, a VS 2008 project has this line:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-family: Courier New;"&gt;&amp;lt;Import Project="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v9.0\WebApplications\Microsoft.WebApplication.targets" /&amp;gt;&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
A VS 2010 project has this line:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-family: Courier New;"&gt;&amp;lt;Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v10.0\WebApplications\Microsoft.WebApplication.targets" /&amp;gt;&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
MSBuild's Condition statement comes in really handy here.  Change the line to these lines and it'll work nicely for both:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-family: Courier New;"&gt;&amp;lt;Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v10.0\WebApplications\Microsoft.WebApplication.targets" Condition="'$(Solutions.VSVersion)' == '9.0'" /&amp;gt;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;&amp;lt;Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v10.0\WebApplications\Microsoft.WebApplication.targets" Condition="'$(Solutions.VSVersion)' == '10.0'" /&amp;gt;&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
5. The C++ project file is drastically different.  VS 2008 uses .vcproj files, VS 2010 uses MSBuild compatible .vcxproj files.  Unfortunately I find no good way to correlate the differences easily.  Here's my solution of choice:&lt;br /&gt;
&lt;br /&gt;
- In the VS 2008 sln file, reference the .vcproj file.  In the VS 2010 sln file, reference the .vcxproj file.&lt;br /&gt;
&lt;br /&gt;
- When changing the project (adding/removing files), you gotta update both.  Yeah, I hate it too.&lt;br /&gt;
&lt;br /&gt;
- Managed code referencing C++ projects gets weird.  Inside the IDE, all you see is the project name, but in the .csproj file is the name of the C++ project file.  Therefore, if VS 2010 notices the vcproj reference, it'll convert it, and when the build server sees the .vcxproj reference, it'll die noting it doesn't support that project type.  To get around it, I use conditionals here too:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-family: Courier New;"&gt;&amp;lt;ProjectReference Condition="'$(MSBuildToolsVersion)' == '&lt;span style="font-weight: bold;"&gt;3.5&lt;/span&gt;'" Include="path\to\project.vcproj"&amp;gt;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;...&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;&amp;lt;/ProjectReference&amp;gt;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;&amp;lt;ProjectReference Condition="'$(MSBuildToolsVersion)' == '&lt;span style="font-weight: bold;"&gt;4.0&lt;/span&gt;'" Include="&lt;/span&gt;&lt;span style="font-family: Courier New;"&gt;path\to\project&lt;/span&gt;&lt;span style="font-family: Courier New;"&gt;.vc&lt;/span&gt;&lt;span style="font-weight: bold; font-family: Courier New;"&gt;x&lt;/span&gt;&lt;span style="font-family: Courier New;"&gt;proj"&amp;gt;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;...&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;&amp;lt;/ProjectReference&amp;gt;&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
On the up-side, if you don't have any native code, you can completely skip this step.&lt;br /&gt;
&lt;br /&gt;
6. Source control: We'll see some new files appear in our solution when working with VS 2010.  Here's how I handled them:&lt;br /&gt;
&lt;br /&gt;
- Commit the new VS 2010 .sln file, and the new VS 2010 .vcxproj files (if any)&lt;br /&gt;
&lt;br /&gt;
- Review the additions to .csproj files and web.config files.  You may deem some of the adjustments it made unnecessary (e.g. the big section about "previous tools version").  After removing them, fire the solution back up in both VS 2010 and VS 2008 to see if either gripes or fires up the conversion wizard.  If it does, sadly, ya gotta leave the gunk in.&lt;br /&gt;
&lt;br /&gt;
- The .ncb and .openncb files are the C++ intelisense database.  Exclude them from the repository.  It's like the .user files -- it'll change or get recreated as soon as you open the IDE.&lt;br /&gt;
&lt;br /&gt;
That's it.  Just a few simple steps and you can simultaneously use VS 2008 and VS 2010 on the same code base.  If you don't have Web Application Projects or you don't have C++ projects, it's even easier.  And now each member of the team can use the tool that works best for them.&lt;br /&gt;
&lt;br /&gt;
Unfortunately, those in VS 2010 can't use the C# 4 conventions yet though.  Push the VS 2008 stragglers to upgrade, switch the solution to .net 4.0, and you'll get a whole new love from your IDE.&lt;br /&gt;
&lt;br /&gt;
[Update]: As a matter of "avoiding unnecessary commits to source control", I removed all the additional tags VS 2010 added, including FileUpgradeFlags.  Thanks to Zuhaib (comment below) who pointed out this is the way to get VS 2010 to not keep doing an upgrade loop.  He noted that he had to put the V10.0 path before the V9.0 path as well, which I didn't need to do.&lt;br /&gt;
&lt;br /&gt;
[Update]: I've found more success matching the MSBuild version rather than the VSVersion in random spots.&lt;img src="http://robrich.org/aggbug/65.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Rob Richardson</dc:creator>
            <guid>http://robrich.org/archive/2010/04/20/working-with-both-vs-2010-and-2008-on-the-team.aspx</guid>
            <pubDate>Tue, 20 Apr 2010 18:29:51 GMT</pubDate>
            <comments>http://robrich.org/archive/2010/04/20/working-with-both-vs-2010-and-2008-on-the-team.aspx#feedback</comments>
            <slash:comments>6</slash:comments>
            <wfw:commentRss>http://robrich.org/comments/commentRss/65.aspx</wfw:commentRss>
        </item>
        <item>
            <title>Presenting jQuery</title>
            <category>Events</category>
            <link>http://robrich.org/archive/2010/04/20/presenting-jquery.aspx</link>
            <description>I had a great honor of presenting both an intro and advanced jQuery class at successive JavaScript Users' Groups.  We had quite a lot of fun discussing both what makes jQuery cool and how one can leverage it for really powerful uses.  &lt;a href="http://robrich.org/content/jQuery_Intro.zip"&gt;Here&lt;/a&gt;'s the slides for the intro, and &lt;a href="http://robrich.org/content/jQuery_Deep_Dive.zip"&gt;here&lt;/a&gt;'s the project for the advanced one.&lt;br /&gt;
&lt;br /&gt;
&lt;span style="font-weight: bold;"&gt;NOTE&lt;/span&gt;: Internet Explorer users will note that the download fails because the files are corrupt.  This is because my server felt the need to gzip the zipped file, and IE doesn't know how to handle that.  The fault is completely mine, but &lt;span style="font-weight: bold;"&gt;the files are not corrupt&lt;/span&gt;.  Download them with Firefox, and you'll do just fine.&lt;br /&gt;
&lt;br /&gt;
In the intro class, the primary topics included:&lt;br /&gt;
&lt;ul&gt;
    &lt;li&gt;including in the page and browser compatibility&lt;/li&gt;
    &lt;li&gt;jQuery selectors (CSS queries)&lt;/li&gt;
    &lt;li&gt;basic jQuery functions (.css() is a great example)&lt;/li&gt;
    &lt;li&gt;script loading paradigms and the $(document).ready() function&lt;/li&gt;
    &lt;li&gt;node traversal: .parent(), .children(), .find(), etc&lt;/li&gt;
    &lt;li&gt;a brief look at AJAX calls, though we didn't execute any of them&lt;/li&gt;
    &lt;li&gt;a brief walk through animation (.fadeTo() and .show() / .hide())&lt;/li&gt;
&lt;/ul&gt;
Interspersed in this we discussed advanced topics (mostly answers to questions):&lt;br /&gt;
&lt;ul&gt;
    &lt;li&gt;put CSS in the head, put scripts at the end for best performance&lt;/li&gt;
    &lt;li&gt;avoid redundant trips to the dom by caching selections or chaining&lt;/li&gt;
    &lt;li&gt;catch and handle HTTP errors when making AJAX calls&lt;/li&gt;
    &lt;li&gt;avoid global JavaScript variables&lt;/li&gt;
&lt;/ul&gt;
In the advanced class, we talked about:&lt;br /&gt;
&lt;ul&gt;
    &lt;li&gt;a quick review of the intro class&lt;/li&gt;
    &lt;li&gt;AJAX with $.get(), $.post(), $.getJSON, and $.ajax()&lt;/li&gt;
    &lt;li&gt;AJAX error handling in jQuery&lt;/li&gt;
    &lt;li&gt;built-in jQuery animation, and how to layer effects&lt;/li&gt;
    &lt;li&gt;a lap through our favorite plugins&lt;/li&gt;
    &lt;li&gt;building a plugin: both a $(...).dosomethingwitheach() and a $.dosomethingwhencalled() plugin&lt;/li&gt;
    &lt;li&gt;passing dom ids from server-side frameworks into client-side code&lt;/li&gt;
    &lt;li&gt;using jQuery and jQuery UI from Google's CDN&lt;/li&gt;
&lt;/ul&gt;
We also had fun discussing random off-topic ideas:&lt;br /&gt;
&lt;ul&gt;
    &lt;li&gt;building markup using arrays vs. dom snippits&lt;/li&gt;
    &lt;li&gt;mechanisms for passing page data into library files&lt;/li&gt;
    &lt;li&gt;a quick lap around compression&lt;/li&gt;
    &lt;li&gt;jQuery UI theming&lt;/li&gt;
    &lt;li&gt;tools to use when developing -- to that end, the resources list inside the advanced download is quite helpful&lt;/li&gt;
&lt;/ul&gt;
All in all, both days were a barrel of fun, and I was quite honored to be a part of it.&lt;img src="http://robrich.org/aggbug/64.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Rob Richardson</dc:creator>
            <guid>http://robrich.org/archive/2010/04/20/presenting-jquery.aspx</guid>
            <pubDate>Tue, 20 Apr 2010 17:42:03 GMT</pubDate>
            <comments>http://robrich.org/archive/2010/04/20/presenting-jquery.aspx#feedback</comments>
            <wfw:commentRss>http://robrich.org/comments/commentRss/64.aspx</wfw:commentRss>
        </item>
        <item>
            <title>iPhone: it's not really a digital convergence device</title>
            <category>Rants</category>
            <link>http://robrich.org/archive/2010/03/04/iphone-its-not-really-a-digital-convergence-device.aspx</link>
            <description>It finally gelled in my head what bugged me about the iPhone's lack of simultaneous processing.  It's not truly a digital convergence device.  It's a sequential task device.&lt;br /&gt;
&lt;br /&gt;
A digital convergence device is a device that does lots of things: MP3/FM music player, alarm clock, cell phone, web browser, calendar, address book, GPS-enabled map, pedometer, etc.  The purpose of such a device is to do all of these things, not each of these things.  If I can't do them all simultaneously, it isn't a convergence device, it's a sequential device.  The iPhone is exactly that: a sequential task device.&lt;br /&gt;
&lt;br /&gt;
If I've decided I'm going to IM, I can't do anything else with the phone -- I can't browse the web, can't check on news feeds, can't check email.  The device is useless to me until I get an IM.  If I wanted an IM-only device, I'd have bought one.  I bought a digital convergence device.  The same could be said of any app that receives data: Skype, Google Voice, Email, RSS feeds, Facebook sync, texting services, RTMPGs, etc, as well as anything that monitors anything like Google Latitude, a pedometer app, navigation app, etc.  Anything I expect to alert me to something or to keep track of something either needs to "push notification" me, or I forfeit all other features of this device as I use it for that task.  If it goes the push notification route, the logic must be in the cloud, not on my device, can't be peer-to-peer, and can't get real-time status call-backs -- e.g. it can't auto-detect where I am and update my progress.  As an extreme example, I can't count the number of times I was playing some random game and walked around for a while looking for a clock so I didn't need to kill my game.  My convergence device was in "single task" mode.&lt;br /&gt;
&lt;br /&gt;
The notable exceptions to the single-use mode are all Apple apps: phone, iTunes, and alert apps such as calendar and text alerts.  I can get a call which immediately and irrevocably halts anything I'm doing (including upgrading OS versions), and I can listen to iTunes music while I do a few other things.  But what if my music player of choice is Pandora?&lt;br /&gt;
&lt;br /&gt;
I think you see where I'm going with this.  The iPhone clearly isn't a digital convergence device.  Neither is the iPad.  It is clearly a sequential task device, a data snacker device.  Pick the function you want it to do now, and it'll do great.  Want to do 2 things at once?  Well of course, buy two of them.  Want to get alerted when something happens?  Buy a specialty device too.  Um, I think I'll pass.&lt;img src="http://robrich.org/aggbug/63.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Rob Richardson</dc:creator>
            <guid>http://robrich.org/archive/2010/03/04/iphone-its-not-really-a-digital-convergence-device.aspx</guid>
            <pubDate>Thu, 04 Mar 2010 18:32:12 GMT</pubDate>
            <comments>http://robrich.org/archive/2010/03/04/iphone-its-not-really-a-digital-convergence-device.aspx#feedback</comments>
            <wfw:commentRss>http://robrich.org/comments/commentRss/63.aspx</wfw:commentRss>
        </item>
        <item>
            <title>iPhone: the rise and fall of the latest Apple gadget</title>
            <category>Rants</category>
            <link>http://robrich.org/archive/2010/02/19/iphone-the-rise-and-fall-of-the-latest-apple-gadget.aspx</link>
            <description>The iPhone.  It's a wonderful device.  It was a game changer.  3 years ago when it was released, it revolutionized the phone landscape.  For the first time, people had a portable digital convergence device.  (Ok, maybe it wasn't the first, maybe it wasn't the best, maybe it was just targeted at regular people instead of corporate users.)  For the first time, we could walk around with a web browser, a calendar, a music player, an address book, and really manage our lives on the go.  (Ok, I'll grant that BlackBerry defined that niche for business users, but the iPhone made it cool and made it work for consumers.)&lt;br /&gt;
&lt;br /&gt;
I'm hesitant to sit on the bleeding edge.  I waited until Service Pack 1 to get my iPhone 3G.  It was nearly 2 years ago.  It was a game changer for me too.  At random times in random places, I could check email, surf the web to answer the nagging question, create calendar entries, or just horse around.  I could pull up a map of where I am and where I wanted to go, so the preparation for travel wasn't as urgent.  I could schedule stuff on my calendar, so no more wads of post-its hoping I guessed right.  And reading email in the 2 minutes standing in line or on the walk home from dropping kids off at school is awesome.  It truly revolutionized my world for the better.&lt;br /&gt;
&lt;br /&gt;
Fast forward 3 years for Apple, nearly 2 years for me.  I waited with baited breath at every Apple announcement for the thing that'd keep the iPhone cool or for them to switch to a new carrier.  (The tag-line for the iPhone I've heard not a few times is, "The iPhone is so cool it almost makes up for being stuck on AT&amp;amp;T.")  In that time, they've started to back-fill some missing features, and made subtle improvements, but they've really never leaped out and grabbed me again.&lt;br /&gt;
&lt;br /&gt;
The following features were instantly missing from my experience, and with few exceptions, Apple really never delivered:&lt;br /&gt;
&lt;ul&gt;
    &lt;li&gt;OTA Syncing&lt;/li&gt;
    &lt;li&gt;contact and calendar categories&lt;/li&gt;
    &lt;li&gt;turn-by-turn voice navigation&lt;br /&gt;
    &lt;/li&gt;
    &lt;li&gt;copy &amp;amp; paste&lt;/li&gt;
    &lt;li&gt;an IM and VOIP client that's always on (e.g. I'll still get new IMs or receive VOIP calls if I switched over to read my email)&lt;br /&gt;
    &lt;/li&gt;
    &lt;li&gt;3rd party data on maps (e.g. "map all my contacts" or "where is the  closest gas station on my route" or "does Acme, Inc. have an office near  me?"  I &lt;a href="javascript:void(0);/*1266596507646*/"&gt;ranted&lt;/a&gt; about  this previously -- that I want to shove a kml file into maps.)&lt;br /&gt;
    &lt;/li&gt;
    &lt;li&gt;OpenVPN / RDP client&lt;br /&gt;
    &lt;/li&gt;
    &lt;li&gt;disable auto-rotation based on the accelerometer -- so I can read in bed&lt;/li&gt;
&lt;/ul&gt;
Ok, I'll grant that some of these things have come with third-party apps.  Some have come as jailbreak apps.  Some are just not there.&lt;br /&gt;
&lt;br /&gt;
And I get that I'm hardly a typical user.  Most users looking for "IM client" on a phone would point me at text messaging.  (Yeah, but I can't text into an MSN IM contact.  And if a Skype contact IMs me back but I'm sending an email, I never get it.  Yeah, I know Trillian Astra is "almost there".  I've been waiting on them for as long too.)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Meanwhile, over the last 3 years since the iPhone changed the landscape, Android and most recently Windows Mobile 7 have come into view.  Neither is as polished as the iPhone was at launch.  (I've not seen a Win 7 phone, but I've seen a good deal of Android phones.)  Android only recently got pinch-to-zoom, and really only on Nexus One, and the Droid's map application.  But I see a lot more potential in these devices than in the iPhone.&lt;br /&gt;
&lt;br /&gt;
For instance, me and my family were on vacation in California.  We were caravaning with friends on an unfamiliar stretch of highway.  We popped open the Droid, put in our destination in the voice guidance app, and went back to the home screen.  When something of interest popped up in a friends' cars, they'd text us or tweet.  As the perfect example, my wife was on the web looking up something, the navigation was telling me where to turn, and a text came in.  It all happened simultaneously.  It just worked.&lt;br /&gt;
&lt;br /&gt;
I've played with the Droid pretty extensively since.  I can push the search button, and pull up a web page or a contact or directions.  The back button is just truly awesome -- getting me back a web page or back into the previous app or back a page in the app.   It just works.  The syncing is awesome because it just works.  I'm very good at forgetting day-to-day stuff, and so I often find I haven't sync'd my calendar in a while, and when I do, I double-booked myself.  There's no android sync procedure -- it just does it.&lt;br /&gt;
&lt;br /&gt;
And don't get me started on the iPhone carrier lock-in.  I probably would've gotten a 3GS if my carrier of choice was announced last July.  As it stands, I'm the only member of my circle of mobiles that can't use free mobile-to-mobile minutes ... because I'm stuck in AT&amp;amp;T -- say nothing of the service.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
When Android finally got pinch-to-zoom, it was a done deal.  I'm sold.  As soon as my AT&amp;amp;T contract is up, I'm sailing away on Android.  It'll let me use it the way I want.&lt;br /&gt;
&lt;br /&gt;
Rob&lt;img src="http://robrich.org/aggbug/62.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Rob Richardson</dc:creator>
            <guid>http://robrich.org/archive/2010/02/19/iphone-the-rise-and-fall-of-the-latest-apple-gadget.aspx</guid>
            <pubDate>Fri, 19 Feb 2010 17:02:15 GMT</pubDate>
            <comments>http://robrich.org/archive/2010/02/19/iphone-the-rise-and-fall-of-the-latest-apple-gadget.aspx#feedback</comments>
            <wfw:commentRss>http://robrich.org/comments/commentRss/62.aspx</wfw:commentRss>
        </item>
        <item>
            <title>VS's Cassini (Web Server) + Fiddler = Remote testing</title>
            <category>Tools</category>
            <link>http://robrich.org/archive/2010/02/15/vss-cassini-web-server-fiddler-remote-testing.aspx</link>
            <description>From time to time, I have a need to debug a web site from a browser not installed on my current machine.  Safari/Mac or phone browsers are the quintessential example.  Granted, if it happens server-side, it happens for all browsers, and if it happens client-side, debugging server-side may not help.  But there's that one random time every now and again when this becomes quite important: debug server, client isn't on localhost.  Perhaps it's easier to replay the request from the browser where the problem happened rather than figure out how to simulate it locally.  Perhaps it's a weird combination of race conditions that make it happen.  Perhaps it's how the cookies built up that led to cruft not easily reproducible elsewhere.  Alas, being able to debug the server from a non-local client comes in mighty handy now-and-again.&lt;br /&gt;
&lt;br /&gt;
As we all know, Visual Studio's built in web server (originally based on Cassini) is hard-coded to only bind to loopback.  If I had my way that wouldn't be the case, but here we are.  Traditionally, we'd need to install our site in IIS, attach to the w3svc service, and debug that way.  (Even if you're not on a 64-bit box, you can empathize with how intense that is.)&lt;br /&gt;
&lt;br /&gt;
Fiddler is an awesome tool for watching network traffic flow between browser and web server.  In the end, everything you send between server and browser is either HTML, CSS, or JavaScript (or binary content like images and plug-ins), so watching it happen is incredibly insiteful.  We'll not use Fiddler for that here, but I highly recommend it for that work as well.  (There's times when watching the traffic yields insights quite difficult to see any other way.)  Alas, today we'll just mis-use it as a proxy server that can forward traffic back and forth from a LAN address to a loopback address.&lt;br /&gt;
&lt;br /&gt;
Here's the steps:&lt;br /&gt;
&lt;br /&gt;
1. Download and install Fiddler from &lt;a rel="nofollow" href="www.fiddlertool.com"&gt;www.fiddlertool.com&lt;/a&gt; on the same machine as Visual Studio.&lt;br /&gt;
&lt;br /&gt;
2. Tools menu -&amp;gt; Fiddler Options -&amp;gt; Connections tab: click on "Allow remote computers to connect", and push ok.  (While I'm in here, I usually turn off IPv6 in the General tab as that's another no-no in VS's version of Cassini.)&lt;br /&gt;
&lt;br /&gt;
3.  Rules menu -&amp;gt; Customize Rules.  For me, it opened up CustomRules.js in notepad.&lt;br /&gt;
&lt;br /&gt;
4. Scroll down to the function labeled &lt;span style="font-family: Courier New;"&gt;OnBeforeRequest &lt;/span&gt;and add a line kinda like this:&lt;br /&gt;
&lt;br /&gt;
&lt;span style="font-family: Courier New;"&gt;if (oSession.host.toLowerCase() == "192.168.1.5:8888") oSession.host = "localhost:45678";&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
Replace the first IP with the address of your machine (ipconfig /all from a cmd will give you this), and replace the port at the end with what ever you set / VS assigned to your project.  (Inside Visual Studio, in the project properties window, in the web tab, I always set it to "Specific Port" so I don't have to come in here and futz with it every time VS wants to "help".)&lt;br /&gt;
&lt;br /&gt;
This line says "if the inbound request says it's going to this ip and port (that fiddler is listening to), reroute it to this other ip and port (that Visual Studio's web debugger is listening to).&lt;br /&gt;
&lt;br /&gt;
5. Scroll down a bit further in CustomRules.js to the &lt;span style="font-family: Courier New;"&gt;OnBeforeResponse &lt;/span&gt;function and add a line kinda like this:&lt;br /&gt;
&lt;br /&gt;
&lt;span style="font-family: Courier New;"&gt;oSession.utilReplaceInResponse("localhost:45678","192.168.1.5:8888");&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
You'll also need to change the IP and port here to match the changes above as well, but note they're reversed.&lt;br /&gt;
&lt;br /&gt;
This line says "if the body of the outbound message includes text that routes to Visual Studio's debugger, rewrite it to point to Fiddler."&lt;br /&gt;
&lt;br /&gt;
6. Save this file, restart Fiddler, and try it out.&lt;br /&gt;
&lt;br /&gt;
(You may also have to adjust firewall rules on your development machine to allow inbound TCP connections on port 8888.)&lt;br /&gt;
&lt;br /&gt;
And that's it!  Now you're remote debugging your project from a non-local address.&lt;br /&gt;
&lt;br /&gt;
One final note: ideally, we'd create another rule in &lt;span style="font-family: Courier New;"&gt;OnBeforeResponse &lt;/span&gt;that would rewrite headers as well.  I haven't been very successful in doing that, so I haven't included all the ways that don't work.  But if you're sending back a redirect to a full url without this rule, your client will happily time out looking for content on itself.  Why itself?  Because you redirected to localhost.  :D&lt;br /&gt;
&lt;br /&gt;
Rob&lt;img src="http://robrich.org/aggbug/61.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Rob Richardson</dc:creator>
            <guid>http://robrich.org/archive/2010/02/15/vss-cassini-web-server-fiddler-remote-testing.aspx</guid>
            <pubDate>Mon, 15 Feb 2010 18:25:58 GMT</pubDate>
            <comments>http://robrich.org/archive/2010/02/15/vss-cassini-web-server-fiddler-remote-testing.aspx#feedback</comments>
            <slash:comments>1</slash:comments>
            <wfw:commentRss>http://robrich.org/comments/commentRss/61.aspx</wfw:commentRss>
        </item>
        <item>
            <title>Logging and Rewriting Service Calls</title>
            <category>Code</category>
            <link>http://robrich.org/archive/2010/02/15/logging-and-rewriting-service-calls.aspx</link>
            <description>I love web services.  I love how easy it is to wire up a JavaScript client either through jQuery or through a ServiceProxy.  I love how I can point a .net client at a service, and I get strongly typed command parameters.  And my latest love of asmx services in C# is the SoapExtension.  A SoapExtension is a class that allows you to inspect outgoing or incoming services to get at the data involved.  I use them for logging service calls (frequency, duration, errors), and for rewriting output (fixed a "double html encoded" bug, remove whitespace, rewrite SoapFaults, etc).&lt;br /&gt;
&lt;br /&gt;
The technique is incredibly awesome, and I can't claim original ownership, but I can claim excellent results.  A few simple steps:&lt;br /&gt;
&lt;br /&gt;
1. Create a class that derives from SoapExtension.&lt;br /&gt;
&lt;br /&gt;
2. Override ProcessMessage, handling the SoapMessage's Stage that you're interested in.&lt;br /&gt;
&lt;br /&gt;
3. Add either a web.config entry (to handle all inbound and outbound service calls), or put an attribute on each WebMethod you want to track.&lt;br /&gt;
&lt;br /&gt;
Voila!  You've got a soap extension.&lt;br /&gt;
&lt;br /&gt;
Here's my ExampleSoapExtension (business logic replaced with TODO to keep it simple):&lt;br /&gt;
&lt;br /&gt;
&lt;span style="font-family: Courier New;"&gt;namespace ExampleNamespace {&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;     #region using&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;     using System;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;     using System.Data.Linq;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;     using System.IO;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;     using System.Linq;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;     using System.Text.RegularExpressions;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;     using System.Threading;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;     using System.Web;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;     using System.Web.Services.Protocols;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;     #endregion&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;     public class ExampleSoapExtension : SoapExtension {&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;         private Stream originalStream; // The original stream&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;         private Stream internalStream; // Our new stream we chained into place&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;         public ExampleSoapExtension() {&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;         }&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;         // Called the first time the Web service is used if configured with a config file&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;         public override object GetInitializer( Type t ) {&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;             return typeof( ExampleSoapExtension );&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;         }&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;         // Called the first time the Web service is used if configured with an attribute&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;         public override object GetInitializer( LogicalMethodInfo methodInfo, SoapExtensionAttribute attribute ) {&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;             return attribute;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;         }&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;         // Called each time the Web service is used and gets passed the data from GetInitializer() method&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;         public override void Initialize( object initializer ) {&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;             // Get the attribute here and pull whatever you need off it.&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;             //ExampleSoapExtensionAttribute attr = initializer as ExampleSoapExtensionAttribute;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;         }&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;         // The ChainStream() method gives us a chance to grab the SOAP messages as they go by&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;         public override Stream ChainStream( Stream stream ) {&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;             // Save the original stream&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;             originalStream = stream;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;             // Create and return our own in its place&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;             internalStream = new MemoryStream();&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;             return internalStream;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;         }&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;         // The ProcessMessage() method is called between each step in the server-side process&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;         public override void ProcessMessage( SoapMessage message ) {&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;             switch ( message.Stage ) {&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;                 case SoapMessageStage.BeforeDeserialize:&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;                     // About to handle incoming SOAP request&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;                     string requestContent = StreamToText( this.originalStream, false );&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;                     WriteIncomingToLog( message, requestContent );&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;                     TextToStream( requestContent, this.internalStream, true );&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;                     break;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;                 case SoapMessageStage.AfterDeserialize:&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;                     // incoming SOAP request has been deserialized&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;                     WriteInputToLog( message );&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;                     break;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;                 case SoapMessageStage.BeforeSerialize:&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;                     // About to prepare outgoing SOAP Response&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;                     WriteOutputToLog( message );&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;                     break;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;                 case SoapMessageStage.AfterSerialize:&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;                     // outgoing SOAP response is ready to go&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;                     string responseContent = null;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;                     if ( message.Exception != null ) {&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;                         // Rewrite soap fault as valid soap message with internal error code&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;                         // Though it might be nice to do this before serialization, we can't inject our own return value easily&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;                         responseContent = RewriteError( message );&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;                     } else {&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;                         responseContent = StreamToText( this.internalStream, true );&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;                     }&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;                     WriteOutgoingToLog( message, responseContent );&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;                     TextToStream( responseContent, this.originalStream, false );&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;                     break;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;                 default:&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;                     throw new Exception( "invalid stage" );&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;             }&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;         }&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;         private static string StreamToText( Stream stream, bool rewindBefore ) {&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;             if ( rewindBefore ) {&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;                 stream.Position = 0;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;             }&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;             TextReader reader = new StreamReader( stream );&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;             string content = reader.ReadToEnd();&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;             return content;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;         }&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;         private static void TextToStream( String text, Stream stream, bool rewindAfter ) {&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;             TextWriter writer = new StreamWriter( stream );&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;             writer.Write( text );&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;             writer.Flush();&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;             if ( rewindAfter ) {&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;                 stream.Position = 0;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;             }&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;         }&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;         #region WriteIncomingToLog&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;         /// &amp;lt;summary&amp;gt;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;         /// Write the incoming stream content to the logs&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;         /// &amp;lt;/summary&amp;gt;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;         private void WriteIncomingToLog( SoapMessage Message, string Content ) {&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;             HttpRequest req = HttpContext.Current.Request;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;             // TODO: Log various parameters here including the incoming xml and start date&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;         }&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;         #endregion&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;         #region WriteInputToLog&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;         private void WriteInputToLog( SoapMessage Message ) {&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;             ServiceInputObject input = null;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;             if ( Message.MethodInfo.InParameters.Length &amp;gt; 0 ) {&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;                 try {&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;                     input = Message.GetInParameterValue( 0 ) as ServiceInputObject;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;                 } catch ( Exception /*ex*/ ) {&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;                     input = null;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;                 }&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;             }&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;             if ( input != null ) {&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;             &lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;                 // TODO: Log the message details&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;                 &lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;             }&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;         }&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;         #endregion&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;         #region WriteOutputToLog&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;         private void WriteOutputToLog( SoapMessage Message ) {&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;             if ( Message.Exception != null ) {&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;                 // The return object doesn't exist because an exception was thrown&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;                 // RewriteError will create one&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;                 return;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;             }&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;             ServiceOutputObject output = null;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;             if ( Message.MethodInfo != null &amp;amp;&amp;amp; Message.MethodInfo.ReturnType != null &amp;amp;&amp;amp; Message.MethodInfo.ReturnType.FullName != "System.Void" ) {&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;                 output = Message.GetReturnValue() as ServiceOutputObject;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;             } else {&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;                 output = null;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;             }&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;             if ( output != null ) {&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;             &lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;                 // TODO: Log the output details&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;                 &lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;             }&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;         }&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;         #endregion&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;         #region WriteOutgoingToLog&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;         /// &amp;lt;summary&amp;gt;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;         /// Write the message in the stream to the log4net log&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;         /// &amp;lt;/summary&amp;gt;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;         private void WriteOutgoingToLog( SoapMessage Message, string Content ) {&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;             string outboundData = GetStreamText( StreamToLog );&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;             // TODO: Log the outgoing xml string and end date&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;         }&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;         #endregion&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;         #region CaptureError&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;         /// &amp;lt;summary&amp;gt;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;         /// Insure the exception is logged&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;         /// &amp;lt;/summary&amp;gt;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;         private Stream CaptureError( SoapMessage Message, Stream stream ) {&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;             // Log the exception&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;             Exception ex = Message.Exception;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;             while ( ex != null &amp;amp;&amp;amp; ex.InnerException != null &amp;amp;&amp;amp; ex.GetType() == typeof( SoapException ) ) {&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;                 ex = ex.InnerException;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;             }&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;             &lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;             // TODO: Log the exception&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;             &lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;             // TODO: Form the output reply and serialize it&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;             string soapMessage = null;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;             // Now send this as the out stream instead of the &amp;lt;soap:Fault&amp;gt; stream we had before&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;             MemoryStream outStream = new MemoryStream();&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;             TextWriter writer = new StreamWriter( outStream );&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;             writer.WriteLine( soapMessage );&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;             writer.Flush();&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;             outStream.Position = 0;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;             // And send the 200 status instead of 400&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;             //HttpContext.Current.Response.StatusCode = 200;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;             // Here's the content to send instead of what ever you had&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;             return outStream;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;         }&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;         #endregion&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;     }&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;     [AttributeUsage( AttributeTargets.Method )]&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;     public class ExampleSoapExtensionAttribute : SoapExtensionAttribute {&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;         public ExampleSoapExtensionAttribute() {&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;             this.Priority = 1;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;         }&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;         public override int Priority { get; set; }&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;         // Specifies the class of the SOAP Extension to use with this method&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;         public override Type ExtensionType {&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;             get { return typeof( ExampleSoapExtension ); }&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;         }&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;     }&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt; }&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
My current quest: how do I do this in WCF?  Is there such a thing as a "message watcher" or "handler" or "pipeline interceptor"?  All these terms on Google come up blank.  With WCF's new configuration-less services and MS's desire to move on past asmx, WCF seems a foregone conclusion.  For me, this is the last hurdle.&lt;br /&gt;
&lt;br /&gt;
Rob&lt;img src="http://robrich.org/aggbug/60.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Rob Richardson</dc:creator>
            <guid>http://robrich.org/archive/2010/02/15/logging-and-rewriting-service-calls.aspx</guid>
            <pubDate>Mon, 15 Feb 2010 17:42:51 GMT</pubDate>
            <comments>http://robrich.org/archive/2010/02/15/logging-and-rewriting-service-calls.aspx#feedback</comments>
            <slash:comments>3</slash:comments>
            <wfw:commentRss>http://robrich.org/comments/commentRss/60.aspx</wfw:commentRss>
        </item>
    </channel>
</rss>