<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-35759427</id><updated>2011-08-16T22:10:16.340-05:00</updated><category term='Software'/><category term='Biking'/><category term='Faith'/><category term='iPhone'/><category term='General'/><category term='Technology'/><category term='Politics'/><title type='text'>Beyond This Point</title><subtitle type='html'>Pete's weblog</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://beyondthispoint.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/35759427/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://beyondthispoint.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Pete</name><uri>http://www.blogger.com/profile/16688548320976910930</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://photos1.blogger.com/blogger2/1347/4365/240/gse_multipart43909.jpg'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>45</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-35759427.post-6465944006105284130</id><published>2010-04-21T09:56:00.001-05:00</published><updated>2010-04-21T09:56:42.583-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Technology'/><category scheme='http://www.blogger.com/atom/ns#' term='iPhone'/><title type='text'>GeeTasks Pro</title><content type='html'>&lt;p&gt;I’m a bit of a personal productivity junky and it seems I’m always trying out the latest time-management apps on computers and my iPhone.&amp;#160; Well, I think I’ve finally settled on one I really like: &lt;a href="http://www.geetasks.com/"&gt;GeeTasks Pro&lt;/a&gt;.&lt;/p&gt;  &lt;p&gt;GeeTasks is basically a rich GUI for &lt;a href="mail.google.com/tasks"&gt;Google Tasks&lt;/a&gt;, a TO-DO list in the Google cloud that integrates with GMail.&amp;#160; So the nice thing is you can manage your tasks on any computer with a web browser and then use GeeTasks when you’re on your iPhone (or iPod Touch).&amp;#160; Google also has an iPhone web interface for Google Tasks, but GeeTasks is much more functional and, since it caches all your task data locally, it works when you’re not connected to the internet!&lt;/p&gt;  &lt;p&gt;The really cool thing about GeeTasks is it’s so easy to use (in some ways, more so than the Google Tasks web app).&amp;#160; The developer is also actively improving it and even added features based on my personal suggestions!&lt;/p&gt;  &lt;p&gt;So if you’re looking for a nice anywhere TO-DO app solution, I highly suggest you check it out!&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/35759427-6465944006105284130?l=beyondthispoint.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://beyondthispoint.blogspot.com/feeds/6465944006105284130/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=35759427&amp;postID=6465944006105284130' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/35759427/posts/default/6465944006105284130'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/35759427/posts/default/6465944006105284130'/><link rel='alternate' type='text/html' href='http://beyondthispoint.blogspot.com/2010/04/geetasks-pro.html' title='GeeTasks Pro'/><author><name>Pete</name><uri>http://www.blogger.com/profile/16688548320976910930</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://photos1.blogger.com/blogger2/1347/4365/240/gse_multipart43909.jpg'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-35759427.post-5619507360855083294</id><published>2009-10-16T15:57:00.002-05:00</published><updated>2009-10-19T23:53:56.253-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='General'/><title type='text'>First Snow</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_Qd3LFpzGA5A/StimCCvUKcI/AAAAAAAAAgM/KVfxXsisIss/s1600-h/IMG_1253.JPG"&gt;&lt;img style="width: 400px; height: 300px" id="BLOGGER_PHOTO_ID_5393243107801442754" border="0" alt="" src="http://3.bp.blogspot.com/_Qd3LFpzGA5A/StimCCvUKcI/AAAAAAAAAgM/KVfxXsisIss/s400/IMG_1253.JPG" /&gt;&lt;/a&gt;   &lt;div&gt;This picture was just too cute not to post. Here’s Brinna, my 1-year old daughter about a week ago when we had our first snow of the year. This is her first experience with snow, at least as a walking human being. I’m sure she doesn’t remember it from last winter. You just have to wonder what’s going through her mind right at this moment. &lt;/div&gt;  &lt;div&gt; &lt;/div&gt;  &lt;div&gt;&lt;/div&gt;  &lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Probably something like: “Why the heck are we getting snow in early October?!?”&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_Qd3LFpzGA5A/St1CPBGyyuI/AAAAAAAAAgU/AXIxEsRS9oY/s1600-h/IMG_1261.JPG"&gt;&lt;img src="http://2.bp.blogspot.com/_Qd3LFpzGA5A/St1CPBGyyuI/AAAAAAAAAgU/AXIxEsRS9oY/s400/IMG_1261.JPG" border="0" alt="" id="BLOGGER_PHOTO_ID_5394540754422123234" style="cursor: pointer; width: 300px; height: 400px; " /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/35759427-5619507360855083294?l=beyondthispoint.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://beyondthispoint.blogspot.com/feeds/5619507360855083294/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=35759427&amp;postID=5619507360855083294' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/35759427/posts/default/5619507360855083294'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/35759427/posts/default/5619507360855083294'/><link rel='alternate' type='text/html' href='http://beyondthispoint.blogspot.com/2009/10/first-snow.html' title='First Snow'/><author><name>Pete</name><uri>http://www.blogger.com/profile/16688548320976910930</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://photos1.blogger.com/blogger2/1347/4365/240/gse_multipart43909.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_Qd3LFpzGA5A/StimCCvUKcI/AAAAAAAAAgM/KVfxXsisIss/s72-c/IMG_1253.JPG' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-35759427.post-6201114548959120449</id><published>2009-08-23T23:59:00.000-05:00</published><updated>2009-08-24T00:21:26.996-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Software'/><title type='text'>Using a Mobile Source Code Repository</title><content type='html'>&lt;p&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="corona_laptop" border="0" alt="corona_laptop" src="http://lh3.ggpht.com/_Qd3LFpzGA5A/SpIjLj6evHI/AAAAAAAAAeA/5PQLkF2mzfI/corona_laptop1.jpg?imgmax=800" width="239" height="197" /&gt; &lt;/p&gt;  &lt;p&gt;If you’re a consultant or just do a lot of coding on the side either for profit or to keep your skills sharp, it’s often nice to be able to store your code in a &lt;a href="http://en.wikipedia.org/wiki/Source_code_repository"&gt;source code repository&lt;/a&gt;. The benefits of a personal repository are almost the same as what you get with a repository shared by a team of developers (history-tracking of your code, the ability to branch and merge, easy roll-back of uncommitted changes), the only different is that you’re the only user.&lt;/p&gt;  &lt;p&gt;But setting up a reliable source code repository is not trivial, especially if you want to be able to access it while on the go. The easy route is to pay for a hosted solution, but that requires an internet connection (at least for doing source code operations) and they usually cost money if you want something decent. &lt;/p&gt;  &lt;p&gt;I’ve come up with a couple simple approaches that allow you to have a personal repository that you control, that can be accessed from anywhere whether you have an internet connection or not, and that can get backed up in the event of data corruption.&lt;/p&gt;  &lt;h2&gt;Virtualization&lt;/h2&gt;  &lt;p&gt;Before I jump into the details, I’d like to say a couple things about virtualization since it is a big component of the solutions I came up with (especially the second). While you can easily code using tools installed directly on your host OS, I highly recommend isolating your software development environments on one or more virtual machines (VM’s). There are several reasons for this:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;It allows you to develop on operating systems that are more comparable to what your application may be running on in production. Most workstation OS’s (like Windows Vista or Windows 7) do not come with all of the server-side components or are just not quite configured the same as the server OS your app may be running on in production (Windows 2000 Server, Windows Server 2003, Windows Server 2008, etc). Some server-side application services (like SharePoint) won’t even run on a non-server OS. &lt;/li&gt;    &lt;li&gt;It allows you to have multiple different development environments. If you have one project or client that uses Visual Studio 2005 and another that uses 2008, it’s safer to run them on separate operating systems to prevent interference. &lt;/li&gt;    &lt;li&gt;It allows you to easily play with new beta development tools without hosing up your working development environments. Nothing like installing the latest beta of VS2010 and finding out VS2008 no longer loads your paying customer’s solution. &lt;/li&gt;    &lt;li&gt;If your host OS is not Windows (ex: Mac OSX) and you want to develop Windows applications, you don’t have much of a choice! &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;Which virtualization option you go with is pretty irrelevant. For the longest time, I was a big user of Microsoft’s &lt;a href="http://www.microsoft.com/windows/virtual-pc/"&gt;Virtual PC&lt;/a&gt;. It was free and it worked great. Since then I’ve moved to VMWare’s &lt;a href="http://www.vmware.com/products/ws/"&gt;Workstation&lt;/a&gt; and &lt;a href="http://www.vmware.com/products/player/"&gt;Player&lt;/a&gt; on Windows and &lt;a href="http://www.vmware.com/products/fusion/"&gt;Fusion&lt;/a&gt; on the Mac. VMWare seems to have a slightly more superior product at the moment, but Microsoft’s virtualization offering is not too far behind, especially when you consider the new functionality available in Windows 7.&lt;/p&gt;  &lt;h2&gt;Source Control System&lt;/h2&gt;  &lt;p&gt;There are a few good choices out there for source control.&amp;#160; Personally, I like &lt;a href="http://subversion.tigris.org/"&gt;Subversion&lt;/a&gt; (SVN) the best.&amp;#160; It’s free (open source) and a lot of very useful third-party tools are out there.&amp;#160; &lt;a href="http://tortoisesvn.tigris.org/"&gt;TortoiseSVN&lt;/a&gt; is an excellent Windows GUI client and is free.&amp;#160; &lt;a href="http://ankhsvn.open.collab.net/"&gt;AnkhSVN&lt;/a&gt; (now being developed by Collabnet, the company that hosts the SVN source control itself) provides source control integration into Visual Studio and is also free.&amp;#160; Finally, there’s &lt;a href="http://www.visualsvn.com/"&gt;VisualSVN&lt;/a&gt;.&amp;#160; They make a free server application called &lt;a href="http://www.visualsvn.com/server/"&gt;VisualSVN Server&lt;/a&gt; that allows you to get a Subversion repository up and running in minutes on a Windows server, complete with HTTPS access over Apache.&amp;#160; VisualSVN also makes a Visual Studio &lt;a href="http://www.visualsvn.com/visualsvn/"&gt;source control add-in&lt;/a&gt; that’s comparable to AnkhSVN, although arguably more robust.&lt;/p&gt;  &lt;p&gt;The two solutions I came up with (Method 1 and Method 2 below) both use VMWare Workstation for the virtualization platform and Subversion for source control.&amp;#160; I’m sure one could adapt these steps fairly easily to work with other systems since the concepts behind them are what are important.&lt;/p&gt;  &lt;h2&gt;Method 1: Single VM with a Local Repository&lt;/h2&gt;  &lt;p&gt;When I first had the need to manage my own source control, I had a single VM I used to work on my code.&amp;#160; &lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_Qd3LFpzGA5A/SpIjL2aCTLI/AAAAAAAAAeE/ontuWPKEbPA/s1600-h/singlevm2.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="single-vm" border="0" alt="single-vm" src="http://lh5.ggpht.com/_Qd3LFpzGA5A/SpIjMT01S_I/AAAAAAAAAeI/QzzArI5wqtI/singlevm_thumb.png?imgmax=800" width="218" height="244" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;The source control solution in this case was quite simple since I could pretty much put everything on that VM.&lt;/p&gt;  &lt;h4&gt;Install a local repository&lt;/h4&gt;  &lt;p&gt;First I used TortoiseSVN to create a local file-based repository.&amp;#160; You can use the command-line Subversion tool to do this as well, but TortoiseSVN makes it easy:&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;a href="http://lh3.ggpht.com/_Qd3LFpzGA5A/SpIjMszyfmI/AAAAAAAAAeM/WNRJFeJUGa0/s1600-h/createlocalrepository2.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="create-local-repository" border="0" alt="create-local-repository" src="http://lh4.ggpht.com/_Qd3LFpzGA5A/SpIjNKh5V_I/AAAAAAAAAeQ/Pj9m3Vausdo/createlocalrepository_thumb.png?imgmax=800" width="225" height="244" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;I usually put the repository itself in a standard location (like C:\SVN) so the repository URL in my Working Copy becomes something like &lt;a href="file:///C:/SVN/my-repository"&gt;file:///C:/SVN/my-repository&lt;/a&gt;:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_Qd3LFpzGA5A/SpIjNdOajlI/AAAAAAAAAeU/adTiwPFRRpo/s1600-h/checkout2.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="checkout" border="0" alt="checkout" src="http://lh6.ggpht.com/_Qd3LFpzGA5A/SpIjNuNRjOI/AAAAAAAAAeY/y80bedA4uX0/checkout_thumb.png?imgmax=800" width="244" height="190" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;h4&gt;Configure repository backup&lt;/h4&gt;  &lt;p&gt;You can do this a number of ways, but you want to make sure that whatever is backing up the repository is doing it automatically on a scheduled basis, so you don’t have to think about it.&amp;#160; An online backup service (like &lt;a href="http://mozy.com"&gt;Mozy.com&lt;/a&gt;) works well, but there’s a monthly service charge.&amp;#160; I do all my backups on my wife’s computer, which is a MacBook.&amp;#160; Therefore I can use TimeMachine, which is integrated into Mac OS, to backup everything important, including my source code repository.&lt;/p&gt;  &lt;p&gt;However, my repository is still sitting in the file system of my VM which isn’t hosted on the MacBook – it’s on my laptop, which is running Windows.&amp;#160; To sync the repository files to the MacBook so they can get backed up I use &lt;a href="http://www.foldershare.com/"&gt;Windows Live Sync&lt;/a&gt; (used to be called FolderShare).&amp;#160; It’s a free file syncing tool from Microsoft that can sync between multiple PC’s and Mac’s via your local network or over the internet and even through firewalls.&amp;#160; All you need is a Windows Live ID to get started.&lt;/p&gt;  &lt;p&gt;So as long as all my source code is developed on a single VM, I can keep the source control repository locally on that VM as a simple file-based SVN repository and use Live Sync and TimeMachine to keep my repository files backed up.&amp;#160; Also, if I go mobile and/or the MacBook isn’t connected to the internet (so Live Sync can’t sync) I still have access to the repository so I can do all the source control operations I may need to keep developing code.&amp;#160; Then the next time my VM is able to connect the Mac, it will sync the repository files so they can be included in the next backup.&lt;/p&gt;  &lt;h2&gt;Method 2: Multiple VM’s with a Shared Repository&lt;/h2&gt;  &lt;p&gt;The above solution works great until you decide to write your code on more than one VM.&amp;#160; Perhaps you have a couple projects going that require different development stacks.&amp;#160; Maybe one is an ASP.NET application running on IIS6 (Windows Server 2003) and another is on IIS7 (Windows Server 2008).&amp;#160; In that case you really should develop your applications on separate VM’s, each running the correct version of Windows.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_Qd3LFpzGA5A/SpIjN8MeKoI/AAAAAAAAAec/pUQEQ2TmKSw/s1600-h/seconddevvm2.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="second-dev-vm" border="0" alt="second-dev-vm" src="http://lh6.ggpht.com/_Qd3LFpzGA5A/SpIjONG_VZI/AAAAAAAAAeg/i_bf9q1FFGA/seconddevvm_thumb.png?imgmax=800" width="218" height="244" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;Of course, now the problem is accessing the file-based source control repository across multiple VM’s.&amp;#160; One option is to move the SVN repository files to the host OS.&amp;#160; Most virtualization solutions like Microsoft Virtual PC and VMWare Workstation have a file sharing feature where a VM can access files on the host OS.&amp;#160; While this works, there’s a very noticeable performance hit.&amp;#160; The other drawback to putting the SVN repository on the host OS itself is that it’s mixing a part of your development environment infrastructure into the host, which conflicts with why you moved everything to virtual machines in the first place.&lt;/p&gt;  &lt;p&gt;A better approach is to move the repository to a dedicated VM that can act as a light-weight server and handle source control operation requests from all the client VM’s.&amp;#160; Subversion can be run as a server in this way.&amp;#160; It uses Apache to handle HTTP requests from clients and it seems perform nearly as well as with the local file-based repository.&lt;/p&gt;  &lt;p&gt;Let’s take a look at how to set all this up.&lt;/p&gt;  &lt;h4&gt;Create a source control server VM&lt;/h4&gt;  &lt;p&gt;The first step is to create this server VM which will host our shared repository and handle client SVN requests from the other VM’s.&amp;#160; This VM won’t require a lot of RAM since it’s primarily just going to serve up request for source control operations.&amp;#160; I created mine using Windows Server 2008, which has a minimum memory requirement of 512MB, so that’s what I went with.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_Qd3LFpzGA5A/SpIjOY9fP9I/AAAAAAAAAek/SCDQ4wrwKW8/s1600-h/servervm8.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="server-vm" border="0" alt="server-vm" src="http://lh4.ggpht.com/_Qd3LFpzGA5A/SpIjOnkfmEI/AAAAAAAAAeo/NN2soB5Y7H4/servervm_thumb2.png?imgmax=800" width="218" height="244" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;Before we move on to installing additional software on the source server VM, we need to take care of some necessary networking infrastructure.&lt;/p&gt;  &lt;h4&gt;Create a private virtual network to connect all the VM’s&lt;/h4&gt;  &lt;p&gt;One of the other advantages of most virtualization systems out there is the ability to create virtual networks.&amp;#160; If you have multiple VM’s that need to communicate with one another and especially if that communication needs to be private (or doesn’t need to occur over a public network), you can create a virtual network.&lt;/p&gt;  &lt;p&gt;In my situation, I ended up using one of the available custom networks installed by VMWare Workstation (vmnet2 in my case).&amp;#160; You don’t want to use the Host-Only, Bridge, or NAT networks as we need something that’s private that we can dedicate to these VM’s.&amp;#160; In my case, to turn this on, I just added a network adapter to all the VM’s, including the server VM, and connected it to the vmnet2 virtual network:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_Qd3LFpzGA5A/SpIjO167xXI/AAAAAAAAAes/qtJGpZ6wwxs/s1600-h/virtualnetwork2.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="virtual-network" border="0" alt="virtual-network" src="http://lh3.ggpht.com/_Qd3LFpzGA5A/SpIjPH6059I/AAAAAAAAAew/4wIjipRYWmM/virtualnetwork_thumb.png?imgmax=800" width="244" height="188" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;This new network adapter showed up in the OS (Windows) of all the VM’s.&amp;#160; To make them easier to manager, I renamed them from “Local Area Connection” and “Local Area Connection 2” to “Public LAN” and “Private LAN” in each VM:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_Qd3LFpzGA5A/SpIjPq2dhVI/AAAAAAAAAe0/d3DP1juFkmk/s1600-h/Windowsnetworkadapters2.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="Windows-network-adapters" border="0" alt="Windows-network-adapters" src="http://lh6.ggpht.com/_Qd3LFpzGA5A/SpIjP6jojuI/AAAAAAAAAe4/deGsM946kH8/Windowsnetworkadapters_thumb.png?imgmax=800" width="244" height="136" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;h4&gt;Set up DNS and DHCP services on the server VM&lt;/h4&gt;  &lt;p&gt;Simply connecting the VM’s together with our new private network connection doesn’t make it so they can communicate (at least not very well) over the private network.&amp;#160; It’s the equivalent to connecting a bunch of computers to a Ethernet hub or switch with network cables.&amp;#160; Each host will default to a 169.254.x.x address and name resolution won’t work at all if each OS has their default firewalls turned on.&lt;/p&gt;  &lt;p&gt;Ideally, we need some way of handing out IP’s and ideally having DNS name resolution to those IP’s (the most important one being the server).&amp;#160; To do that we first need to pick an IP subnet and a static IP for the server VM.&amp;#160; In my case I went with 192.168.76.x for the subnet and 192.168.76.1 for the static IP.&amp;#160; I then set the IP address of the “Private LAN” network adapter on the server VM to that static IP, which is done in Windows itself:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_Qd3LFpzGA5A/SpIjQKcF6YI/AAAAAAAAAe8/ypZaqm8ctNo/s1600-h/staticipconfig2.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="static-ip-config" border="0" alt="static-ip-config" src="http://lh3.ggpht.com/_Qd3LFpzGA5A/SpIjQTFvNTI/AAAAAAAAAfA/jbcDhg1G9xg/staticipconfig_thumb.png?imgmax=800" width="244" height="210" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;Next we need to turn on DNS services on the server VM so it can resolve DNS names local to our virtual network.&amp;#160; After installing the service, I created a forward lookup zone for a private DNS domain called “ts-local”.&amp;#160; You can use whatever private DNS domain name you want as long as it isn’t something that might conflict with domain names on the internet or any other network you may connected to.&lt;/p&gt;  &lt;p&gt;Then I added an A-host for the server VM itself whose hostname is “MCP”:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_Qd3LFpzGA5A/SpIjQtFbyTI/AAAAAAAAAfE/Uao-Es4MEZc/s1600-h/dnsserver2.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="dns-server" border="0" alt="dns-server" src="http://lh3.ggpht.com/_Qd3LFpzGA5A/SpIjQ4y1ekI/AAAAAAAAAfI/razOOC3_LBE/dnsserver_thumb.png?imgmax=800" width="244" height="125" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;Next we need to enabled the DHCP server service on the server VM so we can hand out the IP’s in our subnet.&amp;#160; Once you install the service, configure it to hand out IP’s to our subnet as well as specify the DNS server IP (same as the server VM) and the domain name (in my case “ts-local”):&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_Qd3LFpzGA5A/SpIjRK8Q8DI/AAAAAAAAAfM/OjsbTCIJ1jw/s1600-h/dhcpserver8.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="dhcp-server" border="0" alt="dhcp-server" src="http://lh3.ggpht.com/_Qd3LFpzGA5A/SpIjRbKXKmI/AAAAAAAAAfQ/1x6ngkKuY4A/dhcpserver_thumb2.png?imgmax=800" width="244" height="113" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Finally, we should be able to release and renew the IP address on each of the client VM’s and get an IP in our subnet.&amp;#160; We should also be able to resolve the IP address of the server VM from the client VM’s using PING:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_Qd3LFpzGA5A/SpIjRrvzKFI/AAAAAAAAAfU/1Lvb4y-IfiA/s1600-h/ping8.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="ping" border="0" alt="ping" src="http://lh5.ggpht.com/_Qd3LFpzGA5A/SpIjR3Aj4pI/AAAAAAAAAfY/Rv7FuX7BZEs/ping_thumb2.png?imgmax=800" width="211" height="244" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;h4&gt;Install VisualSVN Server on the server VM&lt;/h4&gt;  &lt;p&gt;Now for the real reason we created this server VM in the first place.&amp;#160; Setting up a Subversion server that uses Apache (the web server that SVN uses) by hand is no easy task (trust me, I’ve done it).&amp;#160; VisualSVN Server makes this a snap.&amp;#160; It will install Apache, configure HTTPS, and create a location for the repositories.&amp;#160; By default, VisualSVN Server creates the repositories directory at C:\Repositories.&amp;#160; I prefer C:\SVN.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_Qd3LFpzGA5A/SpIjR_We9iI/AAAAAAAAAfc/hS4lD4Qg4dI/s1600-h/visualsvnservermanager5.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="visualsvn-server-manager" border="0" alt="visualsvn-server-manager" src="http://lh5.ggpht.com/_Qd3LFpzGA5A/SpIjSULt_BI/AAAAAAAAAfg/EhwFOtbkwaM/visualsvnservermanager_thumb1.png?imgmax=800" width="244" height="210" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;After the install is complete, all you have to do is create a username for your client VM’s to use to access the repositories.&lt;/p&gt;  &lt;h4&gt;Move the repository&lt;/h4&gt;  &lt;p&gt;If you started with Method 1 like I did, you need to move the file-based repository from the single VM to our shiny new Subversion server VM.&amp;#160; VisualSVN Server makes this easy with it’s import option:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_Qd3LFpzGA5A/SpIjSg4cqnI/AAAAAAAAAfk/A0ntuQGHWT8/s1600-h/importrepository2.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="import-repository" border="0" alt="import-repository" src="http://lh6.ggpht.com/_Qd3LFpzGA5A/SpIjS1tEbYI/AAAAAAAAAfo/zozreoaQoYA/importrepository_thumb.png?imgmax=800" width="244" height="210" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;Simply browse to where the repository files are and perform the import.&amp;#160; You may want to copy them over to a temporary directory on the server VM first to make them easily accessible from the server’s file system.&lt;/p&gt;  &lt;p&gt;Once the repository has been imported, you’ll want to set assign the user account you created earlier to have read/write access to the repository:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_Qd3LFpzGA5A/SpIjTH6zhiI/AAAAAAAAAfs/tqi9lEKmxag/s1600-h/asignuser2.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="asign-user" border="0" alt="asign-user" src="http://lh4.ggpht.com/_Qd3LFpzGA5A/SpIjTSngLWI/AAAAAAAAAfw/g3Ac8mL2S5M/asignuser_thumb.png?imgmax=800" width="244" height="216" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;Your server is now ready to service source control requests!&lt;/p&gt;  &lt;h4&gt;Point the Working Copies to the new repository&lt;/h4&gt;  &lt;p&gt;If you already have Working Copies (the term Subversion uses for directories that are under source control) on your original development VM, you can point them at the new URL using TortoiseSVN’s Relocate function.&amp;#160; For example, if you had a working copy connected to &lt;a href="file:///C:/SVN/my-repository"&gt;file:///C:/SVN/my-repository&lt;/a&gt;, you can now point it to &lt;a href="http://mcp.ts-local/SVN/my-repository"&gt;http://mcp.ts-local/SVN/my-repository&lt;/a&gt;:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_Qd3LFpzGA5A/SpIjTvNpAYI/AAAAAAAAAf0/TZiGyPBZ0XE/s1600-h/relocate12.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="relocate-1" border="0" alt="relocate-1" src="http://lh4.ggpht.com/_Qd3LFpzGA5A/SpIjUmwnaAI/AAAAAAAAAf4/ksj0AiPvNGk/relocate1_thumb.png?imgmax=800" width="241" height="244" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_Qd3LFpzGA5A/SpIjUlLvSDI/AAAAAAAAAf8/3HKG9wXdoWA/s1600-h/relocate22.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="relocate-2" border="0" alt="relocate-2" src="http://lh6.ggpht.com/_Qd3LFpzGA5A/SpIjVuTFqtI/AAAAAAAAAgA/kR6jfsENnuI/relocate2_thumb.png?imgmax=800" width="244" height="94" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;h4&gt;Configure repository backup&lt;/h4&gt;  &lt;p&gt;The final step to getting the multiple-VM source code repository operational is to configure the backup.&amp;#160; The approach is the same as with the single-VM approach – you just have to back up the repository files on the server VM instead.&amp;#160; In my case, that directory is C:\SVN.&amp;#160; I point Live Sync there which syncs the files to my MacBook which in turn backs everything up via TimeMachine.&amp;#160; Very nice!&lt;/p&gt;  &lt;p&gt;The multi-VM solution also has the same disconnected benefits that the single-VM solution does.&amp;#160; You can perform all your source code operations anytime you want, even if your host machine is disconnected from the network.&amp;#160; The next time you are connected, Live Sync will sync any repository file changes to the MacBook so they can be backed up.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/35759427-6201114548959120449?l=beyondthispoint.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://beyondthispoint.blogspot.com/feeds/6201114548959120449/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=35759427&amp;postID=6201114548959120449' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/35759427/posts/default/6201114548959120449'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/35759427/posts/default/6201114548959120449'/><link rel='alternate' type='text/html' href='http://beyondthispoint.blogspot.com/2009/08/using-mobile-source-code-repository.html' title='Using a Mobile Source Code Repository'/><author><name>Pete</name><uri>http://www.blogger.com/profile/16688548320976910930</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://photos1.blogger.com/blogger2/1347/4365/240/gse_multipart43909.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh3.ggpht.com/_Qd3LFpzGA5A/SpIjLj6evHI/AAAAAAAAAeA/5PQLkF2mzfI/s72-c/corona_laptop1.jpg?imgmax=800' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-35759427.post-4764883654522928107</id><published>2009-07-25T08:56:00.006-05:00</published><updated>2009-07-25T23:12:54.181-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Technology'/><category scheme='http://www.blogger.com/atom/ns#' term='iPhone'/><title type='text'>Turn off Push with a Jailbroken iPhone 2G on T-Mobile with no Data Plan</title><content type='html'>So this tip probably applies to about 3 people out there besides me, but the road to a solution was about three days of pain, so I feel compelled to post something.&lt;br /&gt;&lt;br /&gt;I've been a happy iPhone user since the beginning of 2009 when my manager at work sold me his 2G.  A buddy of mine showed me the ropes of jailbreaking and unlocking the iPhone and before I knew it I was running iPhone OS 2.2.1 via QuickPwn on the T-Mobile network.  Better yet, I didn't have to have a data plan (yes, I'm cheap), like you do with AT&amp;amp;T.  I just hopped on an available WiFi network whenever I needed to use data - life was good.&lt;br /&gt;&lt;br /&gt;Until I decided it would be a fun idea to upgrade my iPhone to the new 3.0 OS.&lt;br /&gt;&lt;br /&gt;The actual install itself (I used redsn0w) was quite painless and relatively easy.  After I got all my apps re-installed and my settings back to what I had before, I had a sweet phone that couldn't receive incoming calls or text messages.  After several re-installs of the OS, attempting to use trial and error to narrow down the source of the problem, I finally stumbled upon the answer.  And, yes, I tried googling the heck out of the internet and could not find another poor soul out there who was in my same predicament (hence the first sentence of this post).&lt;br /&gt;&lt;br /&gt;The source of the problem was that I had Push turned on (which is the default when you install OS 3.0).  Push is a technology that allows applications like email and calendar to receive updates from the server without having to continuously poll the sever. Polling like this is also called Pull, the opposite of Push.  Push requires a data plan to work. I don't have a data plan with T-Mobile. I guess Push doesn't behave well without one. Kinda makes sense since I'm sure the iPhone developers didn't really think about this scenario since I think you're required to have a data plan with your iPhone if you're with AT&amp;amp;T.&lt;br /&gt;   &lt;br /&gt;Anyway, to turn Push off, here's where you go in the iPhone settings:&lt;br /&gt;&lt;br /&gt;Settings &gt; Mail, Contacts, Calendars &gt; Fetch New Data &gt; Push &gt; Off&lt;br /&gt;&lt;br /&gt;After I did that, everything works!  And I have to say the 3.0 version of the iPhone OS is a nice upgrade, even on a 2G.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/35759427-4764883654522928107?l=beyondthispoint.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://beyondthispoint.blogspot.com/feeds/4764883654522928107/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=35759427&amp;postID=4764883654522928107' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/35759427/posts/default/4764883654522928107'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/35759427/posts/default/4764883654522928107'/><link rel='alternate' type='text/html' href='http://beyondthispoint.blogspot.com/2009/07/turn-of-push-with-jailbroken-iphone-2g.html' title='Turn off Push with a Jailbroken iPhone 2G on T-Mobile with no Data Plan'/><author><name>Pete</name><uri>http://www.blogger.com/profile/16688548320976910930</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://photos1.blogger.com/blogger2/1347/4365/240/gse_multipart43909.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-35759427.post-4666150613326106702</id><published>2009-02-18T00:17:00.001-06:00</published><updated>2009-02-18T00:21:26.358-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Software'/><title type='text'>Using the Repository Pattern with CSLA.NET, Part 2</title><content type='html'>&lt;p&gt;In my &lt;a href="http://beyondthispoint.blogspot.com/2009/01/using-repository-pattern-with-cslanet.html"&gt;last post&lt;/a&gt;, we built an abstraction for a data access layer used by CSLA.NET business objects based on the &lt;a href="http://martinfowler.com/eaaCatalog/repository.html"&gt;Repository Pattern&lt;/a&gt;.&amp;#160; We started by creating two base interfaces, &lt;strong&gt;IRepository&amp;lt;TContext&amp;gt;&lt;/strong&gt; and &lt;strong&gt;IContext&lt;/strong&gt; and then used them to define a set interfaces that abstracted the data access for a simple order entry system.&amp;#160; Those interfaces were:&lt;/p&gt;&lt;ul&gt;   &lt;li&gt;&lt;strong&gt;IOrderRepository&lt;/strong&gt; - represented the repository for orders and line items; a factory object that can create the other repository objects &lt;/li&gt;&lt;li&gt;&lt;strong&gt;IOrderContext&lt;/strong&gt; - the context that the &lt;strong&gt;IOrderRepository&lt;/strong&gt; creates that performs the actual data access &lt;/li&gt;&lt;li&gt;&lt;strong&gt;IOrderInfoDto&lt;/strong&gt; - a DTO for order summary entities &lt;/li&gt;&lt;li&gt;&lt;strong&gt;IOrderDto&lt;/strong&gt; - a DTO for orders &lt;/li&gt;&lt;li&gt;&lt;strong&gt;ILineItemDto&lt;/strong&gt; - a DTO for order line items &lt;/li&gt; &lt;/ul&gt;&lt;p&gt;We then demonstrated how objects based on those interfaces would be called from the data access areas within actual CSLA.NET business objects (in the DataPortal_* methods).&amp;#160; We also showed how the repository object was injected in via dependency injection.&amp;#160; In our case, we used the &lt;font color="#000000"&gt;&lt;a href="http://msdn.microsoft.com/en-us/library/dd203101.aspx"&gt;Unity&lt;/a&gt;&lt;/font&gt; IOC Container but any IOC Container or dependency injection framework would do.&lt;/p&gt;&lt;p&gt;In this post we're going to build a &lt;em&gt;concrete &lt;/em&gt;implementation of our abstract data access layer.&amp;#160; In other words, we'll build classes that implement the above interfaces.&amp;#160; In this case, we'll use LINQ to SQL as our actual data access technology.&amp;#160; However, given the high level of abstraction of our repository pattern implementation, we could conceivable build concrete implementations in many other technologies such as LINQ to Entities, &amp;lt;insert your favorite ORM here&amp;gt;, or even plain old ADO.NET.&lt;/p&gt;&lt;h2&gt;LINQ to SQL Classes&lt;/h2&gt;&lt;p&gt;Before we jump into the implementation of our interfaces, we need to first build our LINQ to SQL classes.&amp;#160; These classes will play an integral role in our concrete implementation.&lt;/p&gt;&lt;p&gt;LINQ to SQL requires that a physical database exist, so assume that we've started with a simple SQL Server database that contains an &lt;strong&gt;Orders&lt;/strong&gt; table and a &lt;strong&gt;LineItem&lt;/strong&gt; table with a one-to-many relationship between them.&amp;#160; We can then add an &lt;strong&gt;Orders.dbml &lt;/strong&gt;file (LINQ to SQL Classes item) to a Visual Studio 2008 project and drag both tables onto the design surface:&lt;/p&gt;&lt;p&gt;&lt;a href="http://lh4.ggpht.com/_Qd3LFpzGA5A/SZun6cgHh8I/AAAAAAAAAbk/g_ZRZuv6g2k/s1600-h/Ordersdbmlscreenshot4.png"&gt;&lt;img style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="330" alt="Orders-dbml-screenshot" src="http://lh3.ggpht.com/_Qd3LFpzGA5A/SZun7IN_tiI/AAAAAAAAAbo/1tWftJn0BSg/Ordersdbmlscreenshot_thumb2.png?imgmax=800" width="416" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;&lt;p&gt;Visual Studio (actually LINQ to SQL) does a lot of work behind the scenes when we do this.&amp;#160; It code generates a LINQ to SQL &lt;a href="http://msdn.microsoft.com/en-us/library/system.data.linq.datacontext.aspx"&gt;DataContext&lt;/a&gt; class called &lt;strong&gt;OrdersDataContext&lt;/strong&gt; and two entity classes called &lt;strong&gt;Order &lt;/strong&gt;and &lt;strong&gt;LineItem&lt;/strong&gt; that map to the database tables.&lt;/p&gt;&lt;p&gt;You may also notice is the screen shot above that we've added a set of stored procedures to our database that perform all the inserts, updates, and deletes, and they show up as methods on the &lt;strong&gt;OrdersDataContext&lt;/strong&gt; class.&amp;#160; While LINQ to SQL entity classes have the ability to perform these operations on their own, they have a limitation that forces us to use a alternative mechanism, like stored procedures, instead.&amp;#160; We'll investigate this issue further a little later on in the post.&lt;/p&gt;&lt;p&gt;At this point, the &lt;strong&gt;OrdersDataContext&lt;/strong&gt;, &lt;strong&gt;Order&lt;/strong&gt;, and &lt;strong&gt;LineItem&lt;/strong&gt; classes could be used by a set of CSLA.NET business objects to perform all the required data access.&amp;#160; However, the business objects would be be tightly coupled to the LINQ to SQL code and therefore there would be no easy way to abstract the LINQ to SQL code so it could be mocked for unit testing purposes.&amp;#160; Let's see how we can migrate this code into a concrete implementation of our repository pattern abstraction.&lt;/p&gt;&lt;h2&gt;Concrete DTO's&lt;/h2&gt;&lt;p&gt;Before we jump into the concrete repository and context objects, let's take a quick look at how we implement the DTO's.&amp;#160; LINQ to SQL makes this relatively easy since the entity classes code generated by LINQ to SQL &lt;em&gt;are &lt;/em&gt;the DTO's.&amp;#160; All we need to do is make them implement our DTO interfaces.&amp;#160; Since the LINQ to SQL code generation is done using partial classes, this is quite easy.&lt;/p&gt;&lt;p&gt;First the, concrete &lt;strong&gt;IOrderDto&lt;/strong&gt; class:&lt;/p&gt;&lt;pre class="c#" name="code"&gt;partial class Order&lt;br /&gt;    : IOrderDto&lt;br /&gt;{&lt;br /&gt;&lt;br /&gt;    byte[] IOrderDto.Timestamp&lt;br /&gt;    {&lt;br /&gt;        get&lt;br /&gt;        {&lt;br /&gt;            return this.Timestamp.ToArray();&lt;br /&gt;        }&lt;br /&gt;        set&lt;br /&gt;        {&lt;br /&gt;            this.Timestamp = new Binary(value);&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    IEnumerable&amp;lt;ILineItemDto&amp;gt; IOrderDto.LineItems&lt;br /&gt;    {&lt;br /&gt;        get&lt;br /&gt;        {&lt;br /&gt;            return this.LineItems.Cast&amp;lt;ILineItemDto&amp;gt;();&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;}&lt;/pre&gt;&lt;p&gt;At a minimum, we need to define the partial class &lt;strong&gt;Order&lt;/strong&gt; (which binds to the code-generated &lt;strong&gt;Order&lt;/strong&gt; class at compile time) that implements the &lt;strong&gt;IOrderDto&lt;/strong&gt; interface.&amp;#160; But we also need to add a couple explicit &lt;strong&gt;IOrderDto&lt;/strong&gt; property implementations.&lt;/p&gt;&lt;p&gt;The first is due to the fact that the &lt;strong&gt;Order&lt;/strong&gt; class that was code-generated by LINQ to SQL has a &lt;strong&gt;Timestamp&lt;/strong&gt; property that is of the LINQ to SQL-specific type &lt;font color="#000000"&gt;&lt;a href="http://msdn.microsoft.com/en-us/library/system.data.linq.binary.aspx"&gt;Binary&lt;/a&gt;&lt;/font&gt;.&amp;#160; However, the &lt;strong&gt;IOrderDto&lt;/strong&gt; interface defines the &lt;strong&gt;Timestamp&lt;/strong&gt; property as a byte array, which is not specific to a data access technology.&amp;#160; Therefore we need to add the &lt;strong&gt;IOrderDto.Timestamp&lt;/strong&gt; property explicitly and marshal the &lt;strong&gt;Binary&lt;/strong&gt; and byte array values back and forth.&lt;/p&gt;&lt;p&gt;The second explicit property implementation is &lt;strong&gt;IOrderDto.LineItems&lt;/strong&gt;.&amp;#160; The &lt;strong&gt;Order&lt;/strong&gt; class code-generated by LINQ to SQL also defines a &lt;strong&gt;LineItems&lt;/strong&gt; property, but it's of type &lt;font color="#000000"&gt;&lt;a href="http://msdn.microsoft.com/en-us/library/system.data.linq.binary.aspx"&gt;EntitySet&lt;/a&gt;&lt;/font&gt;&lt;strong&gt;&amp;lt;LineItem&amp;gt;&lt;/strong&gt;.&amp;#160; Therefore, we need to convert between the two and a handy way to do it is to use the &lt;font color="#000000"&gt;&lt;a href="http://msdn.microsoft.com/en-us/library/bb341406.aspx"&gt;Cast&lt;/a&gt;&lt;/font&gt; extension method.&lt;/p&gt;&lt;p&gt;The concrete &lt;strong&gt;ILineItemDto&lt;/strong&gt; class is very similar, but we only have to add an explicit implementation of its the &lt;strong&gt;ILineItemDto.Property&lt;/strong&gt;:&lt;/p&gt;&lt;pre class="c#" name="code"&gt;partial class LineItem&lt;br /&gt;    : ILineItemDto&lt;br /&gt;{&lt;br /&gt;&lt;br /&gt;    byte[] ILineItemDto.Timestamp&lt;br /&gt;    {&lt;br /&gt;        get&lt;br /&gt;        {&lt;br /&gt;            return this.Timestamp.ToArray();&lt;br /&gt;        }&lt;br /&gt;        set&lt;br /&gt;        {&lt;br /&gt;            this.Timestamp = new Binary(value);&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;}&lt;/pre&gt;&lt;p&gt;Now, with the concrete DTO's defined, let's move onto the concrete repository and context classes.&lt;/p&gt;&lt;h2&gt;Concrete Order Repository&lt;/h2&gt;&lt;p&gt;You may recall the role of the repository object is to be a factory for all of the other objects needed by the data access layer.&amp;#160; It's main job is to create the associated context.&amp;#160; Therefore our order repository will need to create an order context.&amp;#160; It will also need to be able to create any DTO's required by data access methods that require DTO's as inputs.&amp;#160; &lt;/p&gt;&lt;p&gt;Given that, here's our concrete &lt;strong&gt;OrderRepository&lt;/strong&gt; class:&lt;/p&gt;&lt;pre class="c#" name="code"&gt;public sealed class OrderRepository&lt;br /&gt;    : IOrderRepository&lt;br /&gt;{&lt;br /&gt;&lt;br /&gt;    IOrderContext IRepository&amp;lt;IOrderContext&amp;gt;.CreateContext(bool isTransactional)&lt;br /&gt;    {&lt;br /&gt;        return new OrderContext(isTransactional);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    IOrderDto IOrderRepository.CreateOrderDto()&lt;br /&gt;    {&lt;br /&gt;        return new Order();&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    ILineItemDto IOrderRepository.CreateLineItemDto()&lt;br /&gt;    {&lt;br /&gt;        return new LineItem();&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;}&lt;/pre&gt;&lt;p&gt;A fairly simple implementation of a factory.&amp;#160; The &lt;strong&gt;CreateContext&lt;/strong&gt; method creates a new instance of our concrete &lt;strong&gt;OrderContext &lt;/strong&gt;(which we'll see its implementation just ahead), passing a value to its constructor, telling it whether or not it needs to be transactional.&amp;#160; Then we have two methods for creating DTO's: &lt;strong&gt;CreateOrderDto&lt;/strong&gt; and &lt;strong&gt;CreateLineItemDto&lt;/strong&gt;.&amp;#160; Notice that what we're actually returning are instances of the two entity classes code generated by LINQ to SQL since they implement the required DTO interfaces.&lt;/p&gt;&lt;h2&gt;Concrete Order Context&lt;/h2&gt;&lt;p&gt;While the repository object is the factory that creates all the data access objects, the context object plays the star role in actually performing the data access operations.&amp;#160; Therefore, the &lt;strong&gt;OrderContext&lt;/strong&gt; class is going to have the most meat of any of our concrete repository pattern classes.&amp;#160; Let's examine the &lt;strong&gt;OrderContext&lt;/strong&gt; class in chunks since there's a lot going on.&amp;#160; &lt;/p&gt;&lt;h4&gt;Basic Implementation of OrderContext &lt;/h4&gt;&lt;p&gt;First, let's take a look at the class definition itself and its constructor that we know takes a &lt;strong&gt;isTransactional&lt;/strong&gt; boolean parameter:&lt;/p&gt;&lt;pre class="c#" name="code"&gt;public sealed class OrderContext&lt;br /&gt;    : IOrderContext&lt;br /&gt;{&lt;br /&gt;&lt;br /&gt;    private OrdersDataContext _db;&lt;br /&gt;    private TransactionScope _ts;&lt;br /&gt;&lt;br /&gt;    public OrderContext(bool isTransactional)&lt;br /&gt;    {&lt;br /&gt;        _db = new OrdersDataContext();&lt;br /&gt;        if (isTransactional)&lt;br /&gt;            _ts = new TransactionScope();&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;}&lt;/pre&gt;&lt;p&gt;As you can see, our &lt;strong&gt;OrderContext&lt;/strong&gt; object wraps an instance of an &lt;strong&gt;OrdersDataContext&lt;/strong&gt; object (via the &lt;strong&gt;_db&lt;/strong&gt; field) which is a LINQ to SQL &lt;a href="http://msdn.microsoft.com/en-us/library/system.data.linq.datacontext.aspx"&gt;&lt;strong&gt;DataContext&lt;/strong&gt;&lt;/a&gt;.&amp;#160; Therefore, our &lt;strong&gt;OrderContext&lt;/strong&gt; object is essentially an abstraction of a LINQ to SQL data context.&amp;#160; When it implements the remaining &lt;strong&gt;IOrderContext &lt;/strong&gt;interface members, it does this by making calls against that LINQ to SQL &lt;a href="http://msdn.microsoft.com/en-us/library/system.data.linq.datacontext.aspx"&gt;&lt;strong&gt;DataContext&lt;/strong&gt;&lt;/a&gt; instance.&lt;/p&gt;&lt;p&gt;The &lt;strong&gt;OrderContext &lt;/strong&gt;also wraps a &lt;strong&gt;&lt;a href="http://msdn.microsoft.com/en-us/library/system.transactions.transactionscope.aspx"&gt;TransactionScope&lt;/a&gt;&lt;/strong&gt; object, which it only creates if the calling &lt;strong&gt;OrderRepository&lt;/strong&gt; object specified that the context is transactional.&amp;#160; That transaction is committed in the &lt;strong&gt;CompleteTransaction&lt;/strong&gt; method, which is required by the &lt;strong&gt;IContext &lt;/strong&gt;base interface:&lt;/p&gt;&lt;pre class="c#" name="code"&gt;    void IContext.CompleteTransaction()&lt;br /&gt;    {&lt;br /&gt;        if (_ts != null)&lt;br /&gt;            _ts.Complete();&lt;br /&gt;    }&lt;/pre&gt;&lt;p&gt;The last place we interact with this transaction is at the end of the order context object's lifecycle during the &lt;strong&gt;IDispose.Dispose&lt;/strong&gt; implementation:&lt;/p&gt;&lt;pre class="c#" name="code"&gt;    void IDisposable.Dispose()&lt;br /&gt;    {&lt;br /&gt;        if (_ts != null)&lt;br /&gt;            _ts.Dispose();&lt;br /&gt;        _db.Dispose();&lt;br /&gt;    }&lt;/pre&gt;&lt;p&gt;We also dispose the &lt;strong&gt;OrdersDataContext&lt;/strong&gt; which closes up the database connection.&lt;/p&gt;&lt;p&gt;So far with the &lt;strong&gt;OrderContext &lt;/strong&gt;class we've implemented the creation and clean-up of the object.&amp;#160; Now we need to implement the methods defined by the &lt;strong&gt;IOrderContext&lt;/strong&gt; interface that actually do the data access.&amp;#160; &lt;/p&gt;&lt;h4&gt;IOrderContext.FetchInfoList Implementation&lt;/h4&gt;&lt;p&gt;First, let's take a look at the implementation of the &lt;strong&gt;IOrderContext&lt;/strong&gt; interface's &lt;strong&gt;FetchInfoList&lt;/strong&gt; method:&lt;/p&gt;&lt;pre class="c#" name="code"&gt;    IEnumerable&amp;lt;IOrderInfoDto&amp;gt; IOrderContext.FetchInfoList()&lt;br /&gt;    {&lt;br /&gt;        var query =&lt;br /&gt;            from o in _db.Orders&lt;br /&gt;            orderby o.Date&lt;br /&gt;            select new OrderInfoData&lt;br /&gt;            {&lt;br /&gt;                Id = o.Id,&lt;br /&gt;                Customer = o.Customer,&lt;br /&gt;                Date = o.Date&lt;br /&gt;            };&lt;br /&gt;        return query.Cast&amp;lt;IOrderInfoDto&amp;gt;();&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    private class OrderInfoData&lt;br /&gt;        : IOrderInfoDto&lt;br /&gt;    {&lt;br /&gt;        public int Id { get; set; }&lt;br /&gt;        public string Customer { get; set; }&lt;br /&gt;        public DateTime Date { get; set; }&lt;br /&gt;    }&lt;/pre&gt;&lt;p&gt;The purpose of this method is to return a list of all the orders in the database.&amp;#160; They come back as the light-weight &lt;strong&gt;IOrderInfoDto&lt;/strong&gt; objects.&amp;#160; Our implementation of this method performs a standard LINQ to SQL query against the &lt;strong&gt;Order &lt;/strong&gt;entity objects in the &lt;strong&gt;OrdersDataContext&lt;/strong&gt;.&amp;#160; However, we don't want to return all the data of each order.&amp;#160; The &lt;strong&gt;IOrderInfoDto &lt;/strong&gt;object is only a subset of that data.&amp;#160; An easy solution is to perform a LINQ &lt;em&gt;projection &lt;/em&gt;of &lt;strong&gt;Order&lt;/strong&gt; objects to &lt;strong&gt;IOrderInfoDto &lt;/strong&gt;objects.&amp;#160; This will generate only the T-SQL necessary to populate the data required by the &lt;strong&gt;IOrderInfoDto &lt;/strong&gt;objects.&amp;#160; And of course we need a concrete &lt;strong&gt;IOrderInfoDto &lt;/strong&gt;class to create and return; an easy approach is just to declare a private &lt;strong&gt;OrderInfoData&lt;/strong&gt; class shown above just below the &lt;strong&gt;FetchInfoList&lt;/strong&gt; method.&lt;/p&gt;&lt;h4&gt;IOrderContext.FetchSingleWithLineItems Implementation&lt;/h4&gt;&lt;p&gt;The next data access method required by the &lt;strong&gt;IOrderContext&lt;/strong&gt; interface is &lt;strong&gt;FetchSingleWithLineItems:&lt;/strong&gt;&lt;/p&gt;&lt;pre class="c#" name="code"&gt;    IOrderDto IOrderContext.FetchSingleWithLineItems(int id)&lt;br /&gt;    {&lt;br /&gt;        var options = new DataLoadOptions();&lt;br /&gt;        options.LoadWith&amp;lt;Order&amp;gt;(o =&amp;gt; o.LineItems);&lt;br /&gt;        _db.LoadOptions = options;&lt;br /&gt;        var query =&lt;br /&gt;            from o in _db.Orders&lt;br /&gt;            where o.Id == id&lt;br /&gt;            select o;&lt;br /&gt;        return query.Single();&lt;br /&gt;    }&lt;/pre&gt;&lt;p&gt;This method is similar to the previous in that it is another LINQ to SQL query.&amp;#160; However, it returns a single DTO (instead of a collection) and that DTO is an instance of the full &lt;strong&gt;Order&lt;/strong&gt; entity class, which happens to implement the &lt;strong&gt;IOrderDto &lt;/strong&gt;interface.&lt;/p&gt;&lt;p&gt;But we don't want to return &lt;em&gt;just&lt;/em&gt; the data of the order itself.&amp;#160; We also want to return all of it's child line item data as well (which will be accessible via the &lt;strong&gt;LineItems &lt;/strong&gt;property), and preferably all with &lt;em&gt;one&lt;/em&gt; call to the database.&amp;#160; We can do this with a little LINQ to SQL magic by configuring the &lt;font color="#000000"&gt;&lt;a href="http://msdn.microsoft.com/en-us/library/bb341406.aspx"&gt;LoadOptions&lt;/a&gt;&lt;/font&gt; property of the &lt;strong&gt;OrdersDataContext&lt;/strong&gt;, telling it that when it loads the data of an &lt;strong&gt;Order &lt;/strong&gt;object, go ahead and load its child &lt;strong&gt;LineItem &lt;/strong&gt;objects contained within the &lt;strong&gt;LineItems &lt;/strong&gt;property.&lt;/p&gt;&lt;p&gt;The &lt;strong&gt;FetchInfoList&lt;/strong&gt; and &lt;strong&gt;FetchSingleWithLineItems&lt;/strong&gt; methods pretty much take care of all the data access querying.&amp;#160; Now we need to implement the insert, update, and delete operations.&amp;#160; &lt;/p&gt;&lt;h4&gt;Insert, Update, and Delete Method Implementations&lt;/h4&gt;&lt;p&gt;While the query method implementations simply took advantage of the built-in LINQ capabilities of the entity classes, we can't quite do the same with the insert, update, and delete methods.&amp;#160; Normally, with LINQ to SQL you can make whatever state changes you want to those objects and when you're ready to persist those changes back to the database, you just call the &lt;font color="#000000"&gt;&lt;a href="http://msdn.microsoft.com/en-us/library/bb341406.aspx"&gt;SubmitChanges&lt;/a&gt;&lt;/font&gt; method on the &lt;a href="http://msdn.microsoft.com/en-us/library/system.data.linq.datacontext.aspx"&gt;DataContext&lt;/a&gt; object, which keeps track of which entity objects have changed.&amp;#160; &lt;/p&gt;&lt;p&gt;While this approach could work in our situation, it requires us to maintain references to all of these objects between data access operations performed by our CSLA.NET business objects and this is a problem.&amp;#160; All CSLA.NET business objects need to be serializable so they can function properly in the CSLA.NET DataPortal.&amp;#160; This means that any objects contained within a CSLA.NET business object must also be serializable.&amp;#160; Unfortunately, LINQ to SQL objects are not.&lt;/p&gt;&lt;p&gt;There are some potential workarounds like one where you create a new LINQ to SQL entity object, load it with data, and then attach it to a &lt;strong&gt;DataContext&lt;/strong&gt; as if it were previously fetched by the &lt;strong&gt;DataContext&lt;/strong&gt;, but this is not using LINQ to SQL as it was intended and in many cases produces unexpected behavior.&amp;#160; The only reliable solution is to use a different mechanism than the LINQ to SQL entity objects and the &lt;strong&gt;DataContext&lt;/strong&gt; to persist changes back to the database.&amp;#160; One of the easiest is the use of stored procedures.&amp;#160; This, in fact, is the same approach that Rocky uses with his LINQ to SQL data access code in his sample &lt;strong&gt;ProjectTracker&lt;/strong&gt; application.&amp;#160; In the &lt;strong&gt;Orders.dbml&lt;/strong&gt; file in our project, we simply drag those stored procedures over from the database and LINQ to SQL adds them as methods to the &lt;strong&gt;OrdersDataContext&lt;/strong&gt;.&lt;/p&gt;&lt;p&gt;The last thing we should mention about the insert, update, and delete methods is that, unlike the query methods which returned DTO's (or collections of them), these methods take DTO's as parameters (except for delete which typically only takes an ID).&amp;#160; Therefore, the caller (in this case the CSLA.NET business object) needs to be able to create an empty DTO and populate it.&amp;#160; Which is why the &lt;strong&gt;OrderRepository&lt;/strong&gt; class has the DTO creation methods.&amp;#160; &lt;/p&gt;&lt;p&gt;So, without further delay, here are the insert, update, and delete method implementations for the order entities:&lt;/p&gt;&lt;pre class="c#" name="code"&gt;    void IOrderContext.InsertOrder(IOrderDto newOrder)&lt;br /&gt;    {&lt;br /&gt;        int? id = null;&lt;br /&gt;        Binary timestamp = null;&lt;br /&gt;        _db.insert_order(&lt;br /&gt;            ref id,&lt;br /&gt;            newOrder.Customer,&lt;br /&gt;            newOrder.Date,&lt;br /&gt;            newOrder.ShippingCost, &lt;br /&gt;            ref timestamp);&lt;br /&gt;        newOrder.Id = id.Value;&lt;br /&gt;        newOrder.Timestamp = timestamp.ToArray();&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    void IOrderContext.UpdateOrder(IOrderDto existingOrder)&lt;br /&gt;    {&lt;br /&gt;        Binary newTimestamp = null;&lt;br /&gt;        _db.update_order(&lt;br /&gt;            existingOrder.Id,&lt;br /&gt;            existingOrder.Customer,&lt;br /&gt;            existingOrder.Date,&lt;br /&gt;            existingOrder.ShippingCost,&lt;br /&gt;            existingOrder.Timestamp, &lt;br /&gt;            ref newTimestamp);&lt;br /&gt;        existingOrder.Timestamp = newTimestamp.ToArray();&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    void IOrderContext.DeleteOrder(int id)&lt;br /&gt;    {&lt;br /&gt;        _db.delete_order(id);&lt;br /&gt;    }&lt;/pre&gt;&lt;p&gt;We also have insert, update, and deletes for the line item entities as well, but they are very similar, so I'll save them for the sample code download at the end.&lt;/p&gt;&lt;h2&gt;Sample Application&lt;/h2&gt;&lt;p&gt;To make all of this really gel, I wanted to include a fairly extensive sample application that demonstrates everything we've been talking about in the last three posts: dependency injection and the Repository pattern with CSLA.NET.&amp;#160; However, this sample goes a bit further than what we've talked about so far so I wanted to briefly discuss it but save the details for an upcoming post.&lt;/p&gt;&lt;p&gt;A lot of what we've been building here are mechanisms to abstract each layer of our application so they are more loosely-coupled and more testable.&amp;#160; This is why dependency injection is so useful and why patterns like Repository really help.&amp;#160; But the Repository pattern is really just a means to abstract the data access layer.&amp;#160; What if you wanted to abstract the business layer?&amp;#160; What if you wanted to write unit tests that tested the functionality in your UI layer in isolation so that you wouldn't have to build concrete business objects in your tests?&amp;#160; That's the one big additional thing that the sample application sets out to do.&amp;#160; In short, it does this by defining abstractions (in the form of interfaces) for each business class and pulls out the static factor methods into separate factory interfaces and classes.&lt;/p&gt;&lt;p&gt;So here's a quick rundown of the projects that are included in the sample application solution, which is called &lt;strong&gt;CslaRepositoryTest&lt;/strong&gt;:&lt;/p&gt;&lt;ul&gt;&lt;br /&gt;  &lt;li&gt;&lt;strong&gt;DataAccess&lt;/strong&gt; - contains the abstract data access layer interfaces&lt;/li&gt;&lt;li&gt;&lt;strong&gt;DataAccess.SqlServerRepository&lt;/strong&gt; - a concrete implementation of the types in &lt;strong&gt;DataAccess&lt;/strong&gt;&lt;/li&gt;&lt;li&gt;&lt;strong&gt;BusinessLayer&lt;/strong&gt; - CSLA.NET business layer objects&lt;/li&gt;&lt;li&gt;&lt;strong&gt;BusinessLayer.Test&lt;/strong&gt; - unit tests that test the business layer, mocking out the data access layer&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Gui&lt;/strong&gt; - a Windows Forms GUI that uses the business objects in &lt;strong&gt;BusinessLayer&lt;/strong&gt;.&amp;#160; The GUI uses a &lt;a href="http://en.wikipedia.org/wiki/Model_View_Presenter"&gt;Model/View/Presenter&lt;/a&gt;-style architecture so it can be more easily tested.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Gui.Test&lt;/strong&gt; - unit tests that test the GUI layer, mocking out the business objects&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Core&lt;/strong&gt; - contains common types not necessarily specific to a particular layer&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;p&gt;Some other notes:&lt;/p&gt;&lt;ul&gt;&lt;br /&gt;  &lt;li&gt;The &lt;strong&gt;BusinessLayer&lt;/strong&gt; is compiled against CSLA.NET 3.5.&lt;/li&gt;&lt;li&gt;The sample application uses v1.2 of the &lt;a href="http://msdn.microsoft.com/en-us/library/dd203101.aspx"&gt;Unity Application Block&lt;/a&gt; as an IOC Container.&amp;#160; The &lt;strong&gt;Gui &lt;/strong&gt;project configures uses a file called &lt;strong&gt;Unity.config&lt;/strong&gt; to configure Unity.&lt;/li&gt;&lt;li&gt;The &lt;strong&gt;DataAccess.SqlServerRepository&lt;/strong&gt; project uses &lt;strong&gt;SQL Server Express &lt;/strong&gt;to attach to a file instance of the database, just like the CSLA.NET &lt;strong&gt;ProjectTracker&lt;/strong&gt; sample application does.&amp;#160; The database file is &lt;strong&gt;Orders.mdf&lt;/strong&gt; and is located in the same directory as the solution.&lt;/li&gt;&lt;li&gt;Both test projects use &lt;a href="http://nunit.org"&gt;NUnit&lt;/a&gt; as their unit testing framework.&amp;#160; You should be able to run all of the unit tests in the NUnit-GUI application.&lt;/li&gt;&lt;li&gt;Both test projects use &lt;a href="http://ayende.com/projects/rhino-mocks.aspx"&gt;Rhino Mocks&lt;/a&gt; v3.5 for mocking objects.&lt;/li&gt;&lt;li&gt;The binaries for all of the 3rd party dependencies mentioned above are included in a &lt;strong&gt;lib&lt;/strong&gt; folder in the download.&amp;#160; The only thing you need to have installed is &lt;strong&gt;Visual Studio 2008 &lt;/strong&gt;and &lt;strong&gt;SQL Server 2005 Express &lt;/strong&gt;(which usual comes with &lt;strong&gt;Visual Studio 2008&lt;/strong&gt;).&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;p&gt;Finally, here's the &lt;a href="http://www.twistedstream.com/CslaRepositoryTest.zip"&gt;sample application&lt;/a&gt;.&lt;/p&gt;&lt;h2&gt;Conclusion&lt;/h2&gt;&lt;p&gt;In this post we walked through a concrete implementation of our abstract data access layer that uses the Repository pattern.&amp;#160; Our implementation used LINQ to SQL, but we could have easily created one that used any other data access technology.&amp;#160; In an upcoming post, we'll dig a little further into how to abstract the business layer itself.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/35759427-4666150613326106702?l=beyondthispoint.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://beyondthispoint.blogspot.com/feeds/4666150613326106702/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=35759427&amp;postID=4666150613326106702' title='21 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/35759427/posts/default/4666150613326106702'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/35759427/posts/default/4666150613326106702'/><link rel='alternate' type='text/html' href='http://beyondthispoint.blogspot.com/2009/02/using-repository-pattern-with-cslanet.html' title='Using the Repository Pattern with CSLA.NET, Part 2'/><author><name>Pete</name><uri>http://www.blogger.com/profile/16688548320976910930</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://photos1.blogger.com/blogger2/1347/4365/240/gse_multipart43909.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh3.ggpht.com/_Qd3LFpzGA5A/SZun7IN_tiI/AAAAAAAAAbo/1tWftJn0BSg/s72-c/Ordersdbmlscreenshot_thumb2.png?imgmax=800' height='72' width='72'/><thr:total>21</thr:total></entry><entry><id>tag:blogger.com,1999:blog-35759427.post-6256879000236701839</id><published>2009-01-28T23:59:00.001-06:00</published><updated>2009-02-19T15:59:53.382-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Software'/><title type='text'>Using the Repository Pattern with CSLA.NET, Part 1</title><content type='html'>&lt;p&gt;In a &lt;a href="http://beyondthispoint.blogspot.com/2008/12/using-dependency-injection-with-cslanet.html"&gt;previous post&lt;/a&gt; I showed a way to use dependency injection with CSLA.NET.&amp;#160; In this post, I'll show a practical example of how to use that technique to abstract the data access layer used by your business objects using the &lt;a href="http://martinfowler.com/eaaCatalog/repository.html"&gt;Repository pattern&lt;/a&gt;.&amp;#160; This post is part 1.&amp;#160; At the end of part 2, I'll include a link to a fully-functional demo application that even includes a fully functional business layer, data access layer, UI layer, and unit tests.&lt;/p&gt;&lt;h2&gt;Background&lt;/h2&gt;&lt;p&gt;The default approach for data access in CSLA.NET is to write your data access code right in the business class in the DataPortal_* or Child_* methods.&amp;#160; These methods have direct access to the internal state of the business object so they can easily persist that state to your data store without breaking encapsulation.&amp;#160; While this approach obviously works, there are several reasons why one might want to move this code out of the business object and into its own layer.&amp;#160; Here are few good ones:&lt;/p&gt;&lt;ol&gt;   &lt;li&gt;&lt;em&gt;Testability&lt;/em&gt;: If you want to be able to write tests for your business objects that test isolated units of code (i.e. unit tests), your business object shouldn't be talking to a real database at test-time.&amp;#160; Therefore some kind of abstraction needs to be implemented that allows the real database to be called at run-time and a mocked database to be called at test-time. &lt;/li&gt;    &lt;li&gt;&lt;em&gt;Maintainability&lt;/em&gt;: It's never good when you cram too much code into one class.&amp;#160; Arguably, data access logic is very different than business logic, and therefore has a different set of concerns and pressures for change.&amp;#160; Moving it out to its own layer allows both the business logic and the data access logic to change independently with minimal impact on each other.&amp;#160; For example, an abstracted data access layer, if designed at a high enough level, might allow you to change the underlying database platform (ex: from SQL Server to Oracle) or technology (ADO.NET to LINQ to SQL) without having to alter and recompile your business layer. Abstracting data access into its own layer also jives with SRP (&lt;a href="http://en.wikipedia.org/wiki/Single_responsibility_principle"&gt;Single Responsibility Principal&lt;/a&gt;) which states that every object should only have one reason to change.&amp;#160; While CSLA.NET business objects may never only have just one reason to change, it's always good when you can reduce the number of reasons. &lt;/li&gt;    &lt;li&gt;&lt;em&gt;Security&lt;/em&gt;: Some application architectures require that the data access code itself cannot exist on any public-facing machine (the client workstation in the case of a Rich UI application like WinForms, WPF, or Silverlight or the web server in the case of a web app).&amp;#160; The idea being if a hacker is able to compromise a public-facing machine, and that machine has the data access code on it, they then potentially have the information to access the database itself (i.e. via the connection string).&amp;#160; Moving the data access code to a separate layer means you have the flexibility to only deploy the data access layer to an application server, which stands in between the public-facing machine(s) and the database, behind its own firewall. &lt;/li&gt; &lt;/ol&gt;&lt;h2&gt;Problem&lt;/h2&gt;&lt;p&gt;Unfortunately, abstracting the data access layer is no easy task.&amp;#160; There are a lot of choices and options, so you have to be careful that you don't end up limiting yourself.&amp;#160; Here are a few things we need to consider:&lt;/p&gt;&lt;ol&gt;   &lt;li&gt;We want a data access layer that is called and consumed by the business layer and not one that populates the business objects for us.&amp;#160; If your data access layer populates your business objects it either has to set public properties on your business objects (which fires validation rules unnecessarily) or it has to have access to the internal state of your business objects, which violates encapsulation.&amp;#160; &lt;/li&gt;    &lt;li&gt;We need to determine what level we want to do our abstraction.&amp;#160; We could create a low-level abstraction that represents a specific data access technology.&amp;#160; For example, you could abstract ADO.NET by only working with the various ADO.NET interfaces (&lt;a href="http://msdn.microsoft.com/en-us/library/system.data.idbconnection.aspx"&gt;IDbConnection&lt;/a&gt;, &lt;a href="http://msdn.microsoft.com/en-us/library/system.data.idbconnection.aspx"&gt;IDbCommand&lt;/a&gt;, &lt;a href="http://msdn.microsoft.com/en-us/library/system.data.idbconnection.aspx"&gt;IDataReader&lt;/a&gt;, etc.).&amp;#160; But this approach prevents you from easily switching data access technologies, since, by doing so, you would have to alter your business layer code.&amp;#160; Another approach to consider is making your data access layer abstraction generalized enough so it doesn't pin you to a specific technology. &lt;/li&gt; &lt;/ol&gt;&lt;h2&gt;Solution&lt;/h2&gt;&lt;p&gt;The solution I chose to pursue was to follow the relatively well-known data access pattern called the &lt;strong&gt;Repository Pattern&lt;/strong&gt;.&amp;#160; Repository is a high-level abstraction of your data access layer that allows the caller to perform any necessary data access (queries, CRUD, etc) and get back a structured set of objects that represent the data.&amp;#160; I like to call these objects DTO's (data transfer objects) since they are nothing more than strongly-typed data structures (just data, no logic).&amp;#160; And since the Repository is a high-level abstraction, you gain the flexibility of being able to change the underlying data access technology in the future as well as do other fun tricks like data caching.&lt;/p&gt;&lt;p&gt;Given that description of Repository, there still are a lot of ways to implement it.&amp;#160; Just do a Google search on &amp;quot;Repository Pattern&amp;quot; and you'll see what I mean.&amp;#160; I've come up with what I think is a relatively simple approach that seems to work well with CSLA.NET.&amp;#160; Let's take a closer look.&lt;/p&gt;&lt;h4&gt;Repository and Context&lt;/h4&gt;&lt;p&gt;There are two key components to my Repository design: the repository and the context.&lt;/p&gt;&lt;p&gt;The &lt;em&gt;repository&lt;/em&gt; represents the gateway to the data access layer.&amp;#160; The repository will create all the necessary data access objects we need to perform our data access.&amp;#160; Essentially it is a factory object and it can be injected into our business object via dependency injection.&amp;#160; &lt;/p&gt;&lt;p&gt;When you want to actually perform data access operations, you usually want this to be done in a &lt;em&gt;context&lt;/em&gt; of some sort that you can dispose of when you're done.&amp;#160; Maybe you need to execute one or more calls atomically within a transaction or maybe you just need to do some querying and you want to ensure that the connection is closed when you're done, even if the query fails.&amp;#160; This is what a context is for.&amp;#160; &lt;/p&gt;&lt;p&gt;The repository's primary job is to create a context when the business object needs to perform data access.&amp;#160; It can also create other data access objects, like DTO's, that may be needed to assist in performing data access.&lt;/p&gt;&lt;p&gt;So, let's take a look at what the repository and context might look like in code:&lt;/p&gt;&lt;pre class="c#" name="code"&gt;public interface IRepository&amp;lt;TContext&amp;gt;&lt;br /&gt;    where TContext : IContext&lt;br /&gt;{&lt;br /&gt;    TContext CreateContext(bool isTransactional);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;public interface IContext&lt;br /&gt;    : IDisposable&lt;br /&gt;{&lt;br /&gt;    void CompleteTransaction();&lt;br /&gt;}&lt;/pre&gt;&lt;p&gt;Pretty simple.&amp;#160; An &lt;strong&gt;IRepository&lt;/strong&gt; object can create a specific type of &lt;strong&gt;IContext&lt;/strong&gt; object, and when it does, you have to specify whether or not the &lt;strong&gt;IContext&lt;/strong&gt; is transactional.&amp;#160; If an &lt;strong&gt;IContext&lt;/strong&gt; is transactional, it has a method called &lt;strong&gt;CompleteTransaction&lt;/strong&gt; that is called by the business object to indicate that the transaction has succeeded and should be committed.&amp;#160; And regardless if the &lt;strong&gt;IContext&lt;/strong&gt; object is transactional or not, it is disposable which makes it easy for the calling business object to close things like database connections.&lt;/p&gt;&lt;p&gt;&lt;em&gt;And by the way, I personally prefer to use interfaces for my abstractions (vs abstract classes) because they're much easier to mock in a testing scenario.&lt;/em&gt;&lt;/p&gt;&lt;p&gt;Notice that neither of these interfaces has any methods that perform specific database operation (ex: CRUD).&amp;#160; I prefer to move those down into more specialized interfaces that abstract a specific set of entities in your data access layer.&amp;#160; For example, here's a repository and a context for a simple order-entry system that contains order and line item entities:&lt;/p&gt;&lt;pre class="c#" name="code"&gt;public interface IOrderRepository&lt;br /&gt;    : IRepository&amp;lt;IOrderContext&amp;gt;&lt;br /&gt;{&lt;br /&gt;    IOrderDto CreateOrderDto();&lt;br /&gt;    ILineItemDto CreateLineItemDto();&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public interface IOrderContext&lt;br /&gt;    : IContext&lt;br /&gt;{&lt;br /&gt;    IEnumerable&amp;lt;IOrderInfoDto&amp;gt; FetchInfoList();&lt;br /&gt;    IOrderDto FetchSingleWithLineItems(int id);&lt;br /&gt;    void InsertOrder(IOrderDto newOrder);&lt;br /&gt;    void UpdateOrder(IOrderDto existingOrder);&lt;br /&gt;    void DeleteOrder(int id);&lt;br /&gt;    void InsertLineItem(ILineItemDto newLineItem);&lt;br /&gt;    void UpdateLineItem(ILineItemDto existingLineItem);&lt;br /&gt;    void DeleteLineItem(int id, int orderId);&lt;br /&gt;}&lt;/pre&gt;&lt;p&gt;Most of the action is down in the &lt;strong&gt;IOrderContext &lt;/strong&gt;object; it contains all of the data access methods that involve interacting with the database.&amp;#160; There are two methods for querying and retrieving order data.&amp;#160; The remaining methods perform the inserts, updates, and deletes.&lt;/p&gt;&lt;p&gt;Notice that my query methods are explicit: &amp;quot;fetch info list&amp;quot; and &amp;quot;fetch single order with line items&amp;quot;.&amp;#160; I've seen some Repository Pattern implementations out there that provide more generic query methods that allow you to harness the power of LINQ by returning an &lt;a href="http://msdn.microsoft.com/en-us/library/bb351562.aspx"&gt;IQueryable&amp;lt;T&amp;gt;&lt;/a&gt; object.&amp;#160; While there's nothing wrong with this approach, it does lock you into using data access technologies in your concrete implementation that only support LINQ.&amp;#160; Some do, some don't (like ADO.NET).&amp;#160; To be flexible, I chose to not lock myself into LINQ, so my query methods return simple &lt;a href="http://msdn.microsoft.com/en-us/library/9eekhta0.aspx"&gt;IEnumerable&amp;lt;T&amp;gt;&lt;/a&gt; results.&amp;#160; And even if you are confident that any data access technology you will use is LINQ-friendly, not all LINQ implementations are the same.&amp;#160; For example: LINQ syntax for LINQ to SQL is a little different than LINQ syntax for LINQ to Entities.&lt;/p&gt;&lt;h4&gt;The DTO's&lt;/h4&gt;&lt;p&gt;Notice that all data is transferred to and from these data access methods via DTO's.&amp;#160; The only issue is that these DTO's are interfaces so if the business object has to pass them &lt;em&gt;to&lt;/em&gt; the context (say to the &lt;strong&gt;InsertOrder &lt;/strong&gt;method), it needs a way to create one first.&amp;#160; That's where the DTO factory methods on the &lt;strong&gt;IOrderRepository&lt;/strong&gt; object come into play.&amp;#160; This further cements the concept that the repository is really an object factory.&lt;/p&gt;&lt;p&gt;Before we move on, let's take a look at the definition of the DTO interfaces in my example.&lt;/p&gt;&lt;p&gt;First, the &lt;strong&gt;IOrderInfoDto&lt;/strong&gt; which is returned by the &lt;strong&gt;FetchInfoList&lt;/strong&gt; method:&lt;/p&gt;&lt;pre class="c#" name="code"&gt;public interface IOrderInfoDto&lt;br /&gt;{&lt;br /&gt;    int Id { get; set; }&lt;br /&gt;    string Customer { get; set; }&lt;br /&gt;    DateTime Date { get; set; }&lt;br /&gt;}&lt;/pre&gt;&lt;p&gt;This is giving us only a portion of the fields that may be defined by the order entity in the database.&amp;#160; The purpose of this DTO (and the &lt;strong&gt;ReadOnlyBase&amp;lt;T&amp;gt;&lt;/strong&gt; business object it populates, is to provide a simplified view of order data, perhaps to populate a list in the GUI.&lt;/p&gt;&lt;p&gt;When we need a single entire order, the &lt;strong&gt;FetchSingleWithLineItems&lt;/strong&gt; method returns the &lt;strong&gt;IOrderDto&lt;/strong&gt; object:&lt;/p&gt;&lt;pre class="c#" name="code"&gt;public interface IOrderDto&lt;br /&gt;{&lt;br /&gt;    int Id { get; set;  }&lt;br /&gt;    string Customer { get; set; }&lt;br /&gt;    DateTime Date { get; set; }&lt;br /&gt;    decimal ShippingCost { get; set; }&lt;br /&gt;    byte[] Timestamp { get; set; }&lt;br /&gt;    IEnumerable&amp;lt;ILineItemDto&amp;gt; LineItems { get; }&lt;br /&gt;}&lt;/pre&gt;&lt;p&gt;It contains pretty much the same data as &lt;strong&gt;IOrderInfoDto&lt;/strong&gt; plus the remaining order entity data and also a collection of child line items.&amp;#160; Those line item objects are of type &lt;strong&gt;ILineItemDto&lt;/strong&gt;:&lt;/p&gt;&lt;pre class="c#" name="code"&gt;public interface ILineItemDto&lt;br /&gt;{&lt;br /&gt;    int Id { get; set; }&lt;br /&gt;    int OrderId { get; set; }&lt;br /&gt;    string ProductName { get; set; }&lt;br /&gt;    decimal Price { get; set; }&lt;br /&gt;    int Quantity { get; set; }&lt;br /&gt;    byte[] Timestamp { get; set; }&lt;br /&gt;}&lt;/pre&gt;&lt;p&gt;By the way, both the &lt;strong&gt;IOrderDto&lt;/strong&gt; and &lt;strong&gt;ILineItemDto&lt;/strong&gt; interfaces contain a &lt;strong&gt;Timestamp&lt;/strong&gt; property which is used to manage data concurrency.&amp;#160; This timestamp has to be persisted into the business object itself.&amp;#160; I'll show how this is done in the sample code, but Rocky also does it in his &lt;strong&gt;ProjectTracker&lt;/strong&gt; sample application.&lt;/p&gt;&lt;h4&gt;Calling the Repository from the Business Object&lt;/h4&gt;&lt;p&gt;I think to really see how this implementation of the Repository Pattern works, we should take a look at how a business object would call these data access objects.&amp;#160; Let's examine the simplest case of a root-level collection of read-only order business objects (we'll name it &lt;strong&gt;OrderInfoCollection&lt;/strong&gt;) performing its data access while fetching the collection.&lt;/p&gt;&lt;pre class="c#" name="code"&gt;private void DataPortal_Fetch()&lt;br /&gt;{&lt;br /&gt;    RaiseListChangedEvents = false;&lt;br /&gt;    using (var context = EnsureDependency(_repository).CreateContext(false))&lt;br /&gt;    {&lt;br /&gt;        IsReadOnly = false;&lt;br /&gt;        foreach (var dto in context.FetchInfoList())&lt;br /&gt;        {&lt;br /&gt;            var child = DataPortal.FetchChild&amp;lt;OrderInfo&amp;gt;(dto);&lt;br /&gt;            this.Add(child);&lt;br /&gt;        }&lt;br /&gt;        IsReadOnly = true;&lt;br /&gt;    }&lt;br /&gt;    RaiseListChangedEvents = true;&lt;br /&gt;}&lt;/pre&gt;&lt;p&gt;The code follows the standard pattern for the implementation of the &lt;strong&gt;DataPortal_Fetch&lt;/strong&gt; method of a business object that inherits from &lt;strong&gt;ReadOnlyListBase&amp;lt;T,C&amp;gt;&lt;/strong&gt; where we turn off list-changed events and make the collection temporarily writable while we load it.&amp;#160; But instead of opening up an ADO.NET database connection or creating a LINQ to SQL data context, we talk to an abstract repository instance instead and call &lt;strong&gt;CreateContext&lt;/strong&gt; which returns us a &lt;strong&gt;IOrderContext&lt;/strong&gt; instance.&amp;#160; All context objects implement &lt;strong&gt;IDisposable&lt;/strong&gt; so we wrap it in a &lt;strong&gt;using&lt;/strong&gt; statement which guarantees things like database connections and transactions will be closed up at the end. &lt;/p&gt;&lt;p&gt;Within our &lt;strong&gt;using&lt;/strong&gt; block is where we make all the data access method calls against the context.&amp;#160; In this case, we're calling &lt;strong&gt;FetchInfoList&lt;/strong&gt;, enumerating the &lt;strong&gt;IOrderInfoDto&lt;/strong&gt; objects returned, and using each to create an associated child &lt;strong&gt;OrderInfo&lt;/strong&gt; business object for the collection.&lt;/p&gt;&lt;h4&gt;Dependency Injection&lt;/h4&gt;&lt;p&gt;You may be asking: where does the instance of the &lt;strong&gt;_repository&lt;/strong&gt; field get created and what's this &lt;strong&gt;EnsureDependency&lt;/strong&gt; method?&amp;#160; This goes back to how I implement dependency injection with CSLA.NET which I detail in the &lt;a href="http://beyondthispoint.blogspot.com/2008/12/using-dependency-injection-with-cslanet.html"&gt;previous post&lt;/a&gt;.&amp;#160; Here's the definition of that field and the method that Unity (my dependency-injection/IOC Container framework of choice) uses to inject it:&lt;/p&gt;&lt;pre class="c#" name="code"&gt;[NonSerialized]&lt;br /&gt;[NotUndoable]&lt;br /&gt;private IOrderRepository _repository;&lt;br /&gt;&lt;br /&gt;[InjectionMethod]&lt;br /&gt;public void Inject(IOrderRepository repository)&lt;br /&gt;{&lt;br /&gt;    if (repository == null)&lt;br /&gt;        throw new ArgumentNullException(&amp;quot;repository&amp;quot;);&lt;br /&gt;    _repository = repository;&lt;br /&gt;}&lt;/pre&gt;&lt;p&gt;This is the magic that allows us to easily mock out the data access layer at test-time and use a real concrete implementation at run-time.&amp;#160; Showing how you would mock these objects is a bit more than we have time for now.&amp;#160; However, I will provide unit tests that show it in the downloadable sample code at the end of part 2.&lt;/p&gt;&lt;h4&gt;Transactional Data Access Code&lt;/h4&gt;&lt;p&gt;So we showed the simple example of how a read-only collection would call the data access layer to query the database and populate itself.&amp;#160; Let's also show an example of some data access code in a business object that's transactional.&amp;#160; &lt;/p&gt;&lt;p&gt;Let's jump over to a full &lt;strong&gt;BusinessBase&amp;lt;T&amp;gt;&lt;/strong&gt; business object for an order (we'll call it &lt;strong&gt;Order&lt;/strong&gt;) and take a look at what the &lt;strong&gt;DataPortal_Insert&lt;/strong&gt; method implementation might look like for inserting an order:&lt;/p&gt;&lt;pre class="c#" name="code"&gt;protected override void DataPortal_Insert()&lt;br /&gt;{&lt;br /&gt;    using (var context = EnsureDependency(_repository).CreateContext(true))&lt;br /&gt;    {&lt;br /&gt;        var dto = EnsureDependency(_repository).CreateOrderDto();&lt;br /&gt;        dto.Customer = ReadProperty&amp;lt;string&amp;gt;(CustomerProperty);&lt;br /&gt;        dto.Date = ReadProperty&amp;lt;DateTime&amp;gt;(DateProperty);&lt;br /&gt;        dto.ShippingCost = ReadProperty&amp;lt;decimal&amp;gt;(ShippingCostProperty);&lt;br /&gt;        context.InsertOrder(dto);&lt;br /&gt;        LoadProperty&amp;lt;int&amp;gt;(IdProperty, dto.Id);&lt;br /&gt;        _timestamp = dto.Timestamp;&lt;br /&gt;        DataPortal.UpdateChild(ReadProperty&amp;lt;LineItemCollection&amp;gt;(LineItemsProperty), this, context);&lt;br /&gt;        context.CompleteTransaction();&lt;br /&gt;    }            &lt;br /&gt;}&lt;/pre&gt;&lt;p&gt;A little more complicated, but the pattern is very similar.&amp;#160; We create a context (this time specifying that it's transactional) and we perform our data access operations against it.&amp;#160; In this case we to first create an empty &lt;strong&gt;IOrderDto&lt;/strong&gt; so we can populate it with the business object state and pass it into the &lt;strong&gt;InsertOrder&lt;/strong&gt; data access method. When that's done we load the ID and timestamp of the newly inserted order entity back into the business object.&amp;#160; Finally, we cascade the update call down to any child line item business objects.&lt;/p&gt;&lt;h2&gt;Conclusion&lt;/h2&gt;&lt;p&gt;So this is all great and fun and with just the code we've written so far we could get our business layer up and running with passing unit tests.&amp;#160; But with all of that we still haven't actually hit a real database!&amp;#160; We need to code up a concrete implementation of our repository interfaces to get there.&amp;#160; I'm going to save that for the &lt;a href="http://beyondthispoint.blogspot.com/2009/02/using-repository-pattern-with-cslanet.html"&gt;next post&lt;/a&gt; and with that post will come the promised full set of sample code.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/35759427-6256879000236701839?l=beyondthispoint.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://beyondthispoint.blogspot.com/feeds/6256879000236701839/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=35759427&amp;postID=6256879000236701839' title='8 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/35759427/posts/default/6256879000236701839'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/35759427/posts/default/6256879000236701839'/><link rel='alternate' type='text/html' href='http://beyondthispoint.blogspot.com/2009/01/using-repository-pattern-with-cslanet.html' title='Using the Repository Pattern with CSLA.NET, Part 1'/><author><name>Pete</name><uri>http://www.blogger.com/profile/16688548320976910930</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://photos1.blogger.com/blogger2/1347/4365/240/gse_multipart43909.jpg'/></author><thr:total>8</thr:total></entry><entry><id>tag:blogger.com,1999:blog-35759427.post-1580027715484363093</id><published>2008-12-22T10:32:00.001-06:00</published><updated>2009-02-19T16:02:17.267-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Software'/><title type='text'>Using Dependency Injection with CSLA.NET</title><content type='html'>&lt;p&gt;In this post, I'm going to talk about one way to implement &lt;a href="http://en.wikipedia.org/wiki/Dependency_injection"&gt;Dependency Injection&lt;/a&gt; with &lt;a href="http://www.lhotka.net/cslanet/"&gt;CSLA.NET&lt;/a&gt;.&amp;#160; &lt;/p&gt;  &lt;p&gt;&lt;em&gt;NOTE: My discussions and code samples are using CSLA.NET 3.5.&lt;/em&gt;&lt;/p&gt;  &lt;h2&gt;&lt;/h2&gt;  &lt;h4&gt;Background&lt;/h4&gt;  &lt;p&gt;CSLA.NET.&amp;#160; I'm a big fan.&amp;#160; I have been even back before it went .NET in the VB5 and VB6 days.&lt;/p&gt;  &lt;p&gt;I don't know actual statistics, but I would wager that CSLA.NET is probably the most popular framework for creating object-oriented business objects in .NET.&amp;#160; If you're building even a moderately complex line-of-business application where you want your business logic to live in one place (instead of spread all over the place, the worst being in your UI), you want a middle tier that takes advantage of object-oriented design (not just a set of classes that mimic the structure of the database), and optionally have everything just work in a distributed environment, then you should definitely take a close look at CSLA.NET.&lt;/p&gt;  &lt;p&gt;I'm also a big fan of automated tests around my business layer.&amp;#160; And when I say tests, I'm referring to &lt;a href="http://en.wikipedia.org/wiki/Unit_test"&gt;unit tests&lt;/a&gt;, where I want to validate just the behavior of my business object and nothing else.&amp;#160; In other words, I'm not interested in testing the other layers that my business object may interact with (like the database or a logging component).&amp;#160; Those would be integration or regression tests.&amp;#160; Those are good too, by the way, but unit tests should come first.&lt;/p&gt;  &lt;p&gt;Since business objects almost always have to communicate with other layers, your unit tests need the ability to fake those layers out in order to test your business logic in isolation.&amp;#160; There are variety of ways to do this, but a popular one is to abstract calls to these layers using Dependency Injection and then use a mocking framework to inject a fake version of that layer.&amp;#160; To really do Dependency Injection, you need to use a framework of some kind (typically called an &lt;a href="http://martinfowler.com/articles/injection.html"&gt;IOC Container&lt;/a&gt;) to handle your dependency instances and automatically perform injections on your objects.&lt;/p&gt;  &lt;h4&gt;Problem&lt;/h4&gt;  &lt;p&gt;So, like I said, I really love the CSLA.NET framework.&amp;#160; But with all of its awesome excellence, I've always been challenged writing true unit tests against business objects that use it.&amp;#160; This is because CSLA.NET makes it difficult to implement Dependency Injection.&amp;#160; Why?&amp;#160; In short: CSLA.NET creates your business object instances for you.&amp;#160; This happens in the servers-side portion of the DataPortal.&amp;#160; Dependency Injection works best if it can have some kind of access to the object creation process so an object's dependencies can be injected.&amp;#160; Unfortunately, the DataPortal does not provide any sort of hook where you can create your object.&amp;#160; The DataPortal does this all behind the scenes as a black box and then gives you your object instance after creation, letting you then execute your data-access code in the DataPortal_* methods.&lt;/p&gt;  &lt;p&gt;To be fair, the latest version of CSLA.NET, 3.6 which &lt;a href="http://www.lhotka.net/weblog/CSLANET36ForWindowsAndForSilverlightIsReleased.aspx"&gt;just released&lt;/a&gt;, does give you access into the creation of your business objects via the new object factory feature.&amp;#160; However, there are two challenges with going down this road:&lt;/p&gt;  &lt;ol&gt;   &lt;li&gt;You end up having to write a lot more than just object creation into your object factory classes since the object factory is also responsible for performing all data access including the state population of your business objects.&amp;#160; &lt;/li&gt;    &lt;li&gt;You have to write a factory class for every business object that requires dependency injection.&amp;#160; Couple this with the first point, and that adds up to a lot more coding just so you can have control over the creation of your business objects. &lt;/li&gt; &lt;/ol&gt;  &lt;p&gt;There is also another challenge, and that is that the creation of objects on the server-side of the DataPortal isn't the only place business objects in CSLA.NET are being created!&amp;#160; One of the great features of CSLA.NET is this idea of mobile objects: your business objects have the ability to be transported from one physical tier to another.&amp;#160; However, every time an object goes mobile it needs to be serialized.&amp;#160; When it gets to the destination tier, it needs to be deserialized and deserialization involves recreating the object instance except this time its done within the bowels of the .NET Framework.&amp;#160; Yuck!&amp;#160; Even more difficult to hook into.&amp;#160; And by the way, as of CSLA.NET 3.5, for consistency this serialization/deserialization process even occurs when everything happens in-process on one physical tier.&lt;/p&gt;  &lt;h4&gt;Solution&lt;/h4&gt;  &lt;p&gt;The solution I finally stumbled upon essentially gave up on the idea of trying to hijack the object creation process either in the DataPortal or worse yet in the .NET Framework itself during deserialization.&amp;#160; &lt;/p&gt;  &lt;p&gt;The good news is that most IOC Container frameworks out there allow you to perform dependency inject with an existing object instance.&amp;#160; In my case, I'm using &lt;a href="http://msdn.microsoft.com/en-us/library/dd203101.aspx"&gt;Unity&lt;/a&gt;, the IOC Container from Microsoft, which is a member of their Application Blocks family.&amp;#160; Unity's container object has a method called &lt;a href="http://msdn.microsoft.com/en-us/library/dd203234.aspx#api_buildup"&gt;BuildUp&lt;/a&gt; that let's you do exactly this.&lt;/p&gt;  &lt;p&gt;And the other piece of good news is that CSLA.NET allows very easy access to an object instance just after it is created either by the server-side DataPortal or by deserialization.&amp;#160; All you have to do is override a couple virtual methods and you're done!&lt;/p&gt;  &lt;h4&gt;Implementation&lt;/h4&gt;  &lt;p&gt;My implementation of this was to create a set of base classes that all inherit from main CSLA.NET base classes that do the necessary overrides and perform the dependency injection.&amp;#160; Let's build the base class for BusinessBase&amp;lt;T&amp;gt;, probably the most used CSLA.NET base class.&lt;/p&gt;  &lt;p&gt;First, let's get the basics out of the way.&amp;#160; We'll declare an abstract class called &lt;strong&gt;InjectableBusinessBase&amp;lt;T&amp;gt;&lt;/strong&gt; that inherits from &lt;strong&gt;BusinessBase&amp;lt;T&amp;gt;&lt;/strong&gt; and contains a protected constructor, which all abstract classes should have:&lt;/p&gt;  &lt;pre class="c#" name="code"&gt;[Serializable]&lt;br /&gt;public abstract class InjectableBusinessBase&amp;lt;T&amp;gt;&lt;br /&gt;    : BusinessBase&amp;lt;T&amp;gt;&lt;br /&gt;    where T : BusinessBase&amp;lt;T&amp;gt;&lt;br /&gt;{&lt;br /&gt;&lt;br /&gt;    protected InjectableBusinessBase() { }&lt;br /&gt;&lt;br /&gt;}&lt;/pre&gt;&lt;p&gt;Now, let's add the necessary overrides to hook into post-object creation so we can kick off our dependency injection:&lt;/p&gt;&lt;pre class="c#" name="code"&gt;    protected override void DataPortal_OnDataPortalInvoke(DataPortalEventArgs e)&lt;br /&gt;    {&lt;br /&gt;        Inject();&lt;br /&gt;        base.DataPortal_OnDataPortalInvoke(e);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    protected override void Child_OnDataPortalInvoke(DataPortalEventArgs e)&lt;br /&gt;    {&lt;br /&gt;        Inject();&lt;br /&gt;        base.Child_OnDataPortalInvoke(e);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    protected override void OnDeserialized(StreamingContext context)&lt;br /&gt;    {&lt;br /&gt;        Inject();&lt;br /&gt;        base.OnDeserialized(context);&lt;br /&gt;    }&lt;/pre&gt;&lt;p&gt;First we overrode &lt;strong&gt;DataPortal_OnDataPortalInvoke&lt;/strong&gt;.&amp;#160; This gets called by the server-side DataPortal when a root level business object is created.&amp;#160; Then we overrode &lt;strong&gt;Child_OnDataPortalInvoke&lt;/strong&gt; which gets called by the server-side DataPortal whenever a child object is created by the DataPortal (a new feature as of CSLA.NET 3.5 that I highly recommend).&amp;#160; Finally, we overrode &lt;strong&gt;OnDeserialized&lt;/strong&gt;, which, through the help of &lt;strong&gt;BusinessBase&amp;lt;T&amp;gt;&lt;/strong&gt;, gets called by the deserialization process of .NET.&lt;/p&gt;&lt;p&gt;In all three cases, we're calling an &lt;strong&gt;Inject &lt;/strong&gt;method, which is a private method declared in our base class.&amp;#160; It looks like this:&lt;/p&gt;&lt;pre class="c#" name="code"&gt;    private void Inject()&lt;br /&gt;    {&lt;br /&gt;        var type = this.GetType();&lt;br /&gt;        Ioc.Container.BuildUp(type, this);&lt;br /&gt;    }&lt;/pre&gt;&lt;p&gt;Nothing too fancy here.&amp;#160; We're essentially calling the Unity &lt;strong&gt;BuildUp&lt;/strong&gt; method which requires the type of our business object and the business object instance itself.&lt;/p&gt;&lt;p&gt;What is a little exotic, if you're familiar with Unity, is the &lt;font face="Courier New"&gt;Ioc.Container&lt;/font&gt; code.&amp;#160; Our base class needs some way to access the Unity container instance against which to call &lt;strong&gt;BuildUp&lt;/strong&gt;.&amp;#160; I've created a utility class called &lt;strong&gt;Ioc&lt;/strong&gt; that has a static &lt;strong&gt;Container&lt;/strong&gt; property that returns this.&amp;#160; I'll include the code for this class as well as all the code for the various base classes at the end of this post.&amp;#160; For now, just know that the code &lt;font face="Courier New"&gt;Ioc.Container&lt;/font&gt; returns our IOC container.&lt;/p&gt;&lt;p&gt;That pretty much is all that's required to make dependency injection&amp;#160; work with CLSA.NET and Unity!&amp;#160; However, I do have one more method I want to include in my base class:&lt;/p&gt;&lt;pre class="c#" name="code"&gt;    protected TDependency EnsureDependency&amp;lt;TDependency&amp;gt;(TDependency dependency) &lt;br /&gt;        where TDependency : class&lt;br /&gt;    {&lt;br /&gt;        if (dependency == null)&lt;br /&gt;            throw new MissingDependencyInjectionException(this.GetType(), typeof(TDependency));&lt;br /&gt;        return dependency;&lt;br /&gt;    }&lt;/pre&gt;&lt;p&gt;The purpose of this method will become a little more clear when we take a look at an example concrete business object class that inherits from &lt;strong&gt;InjectableBusinessBase&amp;lt;T&amp;gt;&lt;/strong&gt;.&amp;#160; Essentially all this method does is return the same object that gets passed in.&amp;#160; However, before it returns it, the method checks to make sure the object is null; if so, it throws a custom exception that basically says a required dependency instance is missing that it's of a certain type and is contained within the object of this type.&amp;#160; I'll include the source code for this exception class at the end of the post as well.&lt;/p&gt;&lt;h4&gt;Sample Concrete Class&lt;/h4&gt;&lt;p&gt;To make all this gel, let's code up a concrete business object class that inherits from our base class.&amp;#160; I'm going to code up a very simple &lt;strong&gt;Customer&lt;/strong&gt; class that has two properties, &lt;strong&gt;ID&lt;/strong&gt; and &lt;strong&gt;Name&lt;/strong&gt;, and a single &lt;strong&gt;Fetch&lt;/strong&gt; factory method.&amp;#160; Here's the code so far:&lt;/p&gt;&lt;pre class="c#" name="code"&gt;[Serializable]&lt;br /&gt;public sealed class Customer&lt;br /&gt;    : InjectableBusinessBase&amp;lt;Customer&amp;gt;&lt;br /&gt;{&lt;br /&gt;&lt;br /&gt;    private static PropertyInfo&amp;lt;int&amp;gt; IdProperty = &lt;br /&gt;        RegisterProperty&amp;lt;int&amp;gt;(new PropertyInfo&amp;lt;int&amp;gt;(&amp;quot;Id&amp;quot;));&lt;br /&gt;    public int Id&lt;br /&gt;    {&lt;br /&gt;        get { return GetProperty&amp;lt;int&amp;gt;(IdProperty); }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    private static PropertyInfo&amp;lt;string&amp;gt; NameProperty = &lt;br /&gt;        RegisterProperty&amp;lt;string&amp;gt;(new PropertyInfo&amp;lt;string&amp;gt;(&amp;quot;Name&amp;quot;));&lt;br /&gt;    public string Name&lt;br /&gt;    {&lt;br /&gt;        get { return GetProperty&amp;lt;string&amp;gt;(NameProperty); }&lt;br /&gt;        set { SetProperty&amp;lt;string&amp;gt;(NameProperty, value); }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    private Customer() { }&lt;br /&gt;&lt;br /&gt;    public static Customer Fetch(int id)&lt;br /&gt;    {&lt;br /&gt;        var criteria = new SingleCriteria&amp;lt;Customer, int&amp;gt;(id);&lt;br /&gt;        return DataPortal.Fetch&amp;lt;Customer&amp;gt;(criteria);&lt;br /&gt;    }&lt;br /&gt;    &lt;br /&gt;}&lt;/pre&gt;&lt;p&gt;Nothing new here other than the class inherits from &lt;strong&gt;InjectableBusinessBase&amp;lt;Customer&amp;gt;&lt;/strong&gt;.&amp;#160; We have two properties declared using the new CSLA.NET 3.5 managed property syntax.&amp;#160; Then we have the standard private constructor and the static factory method which uses the DataPortal to create and fetch our business object from the database.&lt;/p&gt;&lt;p&gt;Now I want to introduce a dependency into my class that performs the actual data access necessary to fetch the customer, which will be performed in a standard &lt;strong&gt;DataPortal_Fetch&lt;/strong&gt; method.&amp;#160; This data access object will implement a custom interface called &lt;strong&gt;ICustomerDataAccess&lt;/strong&gt; and will have a method called &lt;strong&gt;FetchData&lt;/strong&gt; that takes a customer ID and returns a DTO that contains my customer data.&amp;#160; Here's what my &lt;strong&gt;ICustomerDataAccess&lt;/strong&gt; interface might look like:&lt;/p&gt;&lt;pre class="c#" name="code"&gt;public interface ICustomerDataAccess&lt;br /&gt;{&lt;br /&gt;    ICustomerDto FetchData(int id);&lt;br /&gt;}&lt;/pre&gt;&lt;p&gt;By the way, a DTO (data transfer object) is just an object that contains nothing but data.&amp;#160; For mocking purposes, my DTO is an interface (called &lt;strong&gt;ICustomerDto&lt;/strong&gt;).&amp;#160; &lt;/p&gt;&lt;p&gt;&lt;em&gt;In an &lt;a href="http://beyondthispoint.blogspot.com/2009/01/using-repository-pattern-with-cslanet.html"&gt;upcoming post&lt;/a&gt;, I'll show a more elegant way to abstract away the data access layer in CSLA.NET using dependency injection and the Repository pattern.&lt;/em&gt;&lt;/p&gt;&lt;p&gt;Let's add a field to my class that will contain this dependency:&lt;/p&gt;&lt;pre class="c#" name="code"&gt;    [NonSerialized]&lt;br /&gt;    [NotUndoable]&lt;br /&gt;    private ICustomerDataAccess _dataAccess;&lt;/pre&gt;&lt;p&gt;Notice that I've added two attributes to this field.&amp;#160; The first, &lt;a href="http://msdn.microsoft.com/en-us/library/system.nonserializedattribute.aspx"&gt;NonSerializedAttribute&lt;/a&gt;, is important because we don't want this dependency (or probably any dependency) to be serialized if the business object were to become mobile and transferred from one physical tier to another.&amp;#160; In the case of data access, it really only needs to occur on the tier that performs data access (i.e. an application server).&lt;/p&gt;&lt;p&gt;The second attribute, &lt;strong&gt;NotUndoableAttribute&lt;/strong&gt;, comes from the CSLA.NET framework.&amp;#160; It tells CSLA.NET that this field is not to participate in the undo functionality provided by the &lt;strong&gt;BusinessBase&amp;lt;T&amp;gt;&lt;/strong&gt; class.&amp;#160; This is because the dependency does not contain any business object state that would need to be rolled back if the users were to perform an Undo.&lt;/p&gt;&lt;p&gt;Having both of these attributes in place essentially relieves us of the need to make our dependency serializable.&amp;#160; The actual implementation class of &lt;strong&gt;ICustomerDataAccess&lt;/strong&gt; can be just a plain old object.&lt;/p&gt;&lt;p&gt;Now we need a way to inject this dependency into our business object.&amp;#160; Unity gives you three ways: constructor, set-able property, or method.&amp;#160; We can't use the Customer constructor since we're performing the dependency injection &lt;em&gt;after&lt;/em&gt; the instance has been created by the DataPortal or deserialization.&amp;#160; We could use the set-able property.&amp;#160; However, I personally like the method approach since, if you have multiple dependencies, you can inject all of them in a single call.&amp;#160; &lt;/p&gt;&lt;p&gt;We just have one in our example, so here's the code:&lt;/p&gt;&lt;pre class="c#" name="code"&gt;    [InjectionMethod]&lt;br /&gt;    public void Inject(ICustomerDataAccess dataAccess)&lt;br /&gt;    {&lt;br /&gt;        if (dataAccess == null)&lt;br /&gt;            throw new ArgumentNullException(&amp;quot;dataAccess&amp;quot;);&lt;br /&gt;        _dataAccess = dataAccess;&lt;br /&gt;    }&lt;/pre&gt;&lt;p&gt;My method can be named anything, but it needs to be decorated with the special Unity InjectionMethodAttribute and it needs a parameter for each of my dependencies so I can set the fields.&amp;#160; Notice I also throw an &lt;a href="http://msdn.microsoft.com/en-us/library/system.argumentnullexception.aspx"&gt;ArgumentNullException&lt;/a&gt; if whatever is doing the injection passes in a null.&amp;#160; We want to fail if this ever happens.&lt;/p&gt;&lt;p&gt;Finally, we need to add the &lt;strong&gt;DataPortal_Fetch&lt;/strong&gt; method that will use our data access dependency:&lt;/p&gt;&lt;pre class="c#" name="code"&gt;    private void DataPortal_Fetch(SingleCriteria&amp;lt;Customer, int&amp;gt; criteria)&lt;br /&gt;    {&lt;br /&gt;        var customerData = EnsureDependency(_dataAccess).FetchData(criteria.Value);&lt;br /&gt;        LoadProperty&amp;lt;int&amp;gt;(IdProperty, customerData.Id);&lt;br /&gt;        LoadProperty&amp;lt;string&amp;gt;(NameProperty, customerData.Name);&lt;br /&gt;    }&lt;/pre&gt;&lt;p&gt;The first thing we do is call the &lt;strong&gt;FetchData&lt;/strong&gt; method of my &lt;strong&gt;ICustomerDataAccess&lt;/strong&gt; object to obtain the customer DTO.&amp;#160; Here is where you can see the &lt;strong&gt;EnsureDependency&lt;/strong&gt; method in action that we coded earlier in the base class.&amp;#160; If we wrap the &lt;font face="Courier New" size="2"&gt;_dataAccess&lt;/font&gt; dependency in a call to &lt;strong&gt;EnsureDependency&lt;/strong&gt;, we know we're not going to get a &lt;a href="http://msdn.microsoft.com/en-us/library/system.nullreferenceexception.aspx"&gt;NullReferenceException&lt;/a&gt;, which is non-descriptive and hard to debug.&amp;#160; Rather we'll get a more descriptive exception that explains exactly what dependency is missing from what object.&lt;/p&gt;&lt;p&gt;Once we have the DTO, we can simply populate our business object in the standard CSLA.NET 3.5 way.&lt;/p&gt;&lt;h4&gt;Conclusion&lt;/h4&gt;&lt;p&gt;There you have it!&amp;#160; Dependency Injection and CSLA.NET living peacefully side-by-side.&amp;#160; &lt;/p&gt;&lt;p&gt;If you'd like to take a look at the complete set of source code for this, which includes all the &lt;strong&gt;Injectable&lt;/strong&gt;* base classes, the &lt;strong&gt;MissingDependencyInjectionException&lt;/strong&gt; class, the &lt;strong&gt;Ioc&lt;/strong&gt; helper class, and the sample &lt;strong&gt;Customer&lt;/strong&gt; business object code, you can &lt;a href="http://www.twistedstream.com/CslaDependencyInjection.zip"&gt;download it here&lt;/a&gt;.&amp;#160; Before the sample code will compile, you will also need to download CSLA.NET 3.5 and Unity 1.2 and update the assembly references accordingly.&lt;/p&gt;&lt;p&gt;Now I'm not pretending that this is the only way to make this work.&amp;#160; If there are other people out there who've made Dependency Injection and CSLA.NET work better, perhaps with a different IOC Container like &lt;a href="http://structuremap.sourceforge.net/Default.htm"&gt;StructureMap&lt;/a&gt; or &lt;a href="http://www.castleproject.org/container"&gt;Castle Windsor&lt;/a&gt;, write a comment and let me know!&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/35759427-1580027715484363093?l=beyondthispoint.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://beyondthispoint.blogspot.com/feeds/1580027715484363093/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=35759427&amp;postID=1580027715484363093' title='17 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/35759427/posts/default/1580027715484363093'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/35759427/posts/default/1580027715484363093'/><link rel='alternate' type='text/html' href='http://beyondthispoint.blogspot.com/2008/12/using-dependency-injection-with-cslanet.html' title='Using Dependency Injection with CSLA.NET'/><author><name>Pete</name><uri>http://www.blogger.com/profile/16688548320976910930</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://photos1.blogger.com/blogger2/1347/4365/240/gse_multipart43909.jpg'/></author><thr:total>17</thr:total></entry><entry><id>tag:blogger.com,1999:blog-35759427.post-271431133010340626</id><published>2008-12-15T22:43:00.001-06:00</published><updated>2008-12-15T23:03:24.998-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Software'/><title type='text'>Trying out PreCode and SyntaxHighlighter</title><content type='html'>&lt;p&gt;Per Scott Hanselman's recent &lt;a href="http://www.hanselman.com/blog/BestCodeSyntaxHighlighterForSnippetsInYourBlog.aspx"&gt;blog post&lt;/a&gt; on the best code syntax highlighter for your blog, I'm giving his suggestion a try, which is to use the &lt;a href="http://www.codeplex.com/precode"&gt;PreCode&lt;/a&gt; plug-in for &lt;a href="http://windowslivewriter.spaces.live.com/"&gt;Live Writer&lt;/a&gt; (a free blog writing GUI from Microsoft), which renders your code snippets so they use the &lt;a href="http://code.google.com/p/syntaxhighlighter/"&gt;SyntaxHighlighter&lt;/a&gt; JavaScript library.  &lt;/p&gt;  &lt;p&gt;I host my blog on Google's &lt;a href="http://www.blogger.com/"&gt;Blogger&lt;/a&gt; and so I don't have an easy way to host the necessary JavaScript files to make SyntaxHighlighter work.  But I do have a Google Pages account, so I was able to upload them there and modify my blog's HTML Template in Blogger to include the necessary &amp;lt;link&amp;gt; and &amp;lt;script&amp;gt; tags to make the magic happen.  I used tips from &lt;a href="http://developertips.blogspot.com/2007/08/syntaxhighlighter-on-blogger.html"&gt;this blog post&lt;/a&gt; to get it working.&lt;/p&gt;  &lt;p&gt;I wanted to get this set up since I'm about to post three (yes three!) blog entries on CSLA.NET which will probably contain a fair amount of code.&lt;/p&gt;  &lt;p&gt;Anyway, without further adieu, here's my first SyntaxHighlighter code snippet, generated by the PreCode plug-in:&lt;/p&gt;  &lt;pre class="c#" name="code"&gt;public interface IFoo&lt;br /&gt;{&lt;br /&gt;  void Bar();&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public class MyFoo&lt;br /&gt;  : IFoo&lt;br /&gt;{&lt;br /&gt;  public Bar()&lt;br /&gt;  {&lt;br /&gt;      //do something&lt;br /&gt;  }&lt;br /&gt;}&lt;/pre&gt;&lt;p&gt;NOTE: If you're using a feed reader like Google Reader, this code won't look like anything special.  You'll have to open the blog page directly to see the full effect.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/35759427-271431133010340626?l=beyondthispoint.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://beyondthispoint.blogspot.com/feeds/271431133010340626/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=35759427&amp;postID=271431133010340626' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/35759427/posts/default/271431133010340626'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/35759427/posts/default/271431133010340626'/><link rel='alternate' type='text/html' href='http://beyondthispoint.blogspot.com/2008/12/trying-out-precode-and.html' title='Trying out PreCode and SyntaxHighlighter'/><author><name>Pete</name><uri>http://www.blogger.com/profile/16688548320976910930</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://photos1.blogger.com/blogger2/1347/4365/240/gse_multipart43909.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-35759427.post-1058482108575589401</id><published>2008-11-17T08:57:00.003-06:00</published><updated>2008-11-17T09:02:48.540-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='General'/><title type='text'>Where's Pete?</title><content type='html'>Wow, it's been ages since I've posted anything here.  Pretty lame.  I have a laundry list of stuff I actually want to write about, but I just haven't found the time.  Maybe it's cuz we had another baby back in September?  Still, no excuse.&lt;br /&gt;&lt;br /&gt;Hopefully this will post will kick me in the pants.  Feel free to comment and do the same.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_Qd3LFpzGA5A/SSGHd5YYkYI/AAAAAAAAAGA/_aC6p6EhOyU/s1600-h/IMG_4126.JPG"&gt;&lt;img style="cursor: pointer; width: 320px; height: 240px;" src="http://3.bp.blogspot.com/_Qd3LFpzGA5A/SSGHd5YYkYI/AAAAAAAAAGA/_aC6p6EhOyU/s320/IMG_4126.JPG" alt="" id="BLOGGER_PHOTO_ID_5269641986689241474" border="0" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/35759427-1058482108575589401?l=beyondthispoint.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://beyondthispoint.blogspot.com/feeds/1058482108575589401/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=35759427&amp;postID=1058482108575589401' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/35759427/posts/default/1058482108575589401'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/35759427/posts/default/1058482108575589401'/><link rel='alternate' type='text/html' href='http://beyondthispoint.blogspot.com/2008/11/wheres-pete.html' title='Where&apos;s Pete?'/><author><name>Pete</name><uri>http://www.blogger.com/profile/16688548320976910930</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://photos1.blogger.com/blogger2/1347/4365/240/gse_multipart43909.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_Qd3LFpzGA5A/SSGHd5YYkYI/AAAAAAAAAGA/_aC6p6EhOyU/s72-c/IMG_4126.JPG' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-35759427.post-7051877024218705863</id><published>2008-06-10T10:33:00.001-05:00</published><updated>2008-06-10T10:33:06.494-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Biking'/><title type='text'>More than one way to not drive to work</title><content type='html'>&lt;a href="http://lh3.ggpht.com/peter.stromquist.twistedstream/SE6er4VtP_I/AAAAAAAAAEQ/R6YJRtfF6AI/s1600-h/009%5B2%5D.jpg"&gt;&lt;img style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" height="197" alt="009" src="http://lh4.ggpht.com/peter.stromquist.twistedstream/SE6esZ3EyzI/AAAAAAAAAEU/o2WIWliYMd8/009_thumb.jpg?imgmax=800" width="244" border="0" /&gt;&lt;/a&gt;   &lt;p&gt;Saw this on my ride into work today on the &lt;a href="http://www.segway.com/"&gt;Gateway Trail&lt;/a&gt;.&amp;#160; I think I've seen a maybe two &lt;a href="http://www.segway.com/"&gt;Segways&lt;/a&gt; ever in my life.&amp;#160; This guy rocks!&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/35759427-7051877024218705863?l=beyondthispoint.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://beyondthispoint.blogspot.com/feeds/7051877024218705863/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=35759427&amp;postID=7051877024218705863' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/35759427/posts/default/7051877024218705863'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/35759427/posts/default/7051877024218705863'/><link rel='alternate' type='text/html' href='http://beyondthispoint.blogspot.com/2008/06/more-than-one-way-to-not-drive-to-work.html' title='More than one way to not drive to work'/><author><name>Pete</name><uri>http://www.blogger.com/profile/16688548320976910930</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://photos1.blogger.com/blogger2/1347/4365/240/gse_multipart43909.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh4.ggpht.com/peter.stromquist.twistedstream/SE6esZ3EyzI/AAAAAAAAAEU/o2WIWliYMd8/s72-c/009_thumb.jpg?imgmax=800' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-35759427.post-4562299020229794526</id><published>2008-06-08T20:00:00.000-05:00</published><updated>2008-06-09T08:42:50.906-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Biking'/><title type='text'>2008 MS150 - Sunday</title><content type='html'>&lt;p&gt;Today we finished the second half of the MS150.&amp;#160; Hit the road at about 7:30am and crossed the finish line in Blaine at about 12:20pm.&amp;#160; &lt;/p&gt;  &lt;p&gt;Today's pace was a little faster than yesterdays, probably due to the slight tail wind instead of a head wind.&amp;#160; We averaged over 20mph for the day and we had one 22 mile run in the morning (where we skipped a rest stop) where we averaged over 22mph!&amp;#160; That was a lot of fun!&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/peter.stromquist.twistedstream/SE0zVwYifPI/AAAAAAAAAEI/4cBWAgCpzEs/s1600-h/008%5B3%5D.jpg"&gt;&lt;img style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" height="244" alt="008" src="http://lh5.ggpht.com/peter.stromquist.twistedstream/SE0zWQ0WJRI/AAAAAAAAAEM/OG-VP2k6Ftg/008_thumb%5B1%5D.jpg?imgmax=800" width="196" align="left" border="0" /&gt;&lt;/a&gt;I took a lot of really cool pictures (with my phone), including one over my shoulder while my group was in formation, but somehow (and I'm still perplexed as to how this happened) I lost the memory card in my phone.&amp;#160; Fortunately, I have this one, which my wife Kris took just after the finish with my kids.&amp;#160; This was the highlight of my weekend anyway!&amp;#160; It was great seeing my family cheering me on when I crossed the finish!&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;Well, it was a great event this year.&amp;#160; I'm so glad I decided to join a team and even more glad that I joined the &lt;a href="http://www.backdrafters.com/"&gt;Backdrafters&lt;/a&gt;.&amp;#160; What an awesome group (115 riders!) led by a great team captain, Doug.&lt;/p&gt;  &lt;p&gt;I've already signed up for next year (got my complimentary MS150 socks).&amp;#160; I'm definitely going to keep riding with the Backdrafters.&amp;#160; &lt;/p&gt;  &lt;p&gt;Thanks for &lt;em&gt;everyone&lt;/em&gt; who supported me this year.&amp;#160; I increased my donations from last year and brought in over $600!&amp;#160; Next year I want to raise over $1000, which puts you into the Golden Gears Club.&amp;#160; Also, anyone reading this who would want to experience the MS150 for themselves is more then welcomed to do so!&amp;#160; Just let me know.&amp;#160; You can contact me via email which you can get by viewing the homepage of my blog &lt;a href="http://beyondthispoint.blogspot.com"&gt;here&lt;/a&gt;.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/35759427-4562299020229794526?l=beyondthispoint.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://beyondthispoint.blogspot.com/feeds/4562299020229794526/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=35759427&amp;postID=4562299020229794526' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/35759427/posts/default/4562299020229794526'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/35759427/posts/default/4562299020229794526'/><link rel='alternate' type='text/html' href='http://beyondthispoint.blogspot.com/2008/06/2008-ms150-sunday.html' title='2008 MS150 - Sunday'/><author><name>Pete</name><uri>http://www.blogger.com/profile/16688548320976910930</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://photos1.blogger.com/blogger2/1347/4365/240/gse_multipart43909.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh5.ggpht.com/peter.stromquist.twistedstream/SE0zWQ0WJRI/AAAAAAAAAEM/OG-VP2k6Ftg/s72-c/008_thumb%5B1%5D.jpg?imgmax=800' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-35759427.post-2807745584097063043</id><published>2008-06-07T22:31:00.003-05:00</published><updated>2008-06-10T12:22:30.668-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Biking'/><title type='text'>2008 MS150 - Saturday Evening</title><content type='html'>&lt;p&gt;&lt;a href="http://lh3.ggpht.com/peter.stromquist.twistedstream/SEtSicjGIMI/AAAAAAAAADQ/qdqpqfauJqM/s1600-h/MS1502008Saturday0154.jpg"&gt;&lt;img style="BORDER-TOP-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; BORDER-RIGHT-WIDTH: 0px" height="196" alt="Beer pool!" src="http://lh3.ggpht.com/peter.stromquist.twistedstream/SEtSim-FlNI/AAAAAAAAADU/9ZzaKQ5EXnA/MS1502008Saturday015_thumb2.jpg?imgmax=800" width="244" align="left" border="0" /&gt;&lt;/a&gt;That is a kiddie pool full of beer (with a mini-keg of Heineken in the middle). This was the first thing I saw when we entered the Backdrafters tent. Man, that first beer tasted good!&lt;/p&gt;&lt;p&gt; &lt;/p&gt;&lt;p&gt; &lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;a href="http://lh3.ggpht.com/peter.stromquist.twistedstream/SEtSjMxf75I/AAAAAAAAADY/5k49EdQcbTY/s1600-h/MS1502008Saturday0146.jpg"&gt;&lt;img style="BORDER-TOP-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; BORDER-RIGHT-WIDTH: 0px" height="161" alt="The snack table" src="http://lh3.ggpht.com/peter.stromquist.twistedstream/SEtSjmulMYI/AAAAAAAAADc/6m899OjORHg/MS1502008Saturday014_thumb4.jpg?imgmax=800" width="196" align="left" border="0" /&gt;&lt;/a&gt; &lt;a href="http://lh3.ggpht.com/peter.stromquist.twistedstream/SEtSkEfzqUI/AAAAAAAAADg/IKjDJAuIl7k/s1600-h/MS1502008Saturday0168.jpg"&gt;&lt;img style="BORDER-TOP-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; BORDER-RIGHT-WIDTH: 0px" height="166" alt="Hydrating after the long ride" src="http://lh4.ggpht.com/peter.stromquist.twistedstream/SEtSkW3LgTI/AAAAAAAAADk/EXUzqFrLSZ4/MS1502008Saturday016_thumb6.jpg?imgmax=800" width="196" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;&lt;p&gt;&lt;a href="http://lh6.ggpht.com/peter.stromquist.twistedstream/SEtSlrGw8DI/AAAAAAAAADo/2TsLBMk2XwE/s1600-h/MS1502008Saturday0173.jpg"&gt;&lt;img style="BORDER-TOP-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; BORDER-RIGHT-WIDTH: 0px" height="196" alt="Cap'n Doug enjoying a cold one" src="http://lh6.ggpht.com/peter.stromquist.twistedstream/SEtSmpMMulI/AAAAAAAAADs/0XQMroTX4G4/MS1502008Saturday017_thumb1.jpg?imgmax=800" width="244" align="right" border="0" /&gt;&lt;/a&gt; There was all the beer, water, and snacks you could consume! What a refreshing end to the day. Even Cap'n Doug took a few minutes and enjoyed a cold one.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;At 5pm they served a catered diner and we ate like kings and queens! &lt;/p&gt;&lt;p&gt;&lt;a href="http://lh3.ggpht.com/peter.stromquist.twistedstream/SEtSm14AJ5I/AAAAAAAAADw/151TQ4Yy-I4/s1600-h/MS1502008Saturday0243.jpg"&gt;&lt;img style="BORDER-TOP-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; BORDER-RIGHT-WIDTH: 0px" height="196" alt="Bike farm" src="http://lh5.ggpht.com/peter.stromquist.twistedstream/SEtSnSDcakI/AAAAAAAAAD0/jCCPklrVecE/MS1502008Saturday024_thumb1.jpg?imgmax=800" width="244" align="left" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;&lt;p&gt;Now we and the bikes rest until tomorrow where we do the second half. The weather report looks great: No rain and 10-15 mph NW tail wind!&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/35759427-2807745584097063043?l=beyondthispoint.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://beyondthispoint.blogspot.com/feeds/2807745584097063043/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=35759427&amp;postID=2807745584097063043' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/35759427/posts/default/2807745584097063043'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/35759427/posts/default/2807745584097063043'/><link rel='alternate' type='text/html' href='http://beyondthispoint.blogspot.com/2008/06/2008-ms150-saturday-evening.html' title='2008 MS150 - Saturday Evening'/><author><name>Pete</name><uri>http://www.blogger.com/profile/16688548320976910930</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://photos1.blogger.com/blogger2/1347/4365/240/gse_multipart43909.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh3.ggpht.com/peter.stromquist.twistedstream/SEtSim-FlNI/AAAAAAAAADU/9ZzaKQ5EXnA/s72-c/MS1502008Saturday015_thumb2.jpg?imgmax=800' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-35759427.post-2576249481142964486</id><published>2008-06-07T12:00:00.000-05:00</published><updated>2008-06-07T22:32:46.444-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Biking'/><title type='text'>2008 MS150 - Saturday Ride</title><content type='html'>&lt;p&gt;&lt;a href="http://lh5.ggpht.com/peter.stromquist.twistedstream/SEtSX50C8HI/AAAAAAAAACQ/tKDp_4XNdqc/s1600-h/MS1502008Saturday0044.jpg"&gt;&lt;img style="BORDER-TOP-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; BORDER-RIGHT-WIDTH: 0px" height="196" alt="Leaving Black Bear Casino" src="http://lh5.ggpht.com/peter.stromquist.twistedstream/SEtSYNlJOvI/AAAAAAAAACU/wHkFNUn4V-g/MS1502008Saturday004_thumb2.jpg?imgmax=800" width="244" align="left" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;&lt;p&gt;We left Carlton this morning at about 7:30 am.  I was very excited to ride, but nervous to see if I'd be able to keep up with my partners.  Landon (far right) is a biking freak and claims to have drafted many a semi truck doing 50+ mph!&lt;/p&gt;&lt;p&gt; &lt;/p&gt;&lt;p&gt;&lt;a href="http://lh5.ggpht.com/peter.stromquist.twistedstream/SEtSYopXTYI/AAAAAAAAACY/9ytEu7Rkuh0/s1600-h/MS1502008Saturday00510.jpg"&gt;&lt;img style="BORDER-TOP-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; BORDER-RIGHT-WIDTH: 0px" height="118" alt="First pedals" src="http://lh3.ggpht.com/peter.stromquist.twistedstream/SEtSYzgENxI/AAAAAAAAACc/tcp-VQ4_lWI/MS1502008Saturday005_thumb6.jpg?imgmax=800" width="145" align="right" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;&lt;p&gt;So we hit the road and in about 36 minutes and 10.17 miles later we hit our first rest stop (which was actually rest stop #2 since we started in Carlton instead of Proctor).  Our average speed was 17 mph.  Not too bad for the warm-up ride.  Cory (right, below) decided it was wise to put some air in his tires at this point.&lt;/p&gt;&lt;p&gt;&lt;a href="http://lh6.ggpht.com/peter.stromquist.twistedstream/SEtSZbHN-iI/AAAAAAAAACg/QCBu1zj6kZQ/s1600-h/MS1502008Saturday0068.jpg"&gt;&lt;img style="BORDER-TOP-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; BORDER-RIGHT-WIDTH: 0px" height="140" alt="Rest Stop #2" src="http://lh6.ggpht.com/peter.stromquist.twistedstream/SEtSZyAXh-I/AAAAAAAAACk/7WUv2LrR0vM/MS1502008Saturday006_thumb6.jpg?imgmax=800" width="166" align="left" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;&lt;p&gt;After the first rest stop we picked up the pace and averaged 18.6 to rest stop #3, biking 11.34 miles.  Also I did some pulling (led our group) and was actually able to maintain a decent pace!&lt;/p&gt;&lt;p&gt;At 8:50 we reached rest stop #3 and took a quick breather.  The next stop would be lunch!&lt;/p&gt;&lt;p&gt;With our 10.05 mile run to lunch our average speed increased again to 18.7 mph!  The Subways were great!&lt;/p&gt;&lt;p&gt;&lt;a href="http://lh6.ggpht.com/peter.stromquist.twistedstream/SEtSaS-JgII/AAAAAAAAACo/OSBzqxFgEl0/s1600-h/MS1502008Saturday0085.jpg"&gt;&lt;img style="BORDER-TOP-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; BORDER-RIGHT-WIDTH: 0px" height="166" alt="Rest Stop #4 (Lunch)" src="http://lh4.ggpht.com/peter.stromquist.twistedstream/SEtSagy-TxI/AAAAAAAAACs/zSd0BJqBpLI/MS1502008Saturday008_thumb3.jpg?imgmax=800" width="203" align="left" border="0" /&gt;&lt;/a&gt; &lt;a href="http://lh4.ggpht.com/peter.stromquist.twistedstream/SEtScTYbThI/AAAAAAAAACw/TzyNfkS6r48/s1600-h/MS1502008Saturday0093.jpg"&gt;&lt;img style="BORDER-TOP-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; BORDER-RIGHT-WIDTH: 0px" height="165" alt="Yum!  Subway!" src="http://lh6.ggpht.com/peter.stromquist.twistedstream/SEtScoZ4aBI/AAAAAAAAAC0/_1hbiQgRknw/MS1502008Saturday009_thumb1.jpg?imgmax=800" width="204" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;&lt;p&gt;And a bit filling.  Our 9.13 mile run from lunch to rest stop #5 was a little slower (17.4 mph).  It was definitely getting warmer.  Landon (right, below) was tired of my camera phone.&lt;/p&gt;&lt;p&gt;&lt;a href="http://lh6.ggpht.com/peter.stromquist.twistedstream/SEtSdhbkmPI/AAAAAAAAAC4/zBtla65bHa8/s1600-h/MS1502008Saturday0105.jpg"&gt;&lt;img style="BORDER-TOP-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; BORDER-RIGHT-WIDTH: 0px" height="166" alt="Rest Stop #5" src="http://lh5.ggpht.com/peter.stromquist.twistedstream/SEtSeNkVY4I/AAAAAAAAAC8/NOIKBv7sfdQ/MS1502008Saturday010_thumb3.jpg?imgmax=800" width="204" align="left" border="0" /&gt;&lt;/a&gt; &lt;a href="http://lh5.ggpht.com/peter.stromquist.twistedstream/SEtSewraUfI/AAAAAAAAADA/1ZdsFjCjn0U/s1600-h/MS1502008Saturday0116.jpg"&gt;&lt;img style="BORDER-TOP-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; BORDER-RIGHT-WIDTH: 0px" height="168" alt="It's Pete" src="http://lh3.ggpht.com/peter.stromquist.twistedstream/SEtSfPATMdI/AAAAAAAAADE/7QjAhVLxOD0/MS1502008Saturday011_thumb4.jpg?imgmax=800" width="201" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;&lt;p&gt;Our last run was the going to be the longest (15.6 miles).  Amazingly we all hit a major second wind!  At one point we were consistently holding 24 mph, all of taking our turn pulling, and still doing that with a head wind!&lt;/p&gt;&lt;p&gt;And finally, the finish:&lt;/p&gt;&lt;p&gt;&lt;a href="http://lh3.ggpht.com/peter.stromquist.twistedstream/SEtSfhN90sI/AAAAAAAAADI/AOzarAtUSEA/s1600-h/MS1502008Saturday0193.jpg"&gt;&lt;img style="BORDER-TOP-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; BORDER-RIGHT-WIDTH: 0px" height="161" alt="Hinckley finish line" src="http://lh5.ggpht.com/peter.stromquist.twistedstream/SEtSf4hWz6I/AAAAAAAAADM/K2cxXqKe5Vs/MS1502008Saturday019_thumb1.jpg?imgmax=800" width="410" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;&lt;p&gt;Overall we averaged about 19 mph and arrived in Hinckley at about 11:45am (I think I arrived at Hinckley last year about 3pm)!  And I kept pace the entire day!  I owe most of that to the awesome bike that my boss, Kerry, borrowed me for the event: A 2005 (I think) Bianchi Cross Concept.&lt;/p&gt;&lt;p&gt;Now it was time to check into the hotel, relax a bit, and check out the infamous Backdrafters tent...&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/35759427-2576249481142964486?l=beyondthispoint.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://beyondthispoint.blogspot.com/feeds/2576249481142964486/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=35759427&amp;postID=2576249481142964486' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/35759427/posts/default/2576249481142964486'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/35759427/posts/default/2576249481142964486'/><link rel='alternate' type='text/html' href='http://beyondthispoint.blogspot.com/2008/06/2008-ms150-saturday-ride.html' title='2008 MS150 - Saturday Ride'/><author><name>Pete</name><uri>http://www.blogger.com/profile/16688548320976910930</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://photos1.blogger.com/blogger2/1347/4365/240/gse_multipart43909.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh5.ggpht.com/peter.stromquist.twistedstream/SEtSYNlJOvI/AAAAAAAAACU/wHkFNUn4V-g/s72-c/MS1502008Saturday004_thumb2.jpg?imgmax=800' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-35759427.post-4795995637181961101</id><published>2008-06-07T05:45:00.000-05:00</published><updated>2008-06-07T22:36:15.031-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Biking'/><title type='text'>2008 MS150 - Friday</title><content type='html'>&lt;p&gt;&lt;a href="http://lh6.ggpht.com/peter.stromquist.twistedstream/SEtSQMW2mGI/AAAAAAAAABo/JOEFBEAiJ7c/s1600-h/0073.jpg"&gt;&lt;img style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="114" alt="007" src="http://lh5.ggpht.com/peter.stromquist.twistedstream/SEtSQu97h_I/AAAAAAAAABs/is_nys5KEOA/007_thumb1.jpg?imgmax=800" width="141" border="0" /&gt;&lt;/a&gt; &lt;a href="http://lh5.ggpht.com/peter.stromquist.twistedstream/SEtSRFY91MI/AAAAAAAAABw/YaLGMdbU0HQ/s1600-h/0083.jpg"&gt;&lt;img style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="119" alt="008" src="http://lh3.ggpht.com/peter.stromquist.twistedstream/SEtSRXgyROI/AAAAAAAAAB0/gjW3FuHmW8A/008_thumb1.jpg?imgmax=800" width="147" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;Loaded the bikes in our own rental truck, hopped on the bus, and made it up to Carlton, MN last night with the Backdrafters. &lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/peter.stromquist.twistedstream/SEtSR-uRD_I/AAAAAAAAAB4/lIE3-aA4iig/s1600-h/0132.jpg"&gt;&lt;img style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="196" alt="013" src="http://lh5.ggpht.com/peter.stromquist.twistedstream/SEtSSKtZHyI/AAAAAAAAAB8/9PY0EqAJAvw/013_thumb.jpg?imgmax=800" width="244" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;Spent the night in the Black Bear Casino Hotel, and now I sit in the hotel lobby with my laptop mentally gearing up for the first day of riding with a cup of coffee.&lt;/p&gt;  &lt;p&gt;Our team has a bit of a unique situation in that they're not starting in Proctor like the rest of the 3500 MS150 bikers. Doug, the team captain, wasn't able to book enough hotel rooms in Proctor for the team and so switches us over to Carlton, which is about 15 miles down the trail, making today's ride a little shorter than 75 miles (this is one reason why I chose to bike from home to National Sports Center yesterday, which was about 16 miles).&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/peter.stromquist.twistedstream/SEtTqInnucI/AAAAAAAAAD4/G5NiW33TB9c/s1600-h/009%5B3%5D.jpg"&gt;&lt;img style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" height="206" alt="009" src="http://lh4.ggpht.com/peter.stromquist.twistedstream/SEtSTayTu8I/AAAAAAAAAD8/Ust-odUFKI8/009_thumb%5B2%5D.jpg?imgmax=800" width="252" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;I've already met a lot of great people on the team. I met Doug (above) right away; he's a great guy. &lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/peter.stromquist.twistedstream/SEtSTulDXvI/AAAAAAAAAEA/MfCH8d4pAtQ/s1600-h/012%5B2%5D.jpg"&gt;&lt;img style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" height="103" alt="012" src="http://lh5.ggpht.com/peter.stromquist.twistedstream/SEtSUClpe0I/AAAAAAAAAEE/Yyqspps0tWY/012_thumb%5B1%5D.jpg?imgmax=800" width="125" align="right" border="0" /&gt;&lt;/a&gt;There was also someone practicing their accordion in the parking lot yesterday afternoon before we left Blaine (I'm assuming that's going to be used tonight in Hinckley for the festivities). &lt;/p&gt;  &lt;p&gt;I also met my roommates: Landin (our room captain), Cory, John, and Dillan. The cool thing is that it sounds like all of us want to try to ride together. Ladin is a pretty aggressive rider; he's done both the Minnesota Iron Man and the MS TRAM (Trek Across Minnesota). &lt;/p&gt;  &lt;p&gt;It should be a fun day of biking!&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/35759427-4795995637181961101?l=beyondthispoint.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://beyondthispoint.blogspot.com/feeds/4795995637181961101/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=35759427&amp;postID=4795995637181961101' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/35759427/posts/default/4795995637181961101'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/35759427/posts/default/4795995637181961101'/><link rel='alternate' type='text/html' href='http://beyondthispoint.blogspot.com/2008/06/2008-ms150-friday.html' title='2008 MS150 - Friday'/><author><name>Pete</name><uri>http://www.blogger.com/profile/16688548320976910930</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://photos1.blogger.com/blogger2/1347/4365/240/gse_multipart43909.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh5.ggpht.com/peter.stromquist.twistedstream/SEtSQu97h_I/AAAAAAAAABs/is_nys5KEOA/s72-c/007_thumb1.jpg?imgmax=800' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-35759427.post-8928632337996097230</id><published>2008-06-06T13:17:00.001-05:00</published><updated>2008-06-06T13:17:05.007-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Biking'/><title type='text'>Starting the 2008 MS150</title><content type='html'>&lt;p&gt;&lt;a href="http://lh6.ggpht.com/peter.stromquist.twistedstream/SEl_DocCZGI/AAAAAAAAABc/uKCMJ6Ht8-o/s1600-h/015%5B2%5D.jpg"&gt;&lt;img style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" height="196" alt="015" src="http://lh5.ggpht.com/peter.stromquist.twistedstream/SEl_G4wwu3I/AAAAAAAAABk/8B6kcneUe4I/015_thumb.jpg?imgmax=800" width="244" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Well, it's that time of year again!&amp;#160; Last year was so much fun I had to do it again.&amp;#160; This will be my second &lt;a href="http://bikemnm.nationalmssociety.org/site/PageServer?pagename=BIKE_MNM_TRAM_homepage&amp;amp;s_src=bikems"&gt;Minnesota MS150&lt;/a&gt; bike ride.&amp;#160; I took this photo this morning before I left the house just before I biked to the National Sports Center in Blaine where everything starts.&amp;#160; &lt;/p&gt;  &lt;p&gt;I've been fortunate enough to join a team this year (&lt;a href="http://photos.thehouseshome.net/gallery/2987670_rMAnA#161829259_CF7uR"&gt;Jeff&lt;/a&gt; wasn't able to attend due to his new kid!) called the &lt;a href="http://www.backdrafters.com/"&gt;Backdrafters&lt;/a&gt;.&amp;#160;&amp;#160; Instead of tenting I'm going to be doing the hotel thing Friday (tonight) and Saturday, which means I was able to pack a lot less stuff.&amp;#160; I got it all in that gray backpack.&lt;/p&gt;  &lt;p&gt;Well, I hope to do some actual blogging of the event over the weekend with some pictures from my phone.&amp;#160; Right now I'm sitting at a Caribou right next to the National Sports Center.&amp;#160; In about an hour I head over to get registered, meet up with my team (none of whom I've physically met yet), and hop on the bus for Duluth.&amp;#160; And then it's an early rise to start turnin' pedals (as Doug, the team leader, sez) on Saturday.&lt;/p&gt;  &lt;p&gt;And a huge thanks to everyone who's contributed to this event!&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/35759427-8928632337996097230?l=beyondthispoint.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://beyondthispoint.blogspot.com/feeds/8928632337996097230/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=35759427&amp;postID=8928632337996097230' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/35759427/posts/default/8928632337996097230'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/35759427/posts/default/8928632337996097230'/><link rel='alternate' type='text/html' href='http://beyondthispoint.blogspot.com/2008/06/starting-2008-ms150.html' title='Starting the 2008 MS150'/><author><name>Pete</name><uri>http://www.blogger.com/profile/16688548320976910930</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://photos1.blogger.com/blogger2/1347/4365/240/gse_multipart43909.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh5.ggpht.com/peter.stromquist.twistedstream/SEl_G4wwu3I/AAAAAAAAABk/8B6kcneUe4I/s72-c/015_thumb.jpg?imgmax=800' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-35759427.post-2301176786587202426</id><published>2008-05-12T22:37:00.001-05:00</published><updated>2008-05-12T22:42:40.145-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Technology'/><title type='text'>Upgraded to Vista</title><content type='html'>&lt;p&gt;This may come as a surprise to many people, but I've just now upgraded my main laptop from Windows XP to Windows Vista.&amp;#160; Why the wait?&amp;#160; Two things: First, I wanted to wait until SP1.&amp;#160; Second, I wanted to wait until I had more RAM.&lt;/p&gt;  &lt;p&gt;Regarding that second point, my laptop had 2GB RAM, which seems like plenty.&amp;#160; However, I was maxing it out pretty easily with XP when running a 1GB Virtual PC (which is how I do all of my software development).&amp;#160; So I upgraded to 3GB, which seems to give me just enough breathing room, even with Vista.&amp;#160; I would have gone to 4 (which my &lt;a href="http://www.notebookreview.com/default.asp?newsID=2767"&gt;ThinkPad T60&lt;/a&gt; supports), but discovered that the extra 1GB was pretty much a waste with 32-bit Vista given &lt;a href="http://www.vistaclues.com/reader-question-maximum-memory-in-32-bit-windows-vista/"&gt;issues with hardware memory mapping&lt;/a&gt;.&amp;#160; 64-bit Vista apparently doesn't suffer as bad from this issue, but, alas I do not have a 64-bit processor, yet.&lt;/p&gt;  &lt;p&gt;The good news is that on the same machine I was running XP Pro, Vista seems to perform just fine.&amp;#160; In fact, it seems noticeably &lt;em&gt;faster&lt;/em&gt; than XP was, which is a bit counter to popular opinion.&amp;#160; Of course a clean install of any OS always seems to run fast.&amp;#160; We'll see how things go.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/35759427-2301176786587202426?l=beyondthispoint.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://beyondthispoint.blogspot.com/feeds/2301176786587202426/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=35759427&amp;postID=2301176786587202426' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/35759427/posts/default/2301176786587202426'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/35759427/posts/default/2301176786587202426'/><link rel='alternate' type='text/html' href='http://beyondthispoint.blogspot.com/2008/05/upgraded-to-vista.html' title='Upgraded to Vista'/><author><name>Pete</name><uri>http://www.blogger.com/profile/16688548320976910930</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://photos1.blogger.com/blogger2/1347/4365/240/gse_multipart43909.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-35759427.post-1414318904857277675</id><published>2008-04-14T22:52:00.002-05:00</published><updated>2008-04-14T22:56:43.154-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Software'/><title type='text'>Code Camp Presentation Posted - Tapping the Power of LINQ</title><content type='html'>Well, I had the opportunity to present again at the &lt;a href="http://www.twincitiescodecamp.com/TCCC/Default.aspx"&gt;Twin Cities Code Camp&lt;/a&gt;, this time #4, which was for Spring 2008.  I did a general talk on LINQ including LINQ to Objects, LINQ to XML, and LINQ to SQL. &lt;br /&gt;&lt;br /&gt;Anyway, thanks again to &lt;a href="http://jasonbock.net/"&gt;Jason Bock&lt;/a&gt; of &lt;a href="http://magenic.com/"&gt;Magenic Technologies&lt;/a&gt; for organization this event.  From what I heard, we had a record turn out!&lt;br /&gt;&lt;br /&gt;If you want to download the presentation and/or check out the sample code, you can do so &lt;a href="http://www.twistedstream.com/stuff/pete/Tapping-the-Power-of-LINQ.zip"&gt;here&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/35759427-1414318904857277675?l=beyondthispoint.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://beyondthispoint.blogspot.com/feeds/1414318904857277675/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=35759427&amp;postID=1414318904857277675' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/35759427/posts/default/1414318904857277675'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/35759427/posts/default/1414318904857277675'/><link rel='alternate' type='text/html' href='http://beyondthispoint.blogspot.com/2008/04/code-camp-presentation-posted-tapping.html' title='Code Camp Presentation Posted - Tapping the Power of LINQ'/><author><name>Pete</name><uri>http://www.blogger.com/profile/16688548320976910930</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://photos1.blogger.com/blogger2/1347/4365/240/gse_multipart43909.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-35759427.post-4839385273046094494</id><published>2008-04-10T14:01:00.006-05:00</published><updated>2008-04-10T14:14:39.221-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Software'/><title type='text'>Managing Unhandled Exceptions in ASP.NET Web Services</title><content type='html'>We've been working on building a web services at my current project at work for quite some time now and we're finally wrapping things up. One of the items to unfortunately get addressed toward the end of the project was error handling and logging (doesn't it always). And to my dismay the error handling scenario for ASP.NET Web Services (even in .NET 3.5) is not as pretty as it is with a standard ASP.NET Web Forms website.&lt;br /&gt;&lt;br /&gt;Specifically, you can't just add a few lines of code to the Global.aspx.cs file in the Application_Error event handler method to log unhandled exceptions because the ASP.NET Web Services pipeline bypasses this event so it can return all the exceptions back to the caller. It does this by wrapping the unhandled exception in a &lt;a href="http://msdn2.microsoft.com/en-us/library/system.web.services.protocols.soapexception.aspx"&gt;SoapException&lt;/a&gt;, which ultimately gets deserialized as a SOAP fault. Out of the box, there is very little you can do with these unhandled exceptions before they are sent to the client. For example, there is no quick and easy way to log them like you can with .aspx pages.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style="font-size:130%;"&gt;The SoapExtension Solution&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;The most popular approach to solving this problem (that I've found so far) is to code up a custom &lt;a href="http://msdn2.microsoft.com/en-us/library/system.web.services.protocols.soapextension.aspx"&gt;SoapExtension&lt;/a&gt; subclass, which privodes you low-level access to the &lt;a href="http://msdn2.microsoft.com/en-us/library/system.web.services.protocols.soapmessage.aspx"&gt;SoapMessage&lt;/a&gt; at different stages of the web service request and response. When the unhandled exception occurs, the SoapException can be captured in the response SoapMessage (via the &lt;a href="http://msdn2.microsoft.com/en-us/library/system.web.services.protocols.soapmessage.exception.aspx"&gt;Exception&lt;/a&gt; property). The SoapException's InnerException property then contains the actual unhandled exception. At this point you can at log the exception on its way out the door so when you get that email from your tech support department, you've got something to go on.&lt;br /&gt;&lt;br /&gt;But if you've ever developed a real public-facing web service (like ours is), you'll quickly agree that simply logging the exception isn't enough. Often times you want a little more control over the contents of the SoapException itself that gets sent back to the caller. The SoapExtension subclass approach gives you the ability to modify the SoapException, but at a very low level. Specifically, it's already been deserialized into its representative SOAP fault XML and you have to do some fancy stream manipulation to insert any custom content into that fault (ex: add a detail element). In my opinion, this is a hack and not a very ellegant solution. Instead we should have more control of the SoapException &lt;em&gt;before&lt;/em&gt; it gets thrown. Ideally if we could create and throw the SoapException ourselves, we could have much more control over the contents of the resuling SOAP fault (like the fault code and detail). Then we wouldn't have to bother with intercepting and manipulating the raw SOAP messages themselves.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style="font-size:130%;"&gt;Client-Caused vs. Server-Caused Errors&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;A great example of this is when you want to return a client-caused SOAP fault vs. a server-caused SOAP fault. A client-caused SOAP fault is one caused because the client sent invalid data in their request. For example, they're not authenticated or they sent a null for a value of a required web method parameter or data transfer object (DTO) property. The SOAP fault infrastructure allows you to classify this kind of error by throwing a SOAP fault with a Code = Client (or Sender in SOAP 1.2). It's also helpful to send information in the detail &lt;detail&gt;section of the SOAP fault about what is invalid and how to fix it. It's up to the client to then fix their request before sending it again; otherwise, they'll get another client-caused SOAP fault.&lt;br /&gt;&lt;br /&gt;A server-caused SOAP fault is one where something actually bad happenned on the server. In this case the client is really helpless to fix the issue. Operations or an engineer on the development team needs to determine the issue and fix it on the server side. When this kind of error occurs, you want very little detail to be sent to the client, certainly not a full stack trace! Rather you want a simple error message like "An unexpected error has occurred on the server." Optionally you could also send some kind of error identifier, which the client could use when communicating with customer support so that the error can easily be referened in the server logs. Like the client-caused SOAP fault, the server-caused SOAP fault has a special fault Code = Server (or Receiver in SOAP1.2).&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style="font-size:130%;"&gt;The Try/Catch Solution&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;The best way to implement catching all exceptions and throwing your own SoapException is with a try/catch around the code that can cause the unhandled exception, which essentially is every web method in your web service. Unfortunately, since there's no single place in code you can do this, you're faced with putting try/catch statements around the logic of &lt;em&gt;every&lt;/em&gt; single web method. The code for one particular web method might look like this:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre style="BORDER-RIGHT: #999999 1px dashed; PADDING-RIGHT: 5px; BORDER-TOP: #999999 1px dashed; PADDING-LEFT: 5px; FONT-SIZE: 12px; PADDING-BOTTOM: 5px; OVERFLOW: auto; BORDER-LEFT: #999999 1px dashed; WIDTH: 100%; COLOR: #000000; LINE-HEIGHT: 14px; PADDING-TOP: 5px; BORDER-BOTTOM: #999999 1px dashed; FONT-FAMILY: Andale Mono, Lucida Console, Monaco, fixed, monospace; BACKGROUND-COLOR: #eee"&gt;&lt;code&gt;[WebMethod]&lt;br /&gt;public string WebMethod1(int arg1, int arg2)&lt;br /&gt;{&lt;br /&gt;  try&lt;br /&gt;  {&lt;br /&gt;    //web method logic&lt;br /&gt;  }&lt;br /&gt;  catch (ExceptionType1 ex)&lt;br /&gt;  {&lt;br /&gt;    //handle ExceptionType1&lt;br /&gt;  }&lt;br /&gt;  catch (ExceptionType2 ex)&lt;br /&gt;  {&lt;br /&gt;    //handle ExceptionType2&lt;br /&gt;  }&lt;br /&gt;  catch (Exception ex)&lt;br /&gt;  {&lt;br /&gt;    //handle all other exceptions&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;While this works, it's a ton of duplicated code, especially all the logic in the catch statements. Wouldn't it be nice if there was a way to add a little bit of code to each web method but put the bulk of the error handling logic in a single method somewhere else?&lt;br /&gt;&lt;br /&gt;Well, there is!&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style="font-size:130%;"&gt;The Try/Catch Using Anonymous Methods Solution&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;The trick is to put all of your even handling logic (including the try/catch) into a separate utility method and then invoke that method by passing it a &lt;a href="http://msdn2.microsoft.com/en-us/library/ms173171.aspx"&gt;delegate&lt;/a&gt; to your web method logic. This utility method might look something like this:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre style="BORDER-RIGHT: #999999 1px dashed; PADDING-RIGHT: 5px; BORDER-TOP: #999999 1px dashed; PADDING-LEFT: 5px; FONT-SIZE: 12px; PADDING-BOTTOM: 5px; OVERFLOW: auto; BORDER-LEFT: #999999 1px dashed; WIDTH: 100%; COLOR: #000000; LINE-HEIGHT: 14px; PADDING-TOP: 5px; BORDER-BOTTOM: #999999 1px dashed; FONT-FAMILY: Andale Mono, Lucida Console, Monaco, fixed, monospace; BACKGROUND-COLOR: #eee"&gt;&lt;code&gt;private T Execute&amp;lt;T&amp;gt;(Func&amp;lt;T&amp;gt; body)&lt;br /&gt;{&lt;br /&gt;    //wrap everything in common try/catch&lt;br /&gt;    try&lt;br /&gt;    {&lt;br /&gt;        return body();&lt;br /&gt;    }&lt;br /&gt;    catch (SoapException)&lt;br /&gt;    {&lt;br /&gt;        //rethrow any pre-generated SOAP faults&lt;br /&gt;        throw;&lt;br /&gt;    }&lt;br /&gt;    catch (ValidationException ex)&lt;br /&gt;    {&lt;br /&gt;        //validation error caused by client&lt;br /&gt;        ClientError innerError = new ClientError();&lt;br /&gt;        //TODO: populate client error as needed&lt;br /&gt;        //throw SOAP fault&lt;br /&gt;        throw this.GenerateSoapException(&lt;br /&gt;            "An error occurred while validating the client request.",&lt;br /&gt;            SoapException.ClientFaultCode,&lt;br /&gt;            innerError);&lt;br /&gt;    }&lt;br /&gt;    catch (Exception ex)&lt;br /&gt;    {&lt;br /&gt;        //everything else is treated as an error caused by server&lt;br /&gt;        ServerError innerError = new ServerError();&lt;br /&gt;        //TODO: populate server error as needed&lt;br /&gt;        //TODO: log error&lt;br /&gt;        //throw SOAP fault&lt;br /&gt;        throw this.GenerateSoapException(&lt;br /&gt;            "An unexpected error occurred on the server.",&lt;br /&gt;            SoapException.ServerFaultCode,&lt;br /&gt;            innerError);&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;A few things to note about the Execute method above:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;The delegate type being passed in is the generic &lt;a href="http://msdn2.microsoft.com/en-us/library/bb534960.aspx"&gt;Func&amp;lt;TResult&amp;gt;&lt;/a&gt; delegate, which is new to .NET 3.5 (Visual Studio 2008/C# 3.0). It works with any method that has no arguments and returns a value of the generic type TResult. If you're coding this in .NET 2.0 you could simply declare your own delegate that has the same signature like this&lt;/li&gt;&lt;/ul&gt;&lt;pre style="BORDER-RIGHT: #999999 1px dashed; PADDING-RIGHT: 5px; BORDER-TOP: #999999 1px dashed; PADDING-LEFT: 5px; FONT-SIZE: 12px; PADDING-BOTTOM: 5px; OVERFLOW: auto; BORDER-LEFT: #999999 1px dashed; WIDTH: 100%; COLOR: #000000; LINE-HEIGHT: 14px; PADDING-TOP: 5px; BORDER-BOTTOM: #999999 1px dashed; FONT-FAMILY: Andale Mono, Lucida Console, Monaco, fixed, monospace; BACKGROUND-COLOR: #eee"&gt;&lt;code&gt;private delegate TResult Func&amp;lt;TResult&amp;gt;();&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;The same generic type is also specific in the Execute method and is also the return type of the Execute method. And since we execute the delegate and return its result, everything is done in a strongly-typed fashion with no casting. It's even more elegant since the caller doesn't have to explicitly specify the type T since it can be derrived from the actual parameters of the method this is passed (which we'll see in a moment).&lt;/li&gt;&lt;li&gt;The error-handling logic expcitly catches and rethrows SoapExceptions. The assumption here is that if something lower down threw an actual SoapException, they probably want that exception to drive the resulting SOAP fault generated by ASP.NET instead of the one we're going to create (in the catch statements that follow).&lt;/li&gt;&lt;li&gt;The error-handling logic explicitly catches an exception type that represents all client-caused errors (in this case a made-up exception class called ValidationException). If there are multiple exception types that represent this, then you'd have multiple catches.&lt;/li&gt;&lt;li&gt;The final catch (Exception) is a catch-all for everything else, which is, by definition, an unexpected server error. It has a TODO placeholder for logging the error.&lt;/li&gt;&lt;li&gt;The last two catch statements employ another utility method that generates the SoapException itself (which I will show shortly). This method uses XML serialization to serialize a custom error object DTO and put it in the SoapException Detail.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;So, what would a web method look like that's using this Execute method? Back to our first web method example:&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;pre style="BORDER-RIGHT: #999999 1px dashed; PADDING-RIGHT: 5px; BORDER-TOP: #999999 1px dashed; PADDING-LEFT: 5px; FONT-SIZE: 12px; PADDING-BOTTOM: 5px; OVERFLOW: auto; BORDER-LEFT: #999999 1px dashed; WIDTH: 100%; COLOR: #000000; LINE-HEIGHT: 14px; PADDING-TOP: 5px; BORDER-BOTTOM: #999999 1px dashed; FONT-FAMILY: Andale Mono, Lucida Console, Monaco, fixed, monospace; BACKGROUND-COLOR: #eee"&gt;&lt;code&gt;[WebMethod]&lt;br /&gt;public string WebMethod1(int agr1, arg2)&lt;br /&gt;{&lt;br /&gt;    return this.Execute(delegate&lt;br /&gt;    {&lt;br /&gt;        string result;&lt;br /&gt;        //do something with arg1 and arg2 and generate a string result&lt;br /&gt;        return result;&lt;br /&gt;    });&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Notice how by putting the web method logic within an &lt;a href="http://msdn2.microsoft.com/en-us/library/0yw3tz5k.aspx"&gt;anonymous method&lt;/a&gt;, the call to the Execute method is nothing more than a thin wrapper. Also notice that we don't have to call the Execute method with a string type parameter (i.e. Execute&lt;string&gt;(...)) because it's inferred from the return type of the anonymous method.&lt;br /&gt;&lt;br /&gt;The beauty is that now you've got a single place where you can do all of your plumbing-related coding. Error handling is one thing you can do (which was the original focus of this blog post), but you can also do logging, add instrumentation code, or whatever, all in one spot!&lt;br /&gt;&lt;br /&gt;And here's the handy code for the GenerateSoapException utility method referenced before:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre style="BORDER-RIGHT: #999999 1px dashed; PADDING-RIGHT: 5px; BORDER-TOP: #999999 1px dashed; PADDING-LEFT: 5px; FONT-SIZE: 12px; PADDING-BOTTOM: 5px; OVERFLOW: auto; BORDER-LEFT: #999999 1px dashed; WIDTH: 100%; COLOR: #000000; LINE-HEIGHT: 14px; PADDING-TOP: 5px; BORDER-BOTTOM: #999999 1px dashed; FONT-FAMILY: Andale Mono, Lucida Console, Monaco, fixed, monospace; BACKGROUND-COLOR: #eee"&gt;&lt;code&gt;private SoapException GenerateSoapException(string message, XmlQualifiedName faultCode, object detailError)&lt;br /&gt;{&lt;br /&gt;    using (Stream stream = new MemoryStream())&lt;br /&gt;    {&lt;br /&gt;        using (XmlWriter writer = new XmlTextWriter(stream, Encoding.Default))&lt;br /&gt;        {&lt;br /&gt;            //build detail XML by serializing detailError object&lt;br /&gt;            XmlSerializer serializer = new XmlSerializer(detailError.GetType(), "your-webservice-namespace-here");&lt;br /&gt;            serializer.Serialize(writer, detailError);&lt;br /&gt;            stream.Position = 0;&lt;br /&gt;            XmlDocument doc = new XmlDocument();&lt;br /&gt;            XmlElement detail = this.SoapVersion == SoapProtocolVersion.Soap12&lt;br /&gt;                ? doc.CreateElement("soap", "Detail", "http://www.w3.org/2003/05/soap-envelope")&lt;br /&gt;                : doc.CreateElement("detail");&lt;br /&gt;            doc.Load(stream);&lt;br /&gt;            detail.AppendChild(doc.DocumentElement);&lt;br /&gt;            //build exception&lt;br /&gt;            return new SoapException(&lt;br /&gt;                message,&lt;br /&gt;                faultCode,&lt;br /&gt;                this.Context.Request.Url.AbsoluteUri,&lt;br /&gt;                detail);&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;Enjoy!&lt;br /&gt;&lt;br /&gt;~pete&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/35759427-4839385273046094494?l=beyondthispoint.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://beyondthispoint.blogspot.com/feeds/4839385273046094494/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=35759427&amp;postID=4839385273046094494' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/35759427/posts/default/4839385273046094494'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/35759427/posts/default/4839385273046094494'/><link rel='alternate' type='text/html' href='http://beyondthispoint.blogspot.com/2008/04/managing-unhandled-exceptions-in-aspnet.html' title='Managing Unhandled Exceptions in ASP.NET Web Services'/><author><name>Pete</name><uri>http://www.blogger.com/profile/16688548320976910930</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://photos1.blogger.com/blogger2/1347/4365/240/gse_multipart43909.jpg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-35759427.post-3947645889096766142</id><published>2007-10-31T20:28:00.000-05:00</published><updated>2007-11-03T00:52:26.128-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Software'/><title type='text'>Code Camp Presentation Posted - LINQ to XML</title><content type='html'>&lt;a href="http://www.twincitiescodecamp.com/"&gt;Twin Cities Code Camp&lt;/a&gt; 3 was a blast and I had a lot of fun presenting this time on LINQ to XML. Hats off to &lt;a href="http://jasonbock.net/"&gt;Jason Bock&lt;/a&gt; who did a &lt;em&gt;fantastic&lt;/em&gt; job pulling all this together (as did everyone else who helped out). All the presenters were first class, many of them Microsoft MVP's. It was an honor working along side them.&lt;br /&gt;&lt;br /&gt;If you missed my talk or just have a general interest in LINQ or LINQ to XML, feel free to download my &lt;a href="http://www.twistedstream.com/stuff/pete/LINQ-to-XML.zip"&gt;Powerpoint presentation&lt;/a&gt;. If you have any questions, shoot me an email at: peter at twistedstream dot com.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/35759427-3947645889096766142?l=beyondthispoint.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://beyondthispoint.blogspot.com/feeds/3947645889096766142/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=35759427&amp;postID=3947645889096766142' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/35759427/posts/default/3947645889096766142'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/35759427/posts/default/3947645889096766142'/><link rel='alternate' type='text/html' href='http://beyondthispoint.blogspot.com/2007/10/code-camp-presentation-posted-linq-to.html' title='Code Camp Presentation Posted - LINQ to XML'/><author><name>Pete</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://photos1.blogger.com/blogger2/1347/4365/240/gse_multipart43909.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-35759427.post-1459139439488087396</id><published>2007-10-08T09:52:00.000-05:00</published><updated>2007-10-08T10:07:03.683-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Software'/><title type='text'>Fall 2007 Code Camp</title><content type='html'>One of the more recent really cool tech events to hit the Twin Cities is the &lt;a href="http://www.twincitiescodecamp.com/TCCC/Default.aspx"&gt;Twin Cities Code Camp&lt;/a&gt;.  They happen usually every 6 months and #3 is occurring at the end of the month on October 27th.  Code Camp is organized by &lt;a href="http://jasonbock.net/"&gt;Jason Bock&lt;/a&gt;, a Microsoft MVP and a good friend of mine from &lt;a href="http://magenic.com/"&gt;Magenic Technologies&lt;/a&gt;.  It is sponsored by a number of area technology businesses including Magenic, &lt;a href="http://microsoft.com/"&gt;Microsoft&lt;/a&gt; and &lt;a href="http://www.newhorizonsmn.com/"&gt;New Horizons&lt;/a&gt;, which is where its held.&lt;br /&gt;&lt;br /&gt;The event is &lt;em&gt;free &lt;/em&gt;to attend (plus they feed you breakfast and lunch).  You get to choose from a number of sessions where you can learn about new technologies and chum with a lot of really smart people.&lt;br /&gt;&lt;br /&gt;Also, I've been given the privilege to present one of these sessions.  I'll be talking about &lt;a href="http://www.twincitiescodecamp.com/TCCC/Fall2007/Sessions.aspx#s16"&gt;LINQ to XML&lt;/a&gt;, Microsoft's new XML API.  If you're interested in LINQ (which you really should be since its the main feature add for the next version of .NET), there are two others sessions before mine that talk about LINQ to SQL and LINQ to objects.  Very cool stuff!  Check out &lt;a href="http://www.twincitiescodecamp.com/TCCC/Fall2007/Schedule.aspx"&gt;the schedule&lt;/a&gt; to see all the topics.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/35759427-1459139439488087396?l=beyondthispoint.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://beyondthispoint.blogspot.com/feeds/1459139439488087396/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=35759427&amp;postID=1459139439488087396' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/35759427/posts/default/1459139439488087396'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/35759427/posts/default/1459139439488087396'/><link rel='alternate' type='text/html' href='http://beyondthispoint.blogspot.com/2007/10/fall-2007-code-camp.html' title='Fall 2007 Code Camp'/><author><name>Pete</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://photos1.blogger.com/blogger2/1347/4365/240/gse_multipart43909.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-35759427.post-5113671173994732159</id><published>2007-07-19T10:02:00.000-05:00</published><updated>2007-07-19T10:56:35.530-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Software'/><title type='text'>Google Maps API Weirdness in IE6</title><content type='html'>For the past few months I've integrating the &lt;a href="http://www.google.com/apis/maps/"&gt;Google Maps API&lt;/a&gt; into an existing website. Last month we finally released the new version of the site and everything seemed to be working fine, except in IE6.&lt;br /&gt;&lt;br /&gt;Specifically, the map marker icons (you know, the little red teardrops) were not rendering properly. Here's how they looked in IE7 and Firefox:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://1.bp.blogspot.com/_CxKLIV-Bw2U/Rp9_2KpDTwI/AAAAAAAAABo/-FYKvGcSI3I/s1600-h/good_icon.jpg"&gt;&lt;img id="BLOGGER_PHOTO_ID_5088926672498151170" style="CURSOR: hand" alt="" src="http://1.bp.blogspot.com/_CxKLIV-Bw2U/Rp9_2KpDTwI/AAAAAAAAABo/-FYKvGcSI3I/s200/good_icon.jpg" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;and here's how they looked in IE6:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://1.bp.blogspot.com/_CxKLIV-Bw2U/Rp9_vKpDTvI/AAAAAAAAABg/bN0Vf6DUiDo/s1600-h/bad_icon.jpg"&gt;&lt;img id="BLOGGER_PHOTO_ID_5088926552239066866" style="CURSOR: hand" alt="" src="http://1.bp.blogspot.com/_CxKLIV-Bw2U/Rp9_vKpDTvI/AAAAAAAAABg/bN0Vf6DUiDo/s200/bad_icon.jpg" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Yuck! Now, it's a well-known fact that IE6 can't render PNG files properly (specifically if they have any transparency to them, which these icons do). However, the Google Maps API is aware of this and &lt;a href="http://support.microsoft.com/kb/294714"&gt;employs the AlphaImageLoader filter&lt;/a&gt; to compensate. Go to &lt;a href="http://maps.google.com/"&gt;http://maps.google.com/&lt;/a&gt; on IE6 and the icons look just fine.&lt;br /&gt;&lt;br /&gt;So why were my icons turning out so crumby?&lt;br /&gt;&lt;br /&gt;Well, my scenario was a bit more complex than all the standard 'hello world' samples out there. First, I was using &lt;a href="http://www.econym.demon.co.uk/googlemaps/custom.htm"&gt;custom icons&lt;/a&gt;. But on top of that I wasn't simply slapping some static JavaScript code on my page to display the Google Maps API objects. Rather, I was generating all of the JavaScript code dynamically in my .aspx page on the server side. However, the final HTML output of my page (specifically the JavaScript code rendering the &lt;a href="http://www.google.com/apis/maps/documentation/reference.html#GIcon"&gt;GIcon&lt;/a&gt; objects) looked right.&lt;br /&gt;&lt;br /&gt;Except for one difference. Like I said, I was using custom icons. Just about all of my custom icons were the same shape; they really only differed in color, which meant the only real difference was the GIcon.image property (all the other properties were pretty much identical). In my inifinite wisdome I thought it would be clever to consolidate all of the other image URL properties associated with each GIcon (shadow, transparent, etc) and I would do this by storing those images as &lt;a href="http://aspnet.4guysfromrolla.com/articles/080906-1.aspx"&gt;embedded resources within my assembly&lt;/a&gt; and referencing them via WebResource.axd.&lt;br /&gt;&lt;br /&gt;The end result was that instead of the GIcon JavaScript code looking like this:&lt;br /&gt;&lt;blockquote&gt;&lt;span style="font-family:courier new;"&gt;icon.shadow = "my-shadow.png";&lt;/span&gt; &lt;/blockquote&gt;it looked like this:&lt;br /&gt;&lt;blockquote&gt;&lt;span style="font-family:courier new;"&gt;icon.shadow =&lt;br /&gt;"/WebResource.axd?d=&lt;em&gt;some-long-key&lt;/em&gt;&amp;t=&lt;em&gt;timestamp&lt;/em&gt;";&lt;/span&gt; &lt;/blockquote&gt;And what was tripping up IE6 was the fact that the image URL wasn't ending in ".png". Really what was being tripped up was the Google Maps API JavaScript code running in IE6. Perhaps the version of their code that runs in IE7 is able to automatically detect PNG files without requiring a .png file extension. Who knows.  But the bottom line is that the image URL must end in a .png file extension, ruling out any standard dynamic image URL resource (like WebResource.axd).&lt;br /&gt;&lt;br /&gt;This took me a fair amount of effort to solve. Hopefully, I'll save someone else the time.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/35759427-5113671173994732159?l=beyondthispoint.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://beyondthispoint.blogspot.com/feeds/5113671173994732159/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=35759427&amp;postID=5113671173994732159' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/35759427/posts/default/5113671173994732159'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/35759427/posts/default/5113671173994732159'/><link rel='alternate' type='text/html' href='http://beyondthispoint.blogspot.com/2007/07/google-maps-api-weirdness-in-ie6.html' title='Google Maps API Weirdness in IE6'/><author><name>Pete</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://photos1.blogger.com/blogger2/1347/4365/240/gse_multipart43909.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_CxKLIV-Bw2U/Rp9_2KpDTwI/AAAAAAAAABo/-FYKvGcSI3I/s72-c/good_icon.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-35759427.post-3182455576248059851</id><published>2007-06-11T10:00:00.000-05:00</published><updated>2007-07-19T10:00:21.762-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Biking'/><title type='text'>First MS150 a Success</title><content type='html'>&lt;a href="http://2.bp.blogspot.com/_CxKLIV-Bw2U/Rp96IapDTuI/AAAAAAAAABY/sxpDgJr9CEQ/s1600-h/161828398_IMG_2774.jpg"&gt;&lt;img id="BLOGGER_PHOTO_ID_5088920388960997090" style="FLOAT: left; MARGIN: 0px 10px 10px 0px; CURSOR: hand" alt="" src="http://2.bp.blogspot.com/_CxKLIV-Bw2U/Rp96IapDTuI/AAAAAAAAABY/sxpDgJr9CEQ/s320/161828398_IMG_2774.jpg" border="0" /&gt;&lt;/a&gt;I've had way too busy of a summer. So much so that one of the biggest events, the MS150, came and went, and I never even bothered blogging about it.  Well, here it is, better late than never.&lt;br /&gt;&lt;br /&gt;In short: we (&lt;a href="http://jeffcantspell.blogspot.com/"&gt;Jeff&lt;/a&gt; and I) had a blast.  150 miles of fun... and soreness.  My butt got pretty sore and at the final 10 miles of the tour my left knee was giving me problems.  Jeff had been drafting behind me most of the way and, at the end, was able to take the lead, which was nice.  Jeff had weird gloves and one of his hands was numb for about 3 weeks afterward!&lt;br /&gt;&lt;br /&gt;But the benefits definitely outweighed the costs!  I &lt;em&gt;will &lt;/em&gt;do this event next year.&lt;br /&gt;&lt;br /&gt;Here were some lessons learned:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Stretching is very important (lack of is what caused my knee injury).&lt;/li&gt;&lt;li&gt;Get on the trail &lt;span class="blsp-spelling-corrected" id="SPELLING_ERROR_0"&gt;earlier&lt;/span&gt; in the morning next year to beat the heat.&lt;/li&gt;&lt;li&gt;Get more power with higher &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_1"&gt;RPM's&lt;/span&gt; instead pedalling harder; this is much easier on your joints.&lt;/li&gt;&lt;li&gt;Hang with a group to take advantage of drafting.  I would struggle to maintain 15mph (with a head wind), but it a group could do 18mph with ease.&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/35759427-3182455576248059851?l=beyondthispoint.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://beyondthispoint.blogspot.com/feeds/3182455576248059851/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=35759427&amp;postID=3182455576248059851' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/35759427/posts/default/3182455576248059851'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/35759427/posts/default/3182455576248059851'/><link rel='alternate' type='text/html' href='http://beyondthispoint.blogspot.com/2007/06/first-ms150-success.html' title='First MS150 a Success'/><author><name>Pete</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://photos1.blogger.com/blogger2/1347/4365/240/gse_multipart43909.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_CxKLIV-Bw2U/Rp96IapDTuI/AAAAAAAAABY/sxpDgJr9CEQ/s72-c/161828398_IMG_2774.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-35759427.post-2598800788097249486</id><published>2007-05-22T23:14:00.000-05:00</published><updated>2007-05-22T23:15:39.851-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Software'/><title type='text'>No more room in my VHD</title><content type='html'>&lt;strong&gt;&lt;span style="color:#ff0000;"&gt;UPDATE #2:&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;&lt;span style="color:#000000;"&gt;Hold the phone. A reader just tried the procedure described in the &lt;strong&gt;&lt;span style="color:#ff0000;"&gt;UPDATE&lt;/span&gt;&lt;/strong&gt; section below and found out that when you finally go to convert from the fixed back to a dynamically expanding disk, you never get prompted for a new disk size. If this is true, this handy workaround is pretty much worthless. Has anyone else attempted this that could confirm? Yes, I'm too lazy to do it myself.&lt;/span&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style="color:#ff0000;"&gt;&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style="color:#ff0000;"&gt;UPDATE:&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;The information in the original post is pretty much obsolete, or perhaps more accurately put, incompetent. Thanks to a recent anonymous comment, all I had to do was convert the VHD to a fixed disk and then back to dynamically expanding. Read on if you'd like to learn how to do it the hard way.&lt;br /&gt;:)&lt;br /&gt;&lt;br /&gt;So, you may or may not know that I'm a huge fan and user of &lt;a href="http://www.microsoft.com/windows/virtualpc/default.mspx"&gt;Virtual PC&lt;/a&gt; and &lt;a href="http://www.microsoft.com/windowsserversystem/virtualserver/default.mspx"&gt;Virtual Server&lt;/a&gt;. For over two years, I've been using virtualization exclusively for software development. Sure, there's a slight performance hit, but you can't beet the flexibility you gain from having one (or more) isolated development environments. Before using Virtual PC, it was common for me to be wiping my laptop once every 4 to 6 months. Now, since I don't run any development tool software on my host OS, it's rare that I have to rebuild my host machine. And, of course, creating new virtual PC's is a snap.&lt;br /&gt;&lt;br /&gt;Virtual PC, Virtual Server, and VMWare all use the concept of virtual hard drives (or VHD's). These are nothing more than files on your physical host OS's drive that represent a virtual drive. They can be fixed in size (like a physical drive) or dynamically expand. The later option is handy in that such a VHD only takes up as much physical space as it needs, gradually growing over time. The benefit is it saves a lot on physical drive space, especially if you manage multiple virtual machines.&lt;br /&gt;&lt;br /&gt;When you create a virtually expanding VHD you have to specify its maximum size; for Virtual PC the default max size is 16MB. The kicker is when you're using that VHD and all of the sudden one day you run out of room. It's the same experience you have when you run out of room with a physical drive - you immediately try to free up space by deleting unnecessary files and uninstalling unused apps.&lt;br /&gt;&lt;br /&gt;However, I recently ran into a scenario where making more room wasn't enough. I was installing &lt;a href="http://msdn.microsoft.com/vstudio/support/vs2005sp1/default.aspx"&gt;Visual Studio 2005 Service Pack 1&lt;/a&gt;, which requires a ton of free disk space (over 2GB I think) and I couldn't make it happen. Since you can't change the max size of an existing VHD, I only had two options:&lt;br /&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Rebuild my virtual machine with a larger VHD.&lt;/li&gt;&lt;li&gt;Somehow transfer the contents of my existing VHD to a new empty VHD.&lt;/li&gt;&lt;/ol&gt;The second option was much more appealing since it would require minimal effort. I just needed a tool that could do it. Transferring or cloning a disk is more involved that simply copying files since you need to transfer boot record, partition, as well as other information.&lt;br /&gt;&lt;br /&gt;Doing a little digging, I found there are several utilities out there. Norton Ghost is one. The one I chose was &lt;a href="http://www.acronis.com/enterprise/products/ATISWin/"&gt;Acronis True Image Server&lt;/a&gt; (I installed the Server version since my OS was Windows Server 2003). Neither of these are free, so you have to either buy them or use their trial edition. The process I used was the following:&lt;br /&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Create a new empty dynamically expanding VHD (with a more desirable max size) and mount it to the virtual machine.&lt;/li&gt;&lt;li&gt;Install Acronis True Image Server on the virtual machine.&lt;/li&gt;&lt;li&gt;Start True Image and run the Clone Disk wizard.&lt;/li&gt;&lt;li&gt;Unmount the old VHD.&lt;/li&gt;&lt;li&gt;Mount the new VHD as drive 0.&lt;/li&gt;&lt;/ol&gt;I think there are free disk cloning utilities out there, but I'm not sure how trustworthy they are. Disk cloning isn't something you want to mess up.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style="color:#ff0000;"&gt;AN IMPORTANT NOTE:&lt;br /&gt;&lt;/span&gt;&lt;/strong&gt;Before you perform this operation, make sure you have plenty of room on the &lt;em&gt;physical &lt;/em&gt;drive that contains the new VHD. Running out of physical drive space as the new VHD is getting filled (and expanded) can cause the clone to fail. Learned that one the hard way :).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/35759427-2598800788097249486?l=beyondthispoint.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://beyondthispoint.blogspot.com/feeds/2598800788097249486/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=35759427&amp;postID=2598800788097249486' title='7 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/35759427/posts/default/2598800788097249486'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/35759427/posts/default/2598800788097249486'/><link rel='alternate' type='text/html' href='http://beyondthispoint.blogspot.com/2006/12/no-more-room-in-my-vhd.html' title='No more room in my VHD'/><author><name>Pete</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-35759427.post-3651358372046259668</id><published>2007-05-03T10:05:00.000-05:00</published><updated>2007-05-03T10:22:20.024-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Biking'/><title type='text'>Back in the saddle</title><content type='html'>It's been over two weeks since I've biked (to work)!!!  I've been slacking and am running out of excuses.  Most of it has to do with over-busy-ness.  Blah, blah, blah...&lt;br /&gt;&lt;br /&gt;Tomorrow I AM biking in and the rest of the blogging world can hold me to it. &lt;br /&gt;&lt;br /&gt;Looks like we're going to get &lt;a href="http://www.wunderground.com/cgi-bin/findweather/getForecast?query=55101&amp;hourly=1&amp;amp;yday=123&amp;weekday=Friday"&gt;some rain&lt;/a&gt; too.  Perfect.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/35759427-3651358372046259668?l=beyondthispoint.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://beyondthispoint.blogspot.com/feeds/3651358372046259668/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=35759427&amp;postID=3651358372046259668' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/35759427/posts/default/3651358372046259668'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/35759427/posts/default/3651358372046259668'/><link rel='alternate' type='text/html' href='http://beyondthispoint.blogspot.com/2007/05/back-in-saddle.html' title='Back in the saddle'/><author><name>Pete</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://photos1.blogger.com/blogger2/1347/4365/240/gse_multipart43909.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-35759427.post-3703939979020637969</id><published>2007-04-01T14:43:00.000-05:00</published><updated>2007-04-01T15:35:44.829-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Software'/><title type='text'>Converting the primary key type in a  SQL Server database from int to GUID</title><content type='html'>I maintain and develop a database for a web application that I've worked on for several years now.  The application and the database have evolved significantly, and as things got larger and more complex, I've had to employ more advanced techniques and tools to perform database updates from development to production.&lt;br /&gt;&lt;br /&gt;One of the "mistakes" I made when I originally designed the database was to use primary keys with the &lt;a href="http://msdn2.microsoft.com/en-us/library/aa933198(SQL.80).aspx"&gt;int&lt;/a&gt; data type (along with setting the primary key column as an identity column) for all of the tables.  I have nothing against int's or identity columns and, in fact, that simple, tried-and-true configuration has worked well for this application... except for one, unfortunately very painful side-effect.  When it comes time for me to push a major update from development to production, sorting out duplicate columns between both development and production database can be a real pain in the undercarriage. &lt;br /&gt;&lt;br /&gt;The reason for this is often times in development I'm added new rows to tables that exist in both development and production to test and/or enable a new feature.  If the primary key for that table is an int that is auto-generated by the database (since its an identity column) then there's a VERY good chance that same ID has been duplicated in a row in the production database.  Things get even more complicated when that primary key ID is referenced by other columns in other tables via foreign key relationships.  I tried a few production push sessions where I pretty much took the production database offline (which meant I pretty much took the website offline), pulled down the entire production database, integrated the development data into production by hand (I use &lt;a href="http://redgate.com/"&gt;RedGate&lt;/a&gt; products), and finally pushed the whole thing back up to production.  Not pretty, but it worked... until recently.  Within the last few months, for various reasons the production database has nearly tripled in size, making the whole pulling of the entire production database, and, worse yet, the pushing of it back up, ridiculously impractical.  My last push took over 12 hours!  Now, granted, I'm restricted by the limits of a cable Internet connection, but I came to the realization that I needed another solution.&lt;br /&gt;&lt;br /&gt;Well, the solution is simple: switch to GUID primary keys.  They never duplicate (in theory) so I can create rows in development that will never interfere with new rows in production.  Unfortunately the road to this solution is not so simple.  However, I was able to finally get there, so I thought I would share how I did it.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style="font-size:130%;"&gt;How to do the conversion&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;The main issue with a conversion of primary key columns from int/identity to GUID is the fact that you can't simply do a TABLE ALTER to remove the identity status of a column.  You actually have to drop the column and re-add it! &lt;br /&gt;&lt;br /&gt;Following are the steps I took.  In this example, we'll call the starting database &lt;em&gt;db&lt;/em&gt; and the final database &lt;em&gt;db_new-schema&lt;/em&gt;.&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Create &lt;em&gt;db_new-schema&lt;/em&gt; as an empty database with new schema (i.e. everything the same as the old schema except the primary and foreign key columns are all of type UNIQUEIDENTIFIER).&lt;/li&gt;&lt;li&gt;Create another empty database called &lt;em&gt;db_load&lt;/em&gt; whose schema is identical to &lt;em&gt;db&lt;/em&gt;'s except all the primary and foreign key columns are of type NVARCHAR(36) and contains only tables (views, stored procedures, functions are unnecessary).  Also, there should be no foreign key constraints between tables.&lt;/li&gt;&lt;li&gt;Use an ETL tool (like DTS or SSIS) to copy the raw table data from &lt;em&gt;db &lt;/em&gt;to &lt;em&gt;db_load&lt;/em&gt;.&lt;/li&gt;&lt;li&gt;Create a SQL script that uses a cursor to enumerate the rows of a given table in &lt;em&gt;db_load&lt;/em&gt;, updating each row's primary key with a new GUID value (using the &lt;a href="http://msdn2.microsoft.com/en-us/library/ms190348.aspx"&gt;NEWID&lt;/a&gt; function), and updates the rows in all foreign key tables with the same GUID value (selecting them using the old INT id).  This script needed to modified and run for each table in the database until all of them were updated.  In the end, all INT values were converted to new GUID's (although all primary and foreign key columns were still typed as VARCHAR).&lt;/li&gt;&lt;li&gt;Convert all primary and foreign key columns from VARCHAR to UNIQUEIDENTIFIER.&lt;/li&gt;&lt;li&gt;Use an ETL tool (or RedGate Data Compare) to load the &lt;em&gt;db_new-schema&lt;/em&gt; database with all the data in &lt;em&gt;db_load&lt;/em&gt;.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;Good luck!&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/35759427-3703939979020637969?l=beyondthispoint.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://beyondthispoint.blogspot.com/feeds/3703939979020637969/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=35759427&amp;postID=3703939979020637969' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/35759427/posts/default/3703939979020637969'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/35759427/posts/default/3703939979020637969'/><link rel='alternate' type='text/html' href='http://beyondthispoint.blogspot.com/2007/04/converting-primary-key-type-in-sql.html' title='Converting the primary key type in a  SQL Server database from int to GUID'/><author><name>Pete</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://photos1.blogger.com/blogger2/1347/4365/240/gse_multipart43909.jpg'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-35759427.post-3576306365784961331</id><published>2007-03-03T18:16:00.000-06:00</published><updated>2007-03-03T18:20:03.538-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Biking'/><title type='text'>MS150</title><content type='html'>This June I'm going to be riding in the MS150 Bike Tour, which is an anual fund raising event for &lt;a href="http://en.wikipedia.org/wiki/Multiple_sclerosis"&gt;multiple sclerosis&lt;/a&gt;.  It's 150 mile trek from Duluth to the Twin Cities that lasts 3 days (2 days of actual biking).&lt;br /&gt;&lt;br /&gt;Anyway, I'm very excited as this is the first real long-distance bike trip I've ever taken.  And it's for a good cause!&lt;br /&gt;&lt;br /&gt;If you'd like more information about the event or would like to donate, check out &lt;a href="http://main.nationalmssociety.org/site/TR?px=2995703&amp;pg=personal&amp;amp;fr_id=4840&amp;amp;s_tafId=7181"&gt;my personal MS150 page&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/35759427-3576306365784961331?l=beyondthispoint.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://beyondthispoint.blogspot.com/feeds/3576306365784961331/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=35759427&amp;postID=3576306365784961331' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/35759427/posts/default/3576306365784961331'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/35759427/posts/default/3576306365784961331'/><link rel='alternate' type='text/html' href='http://beyondthispoint.blogspot.com/2007/03/ms150.html' title='MS150'/><author><name>Pete</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://photos1.blogger.com/blogger2/1347/4365/240/gse_multipart43909.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-35759427.post-1518349067767920735</id><published>2007-02-25T17:03:00.000-06:00</published><updated>2007-02-25T17:19:00.885-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='General'/><title type='text'>(In the words of Kip) "That's what I'm talkin' about."</title><content type='html'>We got snow!&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_CxKLIV-Bw2U/ReIW8OTMh3I/AAAAAAAAAAM/yYO5eAXoQKo/s1600-h/IMG_1896.JPG"&gt;&lt;img style="margin: 0pt 10px 10px 0pt; float: left; cursor: pointer;" src="http://1.bp.blogspot.com/_CxKLIV-Bw2U/ReIW8OTMh3I/AAAAAAAAAAM/yYO5eAXoQKo/s320/IMG_1896.JPG" alt="" id="BLOGGER_PHOTO_ID_5035612557240207218" border="0" /&gt;&lt;/a&gt;For once the weather man didn't cry wolf.  I opened up my back door and here's what I saw: beautiful, white, fluffy snow!&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_CxKLIV-Bw2U/ReIX1uTMh4I/AAAAAAAAAAU/EhdwUUU2EgU/s1600-h/IMG_1899.JPG"&gt;&lt;img style="cursor: pointer;" src="http://3.bp.blogspot.com/_CxKLIV-Bw2U/ReIX1uTMh4I/AAAAAAAAAAU/EhdwUUU2EgU/s200/IMG_1899.JPG" alt="" id="BLOGGER_PHOTO_ID_5035613545082685314" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_CxKLIV-Bw2U/ReIYF-TMh5I/AAAAAAAAAAc/xuIc42E4HR8/s1600-h/IMG_1898.JPG"&gt;&lt;img style="cursor: pointer;" src="http://4.bp.blogspot.com/_CxKLIV-Bw2U/ReIYF-TMh5I/AAAAAAAAAAc/xuIc42E4HR8/s200/IMG_1898.JPG" alt="" id="BLOGGER_PHOTO_ID_5035613824255559570" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;It's been several years since we'd had a dump like this one.  I know where we live, we got at least 12" and we're due for more.&lt;br /&gt;&lt;br /&gt;And the coolest part is the kids and I built a snow hut.  I haven't built one of these since I was in Boy Scouts!&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_CxKLIV-Bw2U/ReIYpuTMh6I/AAAAAAAAAAk/MbT9aLOj3v8/s1600-h/IMG_1887.JPG"&gt;&lt;img style="cursor: pointer;" src="http://3.bp.blogspot.com/_CxKLIV-Bw2U/ReIYpuTMh6I/AAAAAAAAAAk/MbT9aLOj3v8/s320/IMG_1887.JPG" alt="" id="BLOGGER_PHOTO_ID_5035614438435882914" border="0" /&gt;&lt;/a&gt;  &lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_CxKLIV-Bw2U/ReIZGuTMh7I/AAAAAAAAAAs/DsqqgZS2BQ4/s1600-h/IMG_1890.JPG"&gt;&lt;img style="cursor: pointer;" src="http://3.bp.blogspot.com/_CxKLIV-Bw2U/ReIZGuTMh7I/AAAAAAAAAAs/DsqqgZS2BQ4/s320/IMG_1890.JPG" alt="" id="BLOGGER_PHOTO_ID_5035614936652089266" border="0" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/35759427-1518349067767920735?l=beyondthispoint.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://beyondthispoint.blogspot.com/feeds/1518349067767920735/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=35759427&amp;postID=1518349067767920735' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/35759427/posts/default/1518349067767920735'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/35759427/posts/default/1518349067767920735'/><link rel='alternate' type='text/html' href='http://beyondthispoint.blogspot.com/2007/02/in-words-of-kip-thats-what-im-talkin.html' title='(In the words of Kip) &quot;That&apos;s what I&apos;m talkin&apos; about.&quot;'/><author><name>Pete</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://photos1.blogger.com/blogger2/1347/4365/240/gse_multipart43909.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_CxKLIV-Bw2U/ReIW8OTMh3I/AAAAAAAAAAM/yYO5eAXoQKo/s72-c/IMG_1896.JPG' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-35759427.post-3954380070396649733</id><published>2007-01-24T09:08:00.000-06:00</published><updated>2007-01-24T21:38:07.585-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Faith'/><category scheme='http://www.blogger.com/atom/ns#' term='Politics'/><title type='text'>Radical Islam</title><content type='html'>I watched a very interesting documentary last night called &lt;a href="http://us.imdb.com/title/tt0487116/"&gt;Obsession: Radical Islam's War Against the West&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;I think since 9/11 everyone has been aware of a link between Islam &lt;span class="blsp-spelling-corrected" id="SPELLING_ERROR_0"&gt;Extremism&lt;/span&gt; and terrorism, but I'm not sure everyone is aware of the gravity of this movement. What I liked about the film was that just about &lt;span class="blsp-spelling-corrected" id="SPELLING_ERROR_1"&gt;everyone&lt;/span&gt; interviewed was a Muslim (one person was a former PLO agent). This diminished the notion that the movie was a biased message from non-Muslim groups (i.e. &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_2" onclick="BLOG_clickHandler(this)"&gt;Jedeo&lt;/span&gt;-Christian). And, of course, the first point made was that Islam &lt;span class="blsp-spelling-corrected" id="SPELLING_ERROR_3"&gt;Extremism&lt;/span&gt; is a view only held by a small percentage of practicing Muslims (something like 10 to 15%). However, that number is getting bigger as the "movement" is being very effective in recruiting non-&lt;span class="blsp-spelling-corrected" id="SPELLING_ERROR_4"&gt;Extremists&lt;/span&gt; to their cause - especially the youth.&lt;br /&gt;&lt;br /&gt;The scary thing is the end goal of Radical Islam: to take over the world (by force) and make everyone Islam, killing everyone who apposes. A lot of people, including myself, questioned why Al &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_5" onclick="BLOG_clickHandler(this)"&gt;Qaeda&lt;/span&gt; attacked the World Trade Center. Why do they hate us so much? Why were they willing to kill Americans, including Muslims? The answer, according to the film, is that they will kill anyone who apposes their mission of world domination. Right now, Western Civilization, is a huge target since it apposes this world view.&lt;br /&gt;&lt;br /&gt;The movie also drew a very interesting parallel between Islam &lt;span class="blsp-spelling-corrected" id="SPELLING_ERROR_6"&gt;Extremism&lt;/span&gt; and the Nazi movement in WWII. Hitler's end goal was similar: to take over the world and &lt;span class="blsp-spelling-corrected" id="SPELLING_ERROR_7"&gt;eradicate&lt;/span&gt; anyone who didn't fit his world view, his primary target being the Jews. An interesting point was made that the current Radical Islam movement is potentially more dangerous than the Nazi movement because the Nazis fought because Hitler told them to; Islam &lt;span class="blsp-spelling-corrected" id="SPELLING_ERROR_8"&gt;Extremists&lt;/span&gt; fight because they believe God is telling them too.&lt;br /&gt;&lt;br /&gt;Taking all this into account, puts a new spin on the War in Iraq, at least for me. Like most Americans right now, I'm getting weary of this conflict: the money being spent, the lives lost, &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_9" onclick="BLOG_clickHandler(this)"&gt;yada&lt;/span&gt; &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_10" onclick="BLOG_clickHandler(this)"&gt;yada&lt;/span&gt;. Up until the last few years, I've considered myself a conservative. I voted for Bush. However, these days its been tough to understand and support what he's doing. After all, wasn't it &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_11" onclick="BLOG_clickHandler(this)"&gt;Osama&lt;/span&gt; bin Laden who attacked us, not Iraq? There were no &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_12" onclick="BLOG_clickHandler(this)"&gt;WMD's&lt;/span&gt;. It just seems like we're there now because we opened a can of worms and can't get out.&lt;br /&gt;&lt;br /&gt;Well, if what this movie says is true, then there is some logic for being in Iraq. Assuming the US government realized the real threat of Islam &lt;span class="blsp-spelling-corrected" id="SPELLING_ERROR_13"&gt;Extremism&lt;/span&gt; (world take-over), they had two options: build US defenses and just wait for the movement to grow and attack us or be proactive and attempt to stabilize the region where most of the Islam &lt;span class="blsp-spelling-corrected" id="SPELLING_ERROR_14"&gt;Extremism&lt;/span&gt; was coming from. At the time we invaded Iraq, everyone, left and right was certain that &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_15" onclick="BLOG_clickHandler(this)"&gt;Saddam Hussein &lt;/span&gt;had &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_16" onclick="BLOG_clickHandler(this)"&gt;WMD's&lt;/span&gt;. And if he was in on this movement, there was a huge threat here. Well, he didn't (at least we didn't find any). So we finally took him out only to find that removing him destabilized the country even more. But given the threat of Radical Islam, did we have any choice? Had we just sat back and let the movement take over the rest of world, we would have had very little chance of effectively defending ourselves in the future. Tough call. I'm glad I'm not the President of the Unites States.&lt;br /&gt;&lt;br /&gt;As a Christian, I'm still not sure where I stand on the War in Iraq. I guess from a strategic standpoint, it makes sense. But as far as showing the love of Christ, I'm not so sure. War is never a black and white issue.&lt;br /&gt;&lt;br /&gt;A final point. About a month ago I watched Al Gore's &lt;a href="http://us.imdb.com/title/tt0497116/"&gt;An Inconvenient Truth&lt;/a&gt;. This movie openned my eyes as well. I find it interesting that both the left and right have a &lt;span class="blsp-spelling-corrected" id="SPELLING_ERROR_17"&gt;doomsday&lt;/span&gt; prophecy: the left thinks the polar ice caps will melt within 50 years and the right thinks a global &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_18" onclick="BLOG_clickHandler(this)"&gt;Jehad&lt;/span&gt; will take over the world. Both sides say we need to take action now to prevent the end of the world as we know it.&lt;br /&gt;&lt;br /&gt;Wouldn't it be ironic if both sides were true.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/35759427-3954380070396649733?l=beyondthispoint.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://beyondthispoint.blogspot.com/feeds/3954380070396649733/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=35759427&amp;postID=3954380070396649733' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/35759427/posts/default/3954380070396649733'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/35759427/posts/default/3954380070396649733'/><link rel='alternate' type='text/html' href='http://beyondthispoint.blogspot.com/2007/01/radical-islam.html' title='Radical Islam'/><author><name>Pete</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://photos1.blogger.com/blogger2/1347/4365/240/gse_multipart43909.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-35759427.post-1023376574472714951</id><published>2007-01-23T10:41:00.000-06:00</published><updated>2007-01-23T11:09:24.640-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Biking'/><title type='text'>January Bike Ride</title><content type='html'>This will be a true test to see if my wife reads my blog. &lt;br /&gt;&lt;br /&gt;I biked into work today.  In me defense, it wasn't totally planned.  The plan was to drop off my car at the shop and bike to the bus stop, which is only about .5 mile away.  However, the problem with the bus, I've found, is that you have to get to the bus stop on time.  So I missed the bus and my inspiring bus schedule said the next one would show up in about 50 minutes.  The decision was easy: wait 50 minutes at a cold bus stop or bike to work in 35.&lt;br /&gt;&lt;br /&gt;The good news was that I was fairly well dressed.  I had assumed before this that I really didn't have the right clothing for anything below 30°F (this morning it was about 20°F).  The only thing that got a little cold were my toes.  The roads were fairly clear and the Gateway Trail, which I rode for about half the trip, was mostly plowed.  As long as I didn't make any sudden turns, everything was smooth.&lt;br /&gt;&lt;br /&gt;Overall, I enjoyed it and it's getting my more excitied about biking.  Spring could not come soon enough.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/35759427-1023376574472714951?l=beyondthispoint.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://beyondthispoint.blogspot.com/feeds/1023376574472714951/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=35759427&amp;postID=1023376574472714951' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/35759427/posts/default/1023376574472714951'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/35759427/posts/default/1023376574472714951'/><link rel='alternate' type='text/html' href='http://beyondthispoint.blogspot.com/2007/01/january-bike-ride.html' title='January Bike Ride'/><author><name>Pete</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://photos1.blogger.com/blogger2/1347/4365/240/gse_multipart43909.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-35759427.post-4570058320834248867</id><published>2007-01-22T22:47:00.000-06:00</published><updated>2007-01-22T23:08:53.483-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Technology'/><title type='text'>Will Google take over the world?</title><content type='html'>For years I've had to endure Microsoft-bashing. I call it bashing because for the past 8 years I've made a living off of the success of Microsoft and knocking Microsoft (or M$ as the affectionate abbreviation goes), meant you were knocking the entity that ultimate allowed me to get a pay check. But don't get me wrong, it's fun to bash large corporations, and I'll admit I've made an occasional M$ jab from time to time. Microsoft has done some cool stuff and some not so cool stuff. And, ironically, many of the sins they were accused of back in the day have been committed by many of their competitors since then. I guess it's the game of business.&lt;br /&gt;&lt;br /&gt;I remember something Bill Gates said a few years ago when Microsoft was at its peak. And that was that at any given moment, Microsoft Corporation was 12 months away from going under. What he meant was that even a software giant like Microsoft could never stand still or the competition would eat it alive.&lt;br /&gt;&lt;br /&gt;Well, I think Microsoft has met it's match: Google. It's no secret that Google has quietly become a huge player in the technology world. They are the standard search engine. I love many of their free services, including &lt;a href="http://beyondthispoint.blogspot.com/2006/10/lovin-gmail.html"&gt;Gmail&lt;/a&gt;, Google Maps, and Google Reader. But I've always found it a bit odd that they offer so much stuff for free.&lt;br /&gt;&lt;br /&gt;What exactly is G$ up too? Check &lt;a href="http://www.pbs.org/cringely/pulpit/2007/pulpit_20070119_001510.html"&gt;this&lt;/a&gt; out.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/35759427-4570058320834248867?l=beyondthispoint.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://beyondthispoint.blogspot.com/feeds/4570058320834248867/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=35759427&amp;postID=4570058320834248867' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/35759427/posts/default/4570058320834248867'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/35759427/posts/default/4570058320834248867'/><link rel='alternate' type='text/html' href='http://beyondthispoint.blogspot.com/2007/01/will-google-take-over-world.html' title='Will Google take over the world?'/><author><name>Pete</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-35759427.post-7651226875156568367</id><published>2007-01-03T00:15:00.000-06:00</published><updated>2007-09-05T00:02:41.054-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Technology'/><title type='text'>Why I HATE Cingular</title><content type='html'>Ya, harsh subject line. And for people who know me, I'm not one to start flaming people or companies. But I've had it. And I need to vent this somehow.&lt;br /&gt;&lt;br /&gt;About a year and a half ago I switched from T-Mobile to &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_0"&gt;Cingular&lt;/span&gt; because I wanted a phone that only &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_1"&gt;Cingular&lt;/span&gt; offered. Dumbest mistake of my life. The very second my 2 year contract is up, I'm switching back. Now, granted, &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_2"&gt;Cingular&lt;/span&gt; has been better than AT&amp;T, but not by much. And just an FYI - my biggest annoyance with &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_3"&gt;Cingular&lt;/span&gt; is that they claim to have the fewest number of dropped calls; I get a dropped call about once a day.&lt;br /&gt;&lt;br /&gt;Anyway, I open up my cell phone bill today expecting it to be around the normal $75. Not this time. Today it was 498.54! Holy crap! So I scrambled to figure it what the heck &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_4"&gt;happenned&lt;/span&gt;. Of course, &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_5"&gt;disciphering&lt;/span&gt; the billing charges requires a PhD and a half, but I finally found the issue. The bill claimed that I had done a 39,578KB data transfer. I don't have a data plan. I haven't done &lt;em&gt;any &lt;/em&gt;data transfers since the first month I had service after which I cancelled their $20/mo data plan.&lt;br /&gt;&lt;br /&gt;I called customer care and the rep did a little research and found out the transfer occurred at 3:52pm on Tuesday 11/28/06. I was at work at that time and to my knowledge was not using my phone to transfer megabytes of data (I did a quick calculation and discovered it would take at least 2 hours to transfer that much data with my phone). So I told them that and they claimed there was nothing they could do. Ha! I asked to speak with a manager. After sitting on hold for 10 minutes, a manager came on and said the best they could do was credit my account $150. When I &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_6"&gt;proceded&lt;/span&gt; to explain to them that I wasn't going to pay &lt;em&gt;any&lt;/em&gt; of the erroneous data charge, the connection went dead. &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_7"&gt;Cingular&lt;/span&gt; - network with the fewest dropped calls. That was the moment that I almost ate my cellphone while tearing the &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_8"&gt;countertop&lt;/span&gt; off my kitchen cabinets.&lt;br /&gt;&lt;br /&gt;There is a happy ending. I called back, sat on hold for a while, and finally spoke with a rep who was able to credit me half of the charge and fill out a form that would supposedly credit the rest back within 30 days. I pray that happens; God have mercy on the poor customer care person who answers my call if it doesn't.&lt;br /&gt;&lt;br /&gt;There, I feel much better now. :)&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style="color:#ff0000;"&gt;UPDATE 9/4/07:&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;I'm happy to report that &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_9"&gt;Cingular&lt;/span&gt; did manage to completely reverse the charges.  It took a month, but at least it was taken care of.  Why I had to go through so much pain is still irritating to think about.  The  conspiracy theorist in me thinks they sprinkle little fees like this all the time to customers and bank on the hopes that the customer won't go through the &lt;span class="blsp-spelling-corrected" id="SPELLING_ERROR_10"&gt;hassle&lt;/span&gt; to reverse them.&lt;br /&gt;&lt;br /&gt;Anyway, in the end, I got even.  The very day after my 2 year contract was up I switched to T-Mobile.  And, like the comment of one of the readers, I haven't had to &lt;em&gt;think&lt;/em&gt; about my wireless provider ever since.&lt;br /&gt;&lt;br /&gt;One more funny thing.  Just after I switched, T-Mobile put up some billboards in the Minneapolis/St. Paul area stating "Don't drop dad!  T-Mobile, fewest drop calls in the Twin Cities".  I knew it wasn't just me!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/35759427-7651226875156568367?l=beyondthispoint.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://beyondthispoint.blogspot.com/feeds/7651226875156568367/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=35759427&amp;postID=7651226875156568367' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/35759427/posts/default/7651226875156568367'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/35759427/posts/default/7651226875156568367'/><link rel='alternate' type='text/html' href='http://beyondthispoint.blogspot.com/2007/01/why-i-hate-cingular.html' title='Why I HATE Cingular'/><author><name>Pete</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-35759427.post-461423336282011799</id><published>2006-11-28T21:27:00.001-06:00</published><updated>2006-11-28T21:27:02.890-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Technology'/><title type='text'>Power lines, KSTP, and DSL filters</title><content type='html'>We've been living in our current house for a little over two years now. The house is nice, but it's less than a block from some really big power lines. Not the normal residential kind, but the big industrial, 1.21 Gigawatt, make-the-air-buzz-when-it's-raning kind, like this:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.freefoto.com//images/13/52/13_52_58_web.jpg?&amp;k=Power+Lines"&gt;&lt;img style="FLOAT: right; MARGIN: 0px 0px 10px 10px; WIDTH: 400px; CURSOR: hand" alt="" src="http://www.freefoto.com//images/13/52/13_52_58_web.jpg?&amp;k=Power+Lines" border="0" /&gt;&lt;/a&gt;Other than the fear of aquiring gradual brain cancer, this was a non-issue.  Until we picked up the phone.  When you used any land-line phone in the house, regardless of the brand of phone (believe me, we tried many) or the carrier (we tried of few of those as well), you could always hear a local talk AM radio station, loud and clear. &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;I tried everything I could think of to fix the problem.  I even installed new phone wiring between my Vonage box in the basement (Vonage is our phone company du jour) and the phone base unit in the kitchen upstairs.  I orginally did this with new standard 4-conductor telephone wire; didn't help a bit.  A few days ago I redid the entire run using plenum-grade CAT-5e cable, figuring that the non-twisted telephone wire was picking up lots of interference.  That help, &lt;em&gt;a little&lt;/em&gt;.&lt;br /&gt;&lt;br /&gt;About 6 months ago my neighbor told me he had the same problem and solved it by installing a DSL filter between the phone jack and his phone.  By chance I remembered that about a month ago and picked one up, but sort of forgot about it.  Well, just tonight after feeling the frustration of my not-so-successful wire replacement project, I remembered the filter I bought, and decided to give it a shot.&lt;br /&gt;&lt;br /&gt;It worked! &lt;br /&gt;&lt;br /&gt;And &lt;a href="http://www.radioshack.com/product/index.jsp?productId=2102447&amp;cp=&amp;amp;origkw=dsl+filter&amp;kw=dsl+filter&amp;amp;parentPage=search"&gt;here's&lt;/a&gt; the little bugger that did the trick. &lt;br /&gt;&lt;br /&gt;Being somewhat of a die-hard cable internet subscriber, I guess I've gained a new respect for DSL. :)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/35759427-461423336282011799?l=beyondthispoint.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://beyondthispoint.blogspot.com/feeds/461423336282011799/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=35759427&amp;postID=461423336282011799' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/35759427/posts/default/461423336282011799'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/35759427/posts/default/461423336282011799'/><link rel='alternate' type='text/html' href='http://beyondthispoint.blogspot.com/2006/11/power-lines-kstp-and-dsl-filters.html' title='Power lines, KSTP, and DSL filters'/><author><name>Pete</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-35759427.post-5192455546349891925</id><published>2006-10-23T11:46:00.000-05:00</published><updated>2006-10-23T12:21:17.477-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Software'/><title type='text'>Subversion on Windows</title><content type='html'>Source control has always been a bit problematic in the Microsoft world. Most of us have used Visual &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_0" onclick="BLOG_clickHandler(this)"&gt;SourceSafe&lt;/span&gt; quite extensively and don't have to be convinced that it pretty much sucks rocks. The good news is that the &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_1" onclick="BLOG_clickHandler(this)"&gt;souce&lt;/span&gt; control story has gotten better in recent years and months. The most notable event was the launch of &lt;a href="http://msdn.microsoft.com/vstudio/teamsystem/team/"&gt;Team Foundation Server&lt;/a&gt;, which comes with &lt;strong&gt;Team Source Control&lt;/strong&gt;, a &lt;em&gt;huge&lt;/em&gt; improvement in the Microsoft offering for source control.&lt;br /&gt;&lt;br /&gt;However, &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_2" onclick="BLOG_clickHandler(this)"&gt;TFS&lt;/span&gt; is not very ideal for single-man shops like myself (yes, I do some software side projects from time to time). It's ideal for teams (even small ones) where collaboration of source code with the software development process is important (this should be the case with any shop). It's also not very free. I'm a bit of a cheap skate, so all-free was &lt;span class="blsp-spelling-corrected" id="SPELLING_ERROR_3"&gt;definitely&lt;/span&gt; more appealing.&lt;br /&gt;&lt;br /&gt;In the past I've played with &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_4" onclick="BLOG_clickHandler(this)"&gt;&lt;a href="http://en.wikipedia.org/wiki/Concurrent_Versions_System"&gt;CVS&lt;/a&gt;&lt;/span&gt; and liked it. It's been around for a long time and has a decent Windows implementation that installs an easy-to-manage Windows service. However, &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_5" onclick="BLOG_clickHandler(this)"&gt;CVS&lt;/span&gt; has its &lt;a href="http://www.devx.com/opensource/Article/27884/"&gt;well-known shortcomings&lt;/a&gt;. These shortcomings are the primary reason for the creation of &lt;a href="http://subversion.tigris.org/"&gt;Subversion&lt;/a&gt; (&lt;span class="blsp-spelling-error" id="SPELLING_ERROR_6" onclick="BLOG_clickHandler(this)"&gt;SVN&lt;/span&gt;), which is very &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_7" onclick="BLOG_clickHandler(this)"&gt;CVS&lt;/span&gt;-like, but better in many ways.&lt;br /&gt;&lt;br /&gt;The only down side to &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_8" onclick="BLOG_clickHandler(this)"&gt;SVN&lt;/span&gt;, it seemed, was that it didn't have a very good Windows implementation. Specifically, the server install did not include a Windows service. You had to come up with some other way of keeping your &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_9" onclick="BLOG_clickHandler(this)"&gt;SVN&lt;/span&gt; process (or daemon as they call it) running using a 3rd party Windows service wrapper or the like.&lt;br /&gt;&lt;br /&gt;Well, digging further, I recently discovered that it's not too difficult to use &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_10" onclick="BLOG_clickHandler(this)"&gt;SVN&lt;/span&gt; with &lt;a href="http://www.apache.org/"&gt;Apache&lt;/a&gt;, which is an open source web server that has a very nice Windows implementation. Accessing &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_11" onclick="BLOG_clickHandler(this)"&gt;SVN&lt;/span&gt; via Apache (i.e. HTTP) is ideal because it allows you to easily browse your repository, and it works great with &lt;a href="http://tortoisesvn.tigris.org/"&gt;&lt;span class="blsp-spelling-error" id="SPELLING_ERROR_12" onclick="BLOG_clickHandler(this)"&gt;TortoiseSVN&lt;/span&gt;&lt;/a&gt; (my preferred &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_13" onclick="BLOG_clickHandler(this)"&gt;SVN&lt;/span&gt; client) which uses &lt;a href="http://en.wikipedia.org/wiki/WebDAV"&gt;&lt;span class="blsp-spelling-error" id="SPELLING_ERROR_14" onclick="BLOG_clickHandler(this)"&gt;WebDAV&lt;/span&gt; &lt;/a&gt;to communicate with &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_15" onclick="BLOG_clickHandler(this)"&gt;SVN&lt;/span&gt; via HTTP. In fact I found the best resource for setting all this up was in the &lt;a href="http://tortoisesvn.net/docs/release/TortoiseSVN_en/tsvn-serversetup.html#tsvn-serversetup-apache"&gt;&lt;span class="blsp-spelling-error" id="SPELLING_ERROR_16" onclick="BLOG_clickHandler(this)"&gt;TortoiseSVN&lt;/span&gt; documentation&lt;/a&gt; itself.&lt;br /&gt;&lt;br /&gt;Very cool! Now, I'm a die-hard &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_17" onclick="BLOG_clickHandler(this)"&gt;SVN&lt;/span&gt; fan.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/35759427-5192455546349891925?l=beyondthispoint.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://beyondthispoint.blogspot.com/feeds/5192455546349891925/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=35759427&amp;postID=5192455546349891925' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/35759427/posts/default/5192455546349891925'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/35759427/posts/default/5192455546349891925'/><link rel='alternate' type='text/html' href='http://beyondthispoint.blogspot.com/2006/10/subversion-on-windows.html' title='Subversion on Windows'/><author><name>Pete</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-35759427.post-3563801856652414935</id><published>2006-10-23T10:16:00.001-05:00</published><updated>2007-01-22T23:03:39.444-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Technology'/><title type='text'>Lovin' GMail</title><content type='html'>It's probably safe to say I switch email providers as often as blog engines, perhaps even more often. However, I think I've finally found one I can stick with for a while: &lt;a href="http://gmail.com/"&gt;Gmail&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;My email story goes something like this:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Way back in the day, used whatever email address my &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_0" onclick="BLOG_clickHandler(this)"&gt;ISP&lt;/span&gt; gave me. I quickly gave this up for a free &lt;a href="http://mail.yahoo.com/"&gt;Yahoo Mail&lt;/a&gt; account, which allowed me to check my mail from anywhere and not give up the address if I changed &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_1" onclick="BLOG_clickHandler(this)"&gt;ISP's&lt;/span&gt;.&lt;/li&gt;&lt;li&gt;Purchased a domain name (&lt;span class="blsp-spelling-error" id="SPELLING_ERROR_2" onclick="BLOG_clickHandler(this)"&gt;twistedstream&lt;/span&gt;.com) and thought it would be cool to host my own mail server at home! I think I played with some 3rd party POP3 server at first. Then I &lt;span class="blsp-spelling-corrected" id="SPELLING_ERROR_3"&gt;switched&lt;/span&gt; to what all the cool Microsoft kids were using: Exchange.&lt;/li&gt;&lt;li&gt;I finally got tired of my home server either going down (which, in all honesty, didn't happen very often) or my ever-reliable &lt;span class="blsp-spelling-corrected" id="SPELLING_ERROR_4"&gt;Internet&lt;/span&gt; connection dropping and consequently people complaining about not being able to send/receive email to/from me. So I switched back to a hosted solution called &lt;a href="http://everyone.net/"&gt;Everyone.net&lt;/a&gt; that would work with my domain name. I believe was able to access my email via POP3 and &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_5" onclick="BLOG_clickHandler(this)"&gt;IMAP&lt;/span&gt;. I liked the &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_6" onclick="BLOG_clickHandler(this)"&gt;IMAP&lt;/span&gt; capability because it was more like Exchange.&lt;/li&gt;&lt;li&gt;Unfortunately Everyone.net only did email. All my calendar and contact data was still local to one machine. Then I decided to switch back (yes, back) to Yahoo Mail. Yahoo was &lt;span class="blsp-spelling-corrected" id="SPELLING_ERROR_7"&gt;also&lt;/span&gt; able to host my domain name as well as sync calendar and contacts with Outlook (this worked most of the time).&lt;/li&gt;&lt;li&gt;Then a coworker turned me onto a hosted Exchange service at &lt;a href="http://1and1.com/"&gt;1and1&lt;/a&gt;. Man, this had to be the Holy Grail of email (for Outlook users)! And, for the most part, it was. But then I took a new job where the &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_8" onclick="BLOG_clickHandler(this)"&gt;OWA&lt;/span&gt; (Outlook Web Access) didn't play well with the corporate firewall; essentially it made checking my personal email next to impossible while at work.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Then enters Gmail. &lt;/p&gt;&lt;p&gt;I've actually had an account for some time (I picked one up back when it first came out and it was the latest rave). However, besides some of its different ways of organizing mail, it just seemed like another web mail client. However, two things peaked my interest and eventually made me switch:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;The ability to send email from &lt;em&gt;any&lt;/em&gt; email address (not just your @gmail.com account). Yes, via a slick validation email, Gmail will allow you to send email from any account. Couple this with aliases set up in my &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_9" onclick="BLOG_clickHandler(this)"&gt;MX&lt;/span&gt; record, and Gmail now becomes the center of my email universe.&lt;/li&gt;&lt;li&gt;The ability to import &lt;em&gt;all&lt;/em&gt; of my old email. Granted, this feature isn't provided out of the box by Gmail. You have to do some digging, but a free tool called the &lt;a href="http://www.marklyon.org/gmail/"&gt;Gmail Loader&lt;/a&gt; allows you to import all of your old mail. And when I say all, I mean &lt;em&gt;all&lt;/em&gt;. I was able to suck in email as far back as 2000 (which is all I had archived). The beauty of this is all of that email (over 1GB) is now labelled and &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_10" onclick="BLOG_clickHandler(this)"&gt;searchable&lt;/span&gt; with &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_11" onclick="BLOG_clickHandler(this)"&gt;Gmail's&lt;/span&gt; awesome search engine. And since Gmail gives you over 2.5GB of storage, I still have plenty of room.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;One more feature that Gmail has that I like, but don't necessary use, is their POP3 access. Like many other hosted email services, you can use a rich email client (Outlook, Outlook Express, &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_12" onclick="BLOG_clickHandler(this)"&gt;Thunderbird&lt;/span&gt;, etc) to pull down your email via the POP3 protocol. Gmail makes that experience better with the following added capabilities:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;More choices on how messages are stored on the server.&lt;/li&gt;&lt;li&gt;The ability to send email from any email address that you've validated on the Gmail site.&lt;/li&gt;&lt;li&gt;Having a copy of any email sent from your rich email client stored in the &lt;strong&gt;Sent Mail&lt;/strong&gt; folder on the server (as long as you use the Gmail SMTP server in your client-side configuration).&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Very cool stuff. I now feel content with my email :).&lt;/p&gt;&lt;p&gt;ps&lt;br /&gt;I have to say a big thanks to my friend &lt;a href="http://www.michaeltangen.com/"&gt;Michael &lt;/a&gt;who's been using Gmail for some time now and got me re-interested.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/35759427-3563801856652414935?l=beyondthispoint.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://beyondthispoint.blogspot.com/feeds/3563801856652414935/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=35759427&amp;postID=3563801856652414935' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/35759427/posts/default/3563801856652414935'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/35759427/posts/default/3563801856652414935'/><link rel='alternate' type='text/html' href='http://beyondthispoint.blogspot.com/2006/10/lovin-gmail.html' title='Lovin&apos; GMail'/><author><name>Pete</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-35759427.post-7027835022786173809</id><published>2006-10-09T19:46:00.000-05:00</published><updated>2006-10-09T19:53:37.224-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='General'/><title type='text'>My BPTBEC ratio is very low</title><content type='html'>I have, once again, switched blog engines. I began by using &lt;a href="http://livejournal.com"&gt;LiveJounral&lt;/a&gt;, then I switched to hosting my own. That was cool, but I got tired of having to maintain the server and have friends complain that they couldn't read my blog because my internet connection was down (which is Comcastic!).  Not that it really mattered anyway since it was a rare occasion to actually see a new post from my blog.&lt;br /&gt;&lt;br /&gt;So, I've made the move back to a hosted blog. For kicks, I decided to go with &lt;a href="http://blogger.com"&gt;Blogger&lt;/a&gt;. They've been around a long time, have a pretty slick interface that's free, and the Blogger account integrates with my Google ID, which is cool.&lt;br /&gt;&lt;br /&gt;Finally, regarding the subject of this post. In all this silliness, I thought it would be fun to make up an acronym to describe my blogging behaviors. BPTBEC stands for Blog Posts To Blog Engine Changes. Right now that ratio for me is at about 3.7. Hehe, I think most average bloggers would have a value in the 100's or 1000's. Well, at least I've got something to shoot for.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/35759427-7027835022786173809?l=beyondthispoint.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://beyondthispoint.blogspot.com/feeds/7027835022786173809/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=35759427&amp;postID=7027835022786173809' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/35759427/posts/default/7027835022786173809'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/35759427/posts/default/7027835022786173809'/><link rel='alternate' type='text/html' href='http://beyondthispoint.blogspot.com/2006/10/my-bptbec-ratio-is-very-low.html' title='My BPTBEC ratio is very low'/><author><name>Pete</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-35759427.post-6468107396483783331</id><published>2006-09-22T01:15:00.000-05:00</published><updated>2006-10-09T19:44:39.660-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Biking'/><title type='text'>Go ahead, blame the rear derailer</title><content type='html'>After all that hoopla in &lt;a href="http://beyondthispoint.blogspot.com/2006/09/dont-always-blame-rear-derailer.html"&gt;my last post&lt;/a&gt; about my rear derailer, I came to realize that my original hunch was actually correct. I finally brought my bike into &lt;a href="http://www.eriksbikeshop.com/"&gt;Erik's Bike Shop&lt;/a&gt; to get some tire fenders installed today. I mentioned my derailer issue and the tech examined the bike and determined the derailer was in fact out of adjustment.&lt;br /&gt;&lt;br /&gt;Shows you what I know. :)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/35759427-6468107396483783331?l=beyondthispoint.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/35759427/posts/default/6468107396483783331'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/35759427/posts/default/6468107396483783331'/><link rel='alternate' type='text/html' href='http://beyondthispoint.blogspot.com/2006/09/go-ahead-blame-rear-derailer.html' title='Go ahead, blame the rear derailer'/><author><name>Pete</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-35759427.post-549855613019781575</id><published>2006-09-19T08:25:00.000-05:00</published><updated>2006-10-09T19:43:51.064-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Biking'/><title type='text'>Don't Always Blame the Rear Derailer</title><content type='html'>My first biking post! Biking (bycicling, not the motorized variety) has become my new favorite hobby. Good form of exercise. Lot's of scenery. And, as of yesturday, I can use it to get to work since I now work in &lt;a href="http://maps.google.com/maps?f=q&amp;hl=en&amp;amp;q=30+7th+St+E,+St+Paul,+MN+55101&amp;ie=UTF8&amp;amp;z=15&amp;ll=44.949189,-93.096256&amp;amp;spn=0.013789,0.042915&amp;om=1"&gt;downtown St. Paul&lt;/a&gt;. I suppose I should go more into that subject in a different post (stay tuned). Like most software developers, staying in shape is always a challenge, so if I can combine commuting with achieving that goal, I'm way ahead.&lt;br /&gt;&lt;br /&gt;So, I haven't done a real bike ride (i.e. &gt; 1 mile) in over two weeks due to my hectic schedule as of late. I took my bike out Sunday night to do a quick 5 miler around &lt;a href="http://maps.google.com/maps?f=q&amp;hl=en&amp;amp;q=Little+Canada,+MN&amp;ie=UTF8&amp;amp;z=15&amp;ll=45.019549,-93.070765&amp;amp;spn=0.014713,0.042915&amp;om=1"&gt;Garvais Lake&lt;/a&gt;. I get about 100 yards and begin to notice that my chain keeps slipping, as if it were hopping between gears. My immediate reaction was that my rear derailer was out of adjustment. So I start fiddling with my rear shifter to try to get it to settle on a gear. Same problem. I then try different gears to see if the issue only occurrs with the higher gears. Same problem.&lt;br /&gt;&lt;br /&gt;Something was fishy. I'm finally biking along the lake and would really rather enjoy the scenery instead of fight with my transmission. In a puff of frustration, I hop off my bike, flip it upside down, and pedal it by hand so I could get a close look at the derailer. I notice that it seems to be inline with the sprokets. Looking closer I also notice that the skipping seems to be occuring at a regular pace as I turn the crank, and if I slow down the RPM's, the skipping slows down.&lt;br /&gt;&lt;br /&gt;Finally I begin to see the problem. Some (not all) of the links in my chain were stiff. They weren't rusted, per se, just stiff. Stiff enough so they wouldn't quite twist around the small gear of the derailer, causing the chain to actually jump off, just for a split second. Hense the skipping.&lt;br /&gt;&lt;br /&gt;I wasn't quite sure how to remedy the problem. So, I decided to just hop on my bike and take it easy for the rest of the ride (i.e. actually enjoy the scenery). As luck would have it, the continual motion of my ride eventually loosened up the chain (perhaps warmed it up?) so within a few minutes, the skipping went away!&lt;br /&gt;&lt;br /&gt;I was a happy biker and learned something new that day!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/35759427-549855613019781575?l=beyondthispoint.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/35759427/posts/default/549855613019781575'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/35759427/posts/default/549855613019781575'/><link rel='alternate' type='text/html' href='http://beyondthispoint.blogspot.com/2006/09/dont-always-blame-rear-derailer.html' title='Don&apos;t Always Blame the Rear Derailer'/><author><name>Pete</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-35759427.post-972742826341452661</id><published>2006-06-30T00:35:00.000-05:00</published><updated>2006-10-09T15:29:03.507-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Software'/><title type='text'>Mobile Objects and Ice Cream</title><content type='html'>So I've been taking the time lately to read through &lt;a class="blines3" title="Link outside of this blog" href="http://www.lhotka.net/weblog" target="_blank"&gt;Rocky Lhotka&lt;/a&gt;'s new &lt;a class="blines3" title="Link outside of this blog" href="http://www.lhotka.net/cslanet" target="_blank"&gt;CSLA.NET 2.0&lt;/a&gt; book, &lt;a class="blines3" title="Link outside of this blog" href="http://www.amazon.com/gp/product/1590596323/sr=8-1/qid=1151469990/ref=pd_bbs_1/102-8848828-0626569?ie=UTF8" target="_blank"&gt;Expert C# 2005 Business Objects&lt;/a&gt;.  It's a great book and in it, among many other things, he talks about the concept of mobile objects.  In short, mobile objects are objects that have the ability to move from one location (process or machine) to another.  In each location they can behave in an object-oriented fashion, interacting with the services that that location has to offer.  Essentially, the concept of mobile objects is one of the ways the CSLA.NET framework solves the age-old problem of doing OO within a distributed application.&lt;br /&gt;&lt;br /&gt;Anyway, I caught myself thinking in mobile object terms the other night when I got a hankering for a bowl of ice cream.  I found myself upstairs in the kitchen with an empty bowl and a spoon.  The problem was that the ice cream was in our second refridgerator in the basement.  Rather than bringing the ice cream container up to the kitchen (only to have to bring it back down again when I'm done), I decided to implement a mobile object.  I was the end user and my bowl was the object (I suppose my spoon was the user interface).  I wanted to make my bowl mobile and send it to the basement to be filled with ice cream. Since it happened to be father's day, I asked my son if he would be willing to execute the fetch.  Being a good boy, he ran downstairs with my bowl, performed the data-centric operation of getting the ice cream out of the ice cream container (the data store) and filling the bowl.  He then made the bowl mobile again by returning it back to me in the kitchen.  With the filled ice cream bowl I performed some UI-centric business logic by pouring on a generous serving of &lt;a class="blines3" title="Link outside of this blog" href="http://www.smuckers.com/fg/ict/default.asp?groupid=4&amp;catid=8" target="_blank"&gt;Magic Shell&lt;/a&gt;.  And then I ate it.&lt;br /&gt;&lt;br /&gt;See, mobile objects really do work!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/35759427-972742826341452661?l=beyondthispoint.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/35759427/posts/default/972742826341452661'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/35759427/posts/default/972742826341452661'/><link rel='alternate' type='text/html' href='http://beyondthispoint.blogspot.com/2006/06/mobile-objects-and-ice-cream.html' title='Mobile Objects and Ice Cream'/><author><name>Pete</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-35759427.post-3486885824337551119</id><published>2006-04-19T16:36:00.000-05:00</published><updated>2006-10-09T15:27:58.446-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Software'/><title type='text'>Setting up an IIS6 Application Pool Identity</title><content type='html'>Often times it's necessary to run an ASP.NET application under a different user account than Network Service.  Usually this is the case when Network Service doesn't have enough rights to access necessary application resources like a database or a file share on a different server.&lt;br /&gt;&lt;br /&gt;Here's how to configure a different account (local account or domain account) so it has sufficient permissions to run an IIS6 Application Pool for ASP.NET applications:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Add the account to the local IIS_WPG security group. &lt;/li&gt;&lt;li&gt;Open the Group Policy Editor for the local computer (&lt;strong&gt;gpedit.msc&lt;/strong&gt;) &lt;/li&gt;&lt;li&gt;Drill down to: &lt;strong&gt;Local Computer Policy/Computer Configuration/Windows Settings/Security Settings/Local Policies/User Rights Assignments &lt;/strong&gt;&lt;/li&gt;&lt;li&gt;Add the new identity account to the following polices:&lt;br /&gt;- Adjust memory quotas for a process&lt;br /&gt;- Logon as a service&lt;br /&gt;- Replace a process level token&lt;/li&gt;&lt;li&gt;If your web application is going to host any web services, you need to also give your account &lt;strong&gt;Delete&lt;/strong&gt; access to the &lt;span style="font-family:courier new;"&gt;C:\WINDOWS\Temp&lt;/span&gt; directory.  Note: this is done via the Advanced dialog in the Security page of the Explorer folder properties dialog box.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;References:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://groups.google.com/group/microsoft.public.inetserver.iis/browse_thread/thread/2e85bc286d16cca9/84e79c262a72e7ba?lnk=st&amp;q=%22iis+6%22+identity+%22service+unavailable%22&amp;amp;rnum=2&amp;amp;hl=en#84e79c262a72e7ba"&gt;IIS 6 Network Service Account vs. IIS 5 IWAM&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a class="blines3" title="Link outside of this blog" href="http://support.microsoft.com/?id=812614" target="_blank"&gt;Default permissions and user rights for IIS 6.0&lt;/a&gt; &lt;/li&gt;&lt;li&gt;&lt;a class="blines3" title="Link outside of this blog" href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnnetsec/html/secnetht01.asp" target="_blank"&gt;Building Secure ASP.NET Applications: Authentication, Authorization, and Secure Communication&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/35759427-3486885824337551119?l=beyondthispoint.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/35759427/posts/default/3486885824337551119'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/35759427/posts/default/3486885824337551119'/><link rel='alternate' type='text/html' href='http://beyondthispoint.blogspot.com/2006/04/setting-up-iis6-application-pool.html' title='Setting up an IIS6 Application Pool Identity'/><author><name>Pete</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-35759427.post-5491489338203538681</id><published>2005-11-19T01:55:00.000-06:00</published><updated>2006-10-09T15:23:05.803-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Software'/><title type='text'>Building a virtualized Team Foundation Server development environment</title><content type='html'>Given the recent RTM release of &lt;a class="blines3" title="Link outside of this blog" href="http://msdn.microsoft.com/vstudio/products/default.aspx" target="_blank"&gt;Visual Studio 2005 &lt;/a&gt;and all the hype around &lt;a class="blines3" title="Link outside of this blog" href="http://msdn.microsoft.com/vstudio/products/vsts/tfs/default.aspx" target="_blank"&gt;Team Foundation Server&lt;/a&gt;, I wanted to build a development environment where I could play with both. So, I finally managed to do it using &lt;a class="blines3" title="Link outside of this blog" href="http://www.microsoft.com/windows/virtualpc/default.mspx" target="_blank"&gt;Virtual PC&lt;/a&gt;. I thought I’d share the knowledge of what took me several hours (days, rather) to complete.&lt;br /&gt;&lt;br /&gt;My goal was to virtualize a development environment that contained a TFS server and one or more clients. I’d heard that one needed to install TFS within an &lt;a class="blines3" title="Link outside of this blog" href="http://en.wikipedia.org/wiki/Windows_Server_domain" target="_blank"&gt;Active Directory domain&lt;/a&gt;. However, after reading the install documentation, I found that to only be necessary if the data tier (SQL Server 2005) and the application tier (TFS) were to be run on separate machines; this is what the doc calls a Dual-Server Deployment. On the contrary, if both the data and application tiers are installed on the same machine (Single-Server Deployment), a domain is optional.&lt;br /&gt;&lt;br /&gt;But since many development shops will have Active Directory running, I wanted to simulate it. So, one option would be to install all of TFS on a single machine that was also a &lt;a class="blines3" title="Link outside of this blog" href="http://en.wikipedia.org/wiki/Domain_controller" target="_blank"&gt;Domain Controller&lt;/a&gt;. However, it is also common knowledge that installing TFS on the Domain Controller itself is problematic (at least it has been with previous TFS betas). Given all that, I decided to build three &lt;a class="blines3" title="Link outside of this blog" href="http://en.wikipedia.org/wiki/Microsoft_Virtual_PC" target="_blank"&gt;virtual machines&lt;/a&gt;:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;A stand-alone Domain Controller – 256 MB RAM &lt;/li&gt;&lt;li&gt;The “TFS server” with SQL Server 2005, Windows Sharepoint Services SP2 (another prereq for TFS), and Team Foundation Server itself – 512 MB RAM &lt;/li&gt;&lt;li&gt;A client machine with a &lt;a class="blines3" title="Link outside of this blog" href="http://msdn.microsoft.com/vstudio/products/vsts/default.aspx" target="_blank"&gt;Team Edition &lt;/a&gt;of Visual Studio 2005 installed (in my case, I have access to a licensed copy of &lt;a class="blines3" title="Link outside of this blog" href="http://msdn.microsoft.com/vstudio/products/vsts/dev/default.aspx" target="_blank"&gt;Team Edition for Software Developers&lt;/a&gt;) – 512 MB RAM&lt;/li&gt;&lt;/ul&gt;The total RAM requirement was 1.5 GB, which fit my memory budget since my host OS (Windows XP Pro on a Dell Latitude D600 laptop) has 2 GB.&lt;br /&gt;&lt;br /&gt;To build all of this, I needed the following software “ingredients”:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;A base VM image of Windows Server 2003 that had been &lt;a class="blines3" title="Link outside of this blog" href="http://blogs.technet.com/megand/articles/357570.aspx" target="_blank"&gt;Sysprep’d&lt;/a&gt;. Make sure this image either has a local copy of the Windows Server 2003 install CD’s I386 directory or you have the CD or an ISO handy. I’m going to use this base image for all three machines, even the client. However, one could just as easily build the client with Windows XP &lt;/li&gt;&lt;li&gt;A handy copy of the Team Foundation Server install documentation (TFSInstall.chm), which can be found on the CD/ISO &lt;/li&gt;&lt;li&gt;A SQL Server 2005 install CD/DVD or ISO (Standard or better). In my case I had &lt;a class="blines3" title="Link outside of this blog" href="http://www.microsoft.com/sql/editions/developer/default.mspx" target="_blank"&gt;Developer Edition&lt;/a&gt; &lt;/li&gt;&lt;li&gt;The install for Windows SharePoint Services 2.0 with SP2, which can be downloaded &lt;a class="blines3" title="Link outside of this blog" href="http://www.microsoft.com/downloads/details.aspx?FamilyId=B922B28D-806A-427B-A4C5-AB0F1AA0F7F9&amp;displaylang=en" target="_blank"&gt;here&lt;/a&gt; &lt;/li&gt;&lt;li&gt;The Team Foundation Server, Beta 3 Refresh CD or ISO &lt;/li&gt;&lt;li&gt;A Microsoft Office 2003 CD or ISO that contains both Word and Excel. &lt;/li&gt;&lt;li&gt;The Microsoft Project 2003 Professional CD or ISO &lt;/li&gt;&lt;li&gt;A Team Edition Visual Studio 2005 install DVD or ISO&lt;/li&gt;&lt;/ul&gt;And, finally, here is the list of steps I took to cook it all up:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;Build the Domain Controller VM&lt;/span&gt;&lt;br /&gt;The domain controller will host a private Active Directory domain. As a bonus, it will also act as a gateway router to the outside world. If any of the machines in the local virtual network require outside (i.e. internet) access, the 2nd network adapter on this machine can be bound to a physical network interface on the host machine, and Routing and Remote Access will provide NAT access to the outside. This capability will actually be called upon when we build the client VM later in this post.&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Create a new VM by copying the base Windows Server 2003 VM files. &lt;/li&gt;&lt;li&gt;In the VM Settings, set the memory to 256 MB. &lt;/li&gt;&lt;li&gt;Change the network adapter to Local only. &lt;/li&gt;&lt;li&gt;Add a second network adapter and bind it to one of the host OS’s network adapters. &lt;/li&gt;&lt;li&gt;Start the VM and let it run through all of the initial Sysprep setup stuff. &lt;/li&gt;&lt;li&gt;Log in as the local administrator. &lt;/li&gt;&lt;li&gt;Change the guest OS network connection bound to Network Adapter 1 so that it has a static IP address. I went with these settings:&lt;br /&gt;&lt;br /&gt;IP: &lt;strong&gt;192.168.100.1&lt;br /&gt;&lt;/strong&gt;Subnet Mask: &lt;strong&gt;255.255.255.0&lt;br /&gt;&lt;/strong&gt;Default Gateway: &lt;strong&gt;127.0.0.1&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Change the VM’s computer name to something meaningful. I went with W2k3TFSB3R-DC. A side note: computer names should be limited to 15 characters or less since that’s the max length of a NetBios computer name. &lt;/li&gt;&lt;li&gt;Run the Configure Your Server Wizard (Administrative Tools). The wizard will automatically suggest that you make the server a domain controller, a DNS server, and a DHCP server. And, since you have two network adapters, it will also suggest installing Routing and Remote Access. Do all of this. I chose to use tfs.local as the Active Directory domain name. This will require a reboot. &lt;/li&gt;&lt;li&gt;In the VM Settings, change the binding of Network Adapter 2 to Not connected. This will keep the network of VM’s completely isolated from the outside world. If you need to connect them for any reason, you can always reconnect the adapter to a physical adapter. &lt;/li&gt;&lt;li&gt;Create the following domain accounts and allow them to remain in their default security group (Domain Users):&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;TFSSETUP &lt;/strong&gt;– This will be used when running TFS setup on the TFS server and will also automatically be joined to the TFS Administrators Application Group&lt;br /&gt;&lt;strong&gt;TFSSERVICE&lt;/strong&gt; – Used as the identity for TFS Windows services&lt;br /&gt;&lt;strong&gt;TFSREPORTS &lt;/strong&gt;– Used by SQL Server Reporting Services&lt;br /&gt;&lt;strong&gt;TeamDeveloper&lt;/strong&gt; – User account of the developer on the client machine&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Leave this VM running.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;&lt;span style="font-size:130%;"&gt;Build the TFS Server VM&lt;br /&gt;&lt;/span&gt;The TFS server will host all of the Team Foundation Server server-side components (application tier) as well as the supporting database (data tier). It will also host Team Foundation Build, which enables automated builds. The kicker is that in order to perform testing and code analysis in the build, a Visual Studio Team SKU needs to also be installed on the server. Luckily, VS Team licensing is per-user, and not per-machine, so installing VS on the server won't break the budget. In our case, we will be installing the same VS SKU on the server as the client machine.&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Create a new VM by copying the base Windows Server 2003 VM files. &lt;/li&gt;&lt;li&gt;In the VM Settings, set the memory to 512 MB. &lt;/li&gt;&lt;li&gt;Change the network adapter to Local only. &lt;/li&gt;&lt;li&gt;Log in as the local administrator. &lt;/li&gt;&lt;li&gt;Change the VM’s computer name to something meaningful. I went with W2k3TFSB3R-SVR. And while you’re doing this, join the computer to the tfs.local domain. This will require a reboot. &lt;/li&gt;&lt;li&gt;Log in as the domain administrator (TFS\Administrator) instead of the local administrator. &lt;/li&gt;&lt;li&gt;Add the TFSSETUP domain account to the local Administrators security group. &lt;/li&gt;&lt;li&gt;Run the Configure Your Server Wizard and turn on the Application Server role, which will enable IIS (make sure to allow ASP.NET). &lt;/li&gt;&lt;li&gt;Install your copy of Visual Studio 2005. You really only need to install the bare minimum since this VS install is solely there to support Team Foundation Build. Therefore, the only components necessary are the following:&lt;br /&gt;&lt;br /&gt;&lt;em&gt;Microsoft Visual Studio 2005 Team Edition &gt;&lt;br /&gt;Team System {your SKU} Tools &gt;&lt;br /&gt;&lt;/em&gt;- &lt;strong&gt;Code Analysis Tools&lt;/strong&gt;&lt;br /&gt;- &lt;strong&gt;Testing Tools&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;where {your SKU} is the version of Visual Studio Team Edition that you have (in my case, I have Team Developer).&lt;br /&gt;&lt;br /&gt;And of couse, you don't need to install MSDN.&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Install SQL Server 2005 using the instructions found here in the TFSInstall.chm file:&lt;br /&gt;&lt;br /&gt;&lt;em&gt;Overview of Team Foundation Server Single-Server Deployment &gt;&lt;br /&gt;Installing Team Foundation Server &gt;&lt;br /&gt;How to: Install Microsoft SQL Server 2005 for Team Foundation Server (Single-Server Deployment)&lt;br /&gt;&lt;/em&gt;&lt;br /&gt;&lt;strong&gt;NOTE:&lt;br /&gt;&lt;/strong&gt;I would advise using the TFSQL2005AnswerFile.ini answer file approach, which is explained at the bottom of the doc page under the section: To install Microsoft SQL Server 2005 using an answer file.&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Install Windows SharePoint Services 2.0 with SP2 using the instructions here in the TFSInstall.chm file:&lt;br /&gt;&lt;br /&gt;&lt;em&gt;Overview of Team Foundation Server Single-Server Deployment &gt;&lt;br /&gt;Installing Team Foundation Server &gt;&lt;br /&gt;How to: Install Microsoft Windows SharePoint Services for Team Foundation Server (Single-Server Deployment)&lt;br /&gt;&lt;br /&gt;&lt;/em&gt;&lt;/li&gt;&lt;li&gt;Log off and log back in as TFS\TFSSETUP. &lt;/li&gt;&lt;li&gt;Install Team Foundation Server using the instructions here in the TFSInstall.chm file:&lt;br /&gt;&lt;br /&gt;&lt;em&gt;Overview of Team Foundation Server Single-Server Deployment &gt;&lt;br /&gt;Installing Team Foundation Server &gt;&lt;br /&gt;How to: Install Team Foundation Server&lt;br /&gt;&lt;br /&gt;&lt;/em&gt;&lt;/li&gt;&lt;li&gt;Install Team Foundation Build using the instructions here in the TFSInstall.chm file:&lt;br /&gt;&lt;br /&gt;&lt;em&gt;Overview of Installing Team Foundation Build &gt;&lt;br /&gt;How to: Install Team Foundation Build&lt;br /&gt;&lt;br /&gt;&lt;/em&gt;&lt;/li&gt;&lt;li&gt;Leave this VM running.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;&lt;span style="font-size:130%;"&gt;Build the TFS Client VM&lt;/span&gt;&lt;/p&gt;&lt;p&gt;The TFS client will contain all client-side development tools as well as the client-side software for accessing Team Foundation Server, which is called Team Explorer. Team Explorer is not its own application, but rather a set of add-ins that integrate seemlessly with Visual Studio 2005. The TFS client machine will also host a local instance of SQL Server 2005 as well as all of the SQL Server developement tools that come along for the ride.&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Create a new VM by copying the base Windows Server 2003 VM files. &lt;/li&gt;&lt;li&gt;In the VM Settings, set the memory to 512 MB. &lt;/li&gt;&lt;li&gt;Change the network adapter to Local only. &lt;/li&gt;&lt;li&gt;Log in as the local administrator. &lt;/li&gt;&lt;li&gt;Change the VM’s computer name to something meaningful. I went with W2k3TFSB3R-C-TD. And while you’re doing this, join the computer to the tfs.local domain. This will require a reboot. &lt;/li&gt;&lt;li&gt;Log in as the domain administrator (TFS\Administrator) instead of the local administrator. &lt;/li&gt;&lt;li&gt;Add the TeamDeveloper and TFSSETUP domain accounts to the local Administrators security group. &lt;/li&gt;&lt;li&gt;Log off and log back in as TFS\TeamDeveloper. &lt;/li&gt;&lt;li&gt;Install Microsoft Office 2003 from your Office CD/ISO. At a minimum you need to install Word and Excel (complete installs of each). This is a prerequisit for the upcoming Team Explorer install. &lt;/li&gt;&lt;li&gt;Install Microsoft Project 2003 Professional (complete install) from your Project CD/ISO. This is a prerequisit for the upcoming Team Explorer install. &lt;/li&gt;&lt;li&gt;On the domain controller VM (W2k3TFSB3R-DC), bind the Network Adapter 2 to one of your physical network adapters. This should allow all the VM’s access to your physical network (and presumably the internet) via Routing and Remote Access. If this doesn’t work, you can always set the Network Adapter of the TFS Client from Local only to one your physical network adapters. &lt;/li&gt;&lt;li&gt;Perform a &lt;a class="blines3" title="Link outside of this blog" href="http://office.microsoft.com/en-us/officeupdate/default.aspx" target="_blank"&gt;Microsoft Office Update&lt;/a&gt;. &lt;/li&gt;&lt;li&gt;On the domain controller VM (W2k3TFSB3R-DC), set the Network Adapter 2 back to Local only. &lt;/li&gt;&lt;li&gt;Install your copy of Visual Studio 2005. Make sure you specify to install SQL Server Express. We’re going to also install the full version of SQL Server 2005 in the following steps. However, there are advantages to having both installed; one of them being many of the sample apps that come with Visual Studio only seem to work with Express. &lt;/li&gt;&lt;li&gt;Install the MSDN Documentation that comes with Visual Studio 2005. &lt;/li&gt;&lt;li&gt;Install SQL Server 2005. I have the Developer Edition and chose to install the following components:&lt;br /&gt;&lt;br /&gt;&lt;em&gt;- SQL Server Database Services&lt;br /&gt;- Reporting Services&lt;br /&gt;- Integration Services&lt;br /&gt;- Client Tools, Books Online&lt;br /&gt;&lt;br /&gt;You may be prompted that some of the components are already installed (since we installed SQL Server Express earlier via Visual Studio). Make sure to re-check any components that it asks for at this point.&lt;br /&gt;&lt;br /&gt;&lt;/em&gt;&lt;/li&gt;&lt;li&gt;Install Team Explorer using the instructions here in the TFSInstall.chm file:&lt;br /&gt;&lt;br /&gt;&lt;em&gt;Overview of Installing Team Explorer &gt;&lt;br /&gt;How to: Install Team Explorer&lt;br /&gt;&lt;br /&gt;&lt;/em&gt;&lt;/li&gt;&lt;li&gt;Open Visual Studio 2005 and connect Team Explorer to the server (W2k3TFSB3R-SVR) using the instructions here in the TFSInstall.chm file:&lt;br /&gt;&lt;br /&gt;&lt;em&gt;Overview of Installing Team Explorer &gt;&lt;br /&gt;How to: Connect Team Explorer to Team Foundation Server&lt;/em&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;That’s it! ;-)&lt;/p&gt;&lt;p&gt;Now you’ve got a handy virtualized Team Foundation Server development environment, complete with the build server (Team Foundation Build) that you can use to play around with, demo, and even develop real code using TFS.&lt;/p&gt;&lt;p&gt;Good luck!&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/35759427-5491489338203538681?l=beyondthispoint.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/35759427/posts/default/5491489338203538681'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/35759427/posts/default/5491489338203538681'/><link rel='alternate' type='text/html' href='http://beyondthispoint.blogspot.com/2005/11/building-virtualized-team-foundation.html' title='Building a virtualized Team Foundation Server development environment'/><author><name>Pete</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-35759427.post-4507364121806570664</id><published>2005-05-05T10:50:00.000-05:00</published><updated>2006-10-09T15:06:34.402-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Software'/><title type='text'>Version Tolerant Serialization</title><content type='html'>Sorry, I haven't posted anything to my blog in a while (not that anyone was holding their breath... well, except for maybe Jason :-) )...&lt;br /&gt;&lt;br /&gt;I've done a thing or two with .NET Serialization and Remoting in the past and presently I'm doing some stuff where we're considering all of the transport options that come with .NET: Remoting, Web Services, Enterprise Services, and the soon-to-be new kid on the block, Indigo. Recently, I watched an &lt;a class="blines3" title="Link outside of this blog" href="http://msdn.microsoft.com/msdntv/episode.aspx?xml=episodes/en/20050120NETMT/manifest.xml" target="_blank"&gt;MSDN TV show &lt;/a&gt;with Matt Tavis on the new features added to Remoting coming up in .NET 2.0. I think its great that their expanding the functionality of Remoting (especially since many were claiming that it was dead back when Indigo was first announced). The reality is that they're adding some really cool features. At a high level they are:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;A new channel called the IPC Channel which is optimized for same-machine inter-process Remoting. It uses named pipes instead of TCP/IP for communication. &lt;/li&gt;&lt;li&gt;An option to secure the TCP Channel using SSPI-based security that is offered in the new &lt;a class="blines3" title="Link outside of this blog" href="http://msdn2.microsoft.com/library/hse0k89h(en-us,vs.80).aspx" target="_blank"&gt;System.Net.Security &lt;/a&gt;namespace. &lt;/li&gt;&lt;li&gt;Socket cache control &lt;/li&gt;&lt;li&gt;Generics support &lt;/li&gt;&lt;li&gt;IPv6 support&lt;/li&gt;&lt;li&gt;Version tolerant serialization&lt;/li&gt;&lt;/ul&gt;That last feature is the one I'd like to comment on. Technically this new feature isn't specific to Remoting, but is a part of .NET Serialization, which is broader technology used by Remoting.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;VTS and Remoting&lt;br /&gt;&lt;/span&gt;In .NET 1.x if you serialized an object of a given type and then deserialized using a newer version of that type and the new type had additional (or removed) fields, you would get seriaization errors. The only work around was to implement the &lt;a class="blines3" title="Link outside of this blog" href="http://msdn2.microsoft.com/library/wf4375ks(en-us,vs.80).aspx" target="_blank"&gt;ISerializable &lt;/a&gt;interface. However, this approach was crude as you were responsible for reading and writing directly to the serialization stream and things got worse when you wanted to serialize an object hierarchy. Microsoft's answer to this issue is Version Tolerance Serialization. VTS essentially adds 5 new attributes to the &lt;a class="blines3" title="Link outside of this blog" href="http://msdn2.microsoft.com/library/kd1dc9w5(en-us,vs.80).aspx" target="_blank"&gt;System.Runtime.Serialization &lt;/a&gt;namespace that allow you to mark fields as optional (these would be used with the new fields on the new type) as well as mark methods in your class for handling specific events in the serialization/deserialization process. In a Remoting scenario, what this lets you do is update a type on the server-side to a new version and let the client-side versions remain the same, without throwing serialization errors.&lt;br /&gt;&lt;br /&gt;Now, in and of itself, this is pretty cool. But I think its worth it to take a moment to offer up a word of caution. There's a reason why the original serialization mechanism was version intolerant (and it wasn't just because the Microsoft developers were too lazy to add tolerance support). If the object you're serializating across the wire is nothing more than data, then this new feature is fairly safe. But if you're actually remoting objects that are more object-oriented (both data and logic together) then caution needs to exercised. While VTS may prevent serialization (i.e. schema) errors from occurring, it won't stop differences in logic between the two type versions from totally hosing up your application. In other words, when you code up version 2 of the serializable object, you better be thinking about version 1, version 1.5, and any other version between what the client was compiled against and what you've got sitting in your IDE. The question to ask when considering this new feature is this: is it more work to force clients to always get the latest binaries or to add backward-compatibility support to my new type? I think in many cases it's more complex to do the later. Especially when you consider many of tools we have today that allow client applications to automatically update their binaries.&lt;br /&gt;&lt;br /&gt;So, in a Remoting scenario where you allow the client and server to version independently I think one needs to exercise caution (and a lot of good unit testing) before hailing this feature as the answer to all our versioning problems. Throwing a new version of a type onto a server with minimal effort to support backward compatibility will lead to a lot of very hard to debug application errors and may even lead to bad data getting persisted to the database. &lt;a class="blines3" title="Link outside of this blog" href="http://www.lhotka.net/" target="_blank"&gt;Rocky Lhotka &lt;/a&gt;talks more about this problem is a more generic scense in &lt;a class="blines3" title="Link outside of this blog" href="http://www.theserverside.net/articles/showarticle.tss?id=SOAVersioningCovenant" target="_blank"&gt;this article &lt;/a&gt;on &lt;a class="blines3" title="Link outside of this blog" href="http://www.theserverside.net/" target="_blank"&gt;TheServerSide.net&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Howerver...&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;VTS in General&lt;br /&gt;&lt;/span&gt;I think there is a scenario where this new feature shines without the aforementioned caviat. I was on a project once where we used serialization to persist objects to disk instead of persisting them to a traditional RDBMS. The application was a desktop app similar to Microsoft Word where the user worked with "documents" and could save and load them as files. It seemed like a natural fit to have the document be some kind of object model that was persisted to and from disk via serialization. Everything worked fine until we started thinking about version 2 of our app. While there were ways to relax the versioning restrictions in in .NET 1.x serialization when the schema didn't change, the moment we added new fields to our types, the fated serialization errors occurred.&lt;br /&gt;&lt;br /&gt;But this scenario was different than Remoting in that we only had to worry about the difference in type data (schema), not logic. This is because we were not interoperating with the older type. We just had to figure out a way to transform data serialized by an old type so that it would fit the schema of a new type. In the end, we ended up writing an elaborate upgrade system that would parse the serialized data itself (which in our case was SOAP XML) and transform it to a newer version, adding fields as necessary. This was not an ideal solution. First, it was very complex. Second, it essentially broke the rule of encapsulation, since the upgrader tool had to have intimate knowledge of the fields of the type is was upgrading. It would have been better to place the responsibility of managing new fields of a type within the type itself. I believe the new VTS features of .NET 2.0 allow us to do just that and with minimal effort.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/35759427-4507364121806570664?l=beyondthispoint.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/35759427/posts/default/4507364121806570664'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/35759427/posts/default/4507364121806570664'/><link rel='alternate' type='text/html' href='http://beyondthispoint.blogspot.com/2006/10/version-tolerant-serialization.html' title='Version Tolerant Serialization'/><author><name>Pete</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-35759427.post-3655186111801889916</id><published>2005-04-05T00:42:00.000-05:00</published><updated>2006-10-09T15:02:29.250-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Software'/><title type='text'>Clearscreen CAPTCHA Control now supports CS 1.0!</title><content type='html'>Wow, that didn't take long. &lt;a class="blines3" title="Link outside of this blog" href="http://blogs.clearscreen.com/migs/" target="_blank"&gt;Miguel Jiménez&lt;/a&gt; has updated his CAPTCHA control to &lt;a class="blines3" title="Link outside of this blog" href="http://blogs.clearscreen.com/migs/archive/2005/04/05/1270.aspx" target="_blank"&gt;version 1.4&lt;/a&gt; and it now works with Community Server! And here I was all set to hack the source code myself so I could get something up and running. Oh well, I've got better things to do with my time anyway. ;-)&lt;br /&gt;&lt;br /&gt;And the good news is that it actually works! Check it out; it's running on my site.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/35759427-3655186111801889916?l=beyondthispoint.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/35759427/posts/default/3655186111801889916'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/35759427/posts/default/3655186111801889916'/><link rel='alternate' type='text/html' href='http://beyondthispoint.blogspot.com/2006/10/clearscreen-captcha-control-now.html' title='Clearscreen CAPTCHA Control now supports CS 1.0!'/><author><name>Pete</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-35759427.post-9108356538940421975</id><published>2005-03-28T12:34:00.000-06:00</published><updated>2006-10-09T15:00:57.074-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Faith'/><title type='text'>Evolution and creation in the classroom</title><content type='html'>Check out this &lt;a class="blines3" title="Link outside of this blog" href="http://www.jasonbock.net/JB/Default.aspx?blog=entry.20050328T032104" target="_blank"&gt;interesting discussion&lt;/a&gt; on a recent news article talking about a possible Florida mandate to force equal time in the college science classroom for both evolution and creation.&lt;br /&gt;&lt;br /&gt;Feel free to chime in; unlike my (insert adjective here) blog engine, Jason's accepts anonymous comments.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/35759427-9108356538940421975?l=beyondthispoint.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://beyondthispoint.blogspot.com/feeds/9108356538940421975/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=35759427&amp;postID=9108356538940421975' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/35759427/posts/default/9108356538940421975'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/35759427/posts/default/9108356538940421975'/><link rel='alternate' type='text/html' href='http://beyondthispoint.blogspot.com/2005/03/evolution-and-creation-in-classroom.html' title='Evolution and creation in the classroom'/><author><name>Pete</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-35759427.post-6169631018593754572</id><published>2005-03-23T14:58:00.000-06:00</published><updated>2006-10-09T14:58:21.776-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Software'/><title type='text'>Community Server and CAPTCHA</title><content type='html'>So far I've been pretty thrilled with &lt;a class="blines3" title="Link outside of this blog" href="http://communityserver.org/" target="_blank"&gt;Community Server&lt;/a&gt; as my blog engine. One of the things I didn't set up right away was anonymous comments for my blog entries. I wasn't sure how CS would handle spam comments, so I left it disabled for the time being. Well, after doing some research this evening I found a few things about controlling spam comments in a blog and, specifically, in CS:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;The most popular deterrent is to implement a &lt;a class="blines3" title="Link outside of this blog" href="http://www.captcha.net/" target="_blank"&gt;CAPTCHA &lt;/a&gt;control. &lt;/li&gt;&lt;li&gt;A very popular CAPTCHA implementation for ASP.NET is &lt;a class="blines3" title="Link outside of this blog" href="http://blogs.clearscreen.com/migs/" target="_blank"&gt;Miguel Jimenez's&lt;/a&gt; &lt;a class="blines3" title="Link outside of this blog" href="http://blogs.clearscreen.com/migs/archive/2005/02/01/905.aspx" target="_blank"&gt;Clearscreen SharpHIP HIP-CAPTCHA Control&lt;/a&gt;. &lt;/li&gt;&lt;li&gt;The Clearscreen SharpHIP HIP-CAPTCHA Control was specifically designed with the .Text blog engine in mind, the predecessor to Community Server. However, it can be used with any traditional ASP.NET web form. &lt;/li&gt;&lt;li&gt;And, one more minor thing: the Clearscreen SharpHIP HIP-CAPTCHA Control doesn't work with Community Server.&lt;br /&gt;Rats.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;Community Server does some pretty funky stuff with the request URLs, which, I'm sure is what's hosing up the control. Anyway, I'm going to keep an eye on Miguel's blog to see if he posts an update that's compatible with Community Server.&lt;/p&gt;&lt;p&gt;If anyone else out there has a better solution (besides using a different blog engine), please let me know. Of course, you're going to have to log in to my CS site in order to leave that comment. Oh, the irony.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/35759427-6169631018593754572?l=beyondthispoint.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://beyondthispoint.blogspot.com/feeds/6169631018593754572/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=35759427&amp;postID=6169631018593754572' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/35759427/posts/default/6169631018593754572'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/35759427/posts/default/6169631018593754572'/><link rel='alternate' type='text/html' href='http://beyondthispoint.blogspot.com/2006/10/community-server-and-captcha.html' title='Community Server and CAPTCHA'/><author><name>Pete</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-35759427.post-7188306003905182887</id><published>2004-09-09T09:17:00.000-05:00</published><updated>2006-10-09T14:36:19.927-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Software'/><title type='text'>Archiving Outlook</title><content type='html'>I’ve finally discovered a useful reason to keep a blog ;) – it’s a good place to write down how you did something (solved a problem) so you can, by some fluke of a chance, go back years later and recover the solution. Of course, with problems and solutions that involve technology, chances are you’ll never run across the same problem again (at least not in the same form), but hey, at least you can show the world how you did it.&lt;br /&gt;&lt;br /&gt;My problem was that of properly archiving Outlook email. For years I’ve used Outlook for both work and personal email and have throughout those years archived emails to Outlook’s proprietary .pst file (Personal Folder File) format. PST files are great in that in one giant file you can store it all – emails, contacts, calendar and task items, etc. The problem with them is they can really only be opened by Outlook and when Outlook opens one, it needs read/write access to the file. This later point isn’t a big deal until you’ve burned all of your PST files to a CD-ROM. Outlook can’t open them when they’re sitting on a CD, which is a read-only format. So in the untimely event that you want to search for an email, you have to copy each PST file from your CD to some place that’s read/writable (like your hard drive). Typically the PST files are quite large so this can be a very cumbersome and painful process (especially if after the search you still didn’t find that email you were looking for).&lt;br /&gt;&lt;br /&gt;A more desirable archive format would be to have all of the emails in separate text files that conformed to the RFC-822 format (circa 1974 or whatever date that RFC hit the streets). These files typically have the .eml file extension and just about any email client can open them (hehe, except Outlook). If you don’t have one handy you can always use good ‘ol Notepad.&lt;br /&gt;Unfortunately the road from PST to .eml was a bit rocky and I had to play around with several different techniques to finally find a process that seemed to work well. My first thought was to import all of my email into Outlook Express. You can drag and drop emails from Outlook Express to Windows Explorer and it will create individual .eml files for each email for you automatically. Unfortunately, it has a very limited file naming scheme (it uses {email subject}.eml as the file name); if two emails have the same subject, it doesn’t know enough to give the second .eml file a unique name. The other thing is it would be nice to have more control over names of the .eml files. Names with only the subject in them don’t make for very easy browsing.&lt;br /&gt;&lt;br /&gt;Fortunately, someone out there had the same opinion and developed an application called &lt;a href="http://expressarchiver.com/"&gt;Express Archiver&lt;/a&gt;. This handy app reads the folder structure of an Outlook Express profile and allows you to archive the contained messages in a number of ways, including as separate .eml files. It also allows you to customize how those .eml files are named. I chose to use the {date}{from}{subject} as the naming scheme of my Inbox emails and {date}{to}{subject} as the naming scheme of my Sent Items.&lt;br /&gt;&lt;br /&gt;This application worked very well. The only “difficult” part was jumping between Outlook, Outlook Express, and Express Archiver to move emails from the PST format all the way to the nice archive-friendly .eml file format. But once I got the hang of it, it wasn’t too bad. As a final touch I was able to zip up all my .eml files and they compressed down a smaller size than even the PST files did. They can also live on a CD and I can browse and search them without having to use Outlook or copy them to my local hard drive.&lt;br /&gt;Very cool!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/35759427-7188306003905182887?l=beyondthispoint.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://beyondthispoint.blogspot.com/feeds/7188306003905182887/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=35759427&amp;postID=7188306003905182887' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/35759427/posts/default/7188306003905182887'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/35759427/posts/default/7188306003905182887'/><link rel='alternate' type='text/html' href='http://beyondthispoint.blogspot.com/2004/09/archiving-outlook.html' title='Archiving Outlook'/><author><name>Pete</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry></feed>
