<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:creativeCommons="http://backend.userland.com/creativeCommonsRssModule" version="2.0"><channel><title>LeeDumond.com</title><link>http://leedumond.com/</link><description>My musings about .NET and what not</description><generator>Graffiti CMS 1.2 (build 1.2.0.2308)</generator><lastBuildDate>Fri, 14 May 2010 12:42:00 GMT</lastBuildDate><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.feedburner.com/leedumond" /><feedburner:info xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" uri="leedumond" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><creativeCommons:license>http://creativecommons.org/licenses/by-sa/2.0/</creativeCommons:license><feedburner:emailServiceId xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0">leedumond</feedburner:emailServiceId><feedburner:feedburnerHostname xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0">http://feedburner.google.com</feedburner:feedburnerHostname><feedburner:feedFlare xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" href="http://add.my.yahoo.com/rss?url=http%3A%2F%2Ffeeds.feedburner.com%2Fleedumond" src="http://us.i1.yimg.com/us.yimg.com/i/us/my/addtomyyahoo4.gif">Subscribe with My Yahoo!</feedburner:feedFlare><feedburner:feedFlare xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" href="http://www.newsgator.com/ngs/subscriber/subext.aspx?url=http%3A%2F%2Ffeeds.feedburner.com%2Fleedumond" src="http://www.newsgator.com/images/ngsub1.gif">Subscribe with NewsGator</feedburner:feedFlare><feedburner:feedFlare xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" href="http://feeds.my.aol.com/add.jsp?url=http%3A%2F%2Ffeeds.feedburner.com%2Fleedumond" src="http://o.aolcdn.com/favorites.my.aol.com/webmaster/ffclient/webroot/locale/en-US/images/myAOLButtonSmall.gif">Subscribe with My AOL</feedburner:feedFlare><feedburner:feedFlare xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" href="http://www.bloglines.com/sub/http://feeds.feedburner.com/leedumond" src="http://www.bloglines.com/images/sub_modern11.gif">Subscribe with Bloglines</feedburner:feedFlare><feedburner:feedFlare xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" href="http://www.netvibes.com/subscribe.php?url=http%3A%2F%2Ffeeds.feedburner.com%2Fleedumond" src="http://www.netvibes.com/img/add2netvibes.gif">Subscribe with Netvibes</feedburner:feedFlare><feedburner:feedFlare xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" href="http://fusion.google.com/add?feedurl=http%3A%2F%2Ffeeds.feedburner.com%2Fleedumond" src="http://buttons.googlesyndication.com/fusion/add.gif">Subscribe with Google</feedburner:feedFlare><feedburner:feedFlare xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" href="http://www.pageflakes.com/subscribe.aspx?url=http%3A%2F%2Ffeeds.feedburner.com%2Fleedumond" src="http://www.pageflakes.com/ImageFile.ashx?instanceId=Static_4&amp;fileName=ATP_blu_91x17.gif">Subscribe with Pageflakes</feedburner:feedFlare><item><title>Using a GUID as an EntityKey in Entity Framework 4</title><link>http://leedumond.com/blog/using-a-guid-as-an-entitykey-in-entity-framework-4/</link><pubDate>Fri, 14 May 2010 14:42:00 GMT</pubDate><guid isPermaLink="true">http://leedumond.com/blog/using-a-guid-as-an-entitykey-in-entity-framework-4/</guid><dc:creator>Lee Dumond</dc:creator><slash:comments>29</slash:comments><category domain="http://leedumond.com/blog/">Blog</category><description>&lt;p&gt;Do you have tables that use GUIDs (columns of type &lt;tt&gt;uniqueidentifier&lt;/tt&gt;) as primary keys in SQL Server? This scenario is now fully supported in Entity Framework 4, as long as you remember one little gotcha.&lt;/p&gt;&lt;hr /&gt;
&lt;p&gt;Yes, it&amp;rsquo;s been months since I&amp;rsquo;ve blogged, as I&amp;rsquo;ve been hammer-down on &lt;a target="_blank" href="http://leedumond.com/blog/wanna-help-me-write-a-book/"&gt;my upcoming book&lt;/a&gt; since the beginning of the year. However, I thought I&amp;rsquo;d put out a quick post as a heads-up to my fellow developers who, like me, are trying to get up to speed with Entity Framework 4. I recently ran into an issue concerning a rather common database scenario, which bugged me for the better part of a day. Surprisingly, I found no mention of it on any search engine. Hopefully, this post should help to remedy that situation.&lt;/p&gt;
&lt;h3&gt;The Scenario&lt;/h3&gt;
&lt;p&gt;Like many developers, I happen to be a fan of using &lt;tt&gt;uniqueidentifier &lt;/tt&gt;columns as primary keys on database tables (as opposed to the other common practice of using integers). While I don&amp;rsquo;t do this &lt;em&gt;all&lt;/em&gt; the time, in many cases I think it just makes sense, especially when the records in and of themselves don&amp;rsquo;t share a &amp;ldquo;sequential&amp;rdquo; relationship to one another. I understand this is a rather controversial topic in some circles; with good arguments on both sides. Quite frankly, whether you personally &lt;em&gt;agree&lt;/em&gt; with this practice or not is beside the point. For the purposes of this discussion, it&amp;rsquo;s just important that you understand two things: &lt;strong&gt;one&lt;/strong&gt;, that &lt;strong&gt;this done all the time&lt;/strong&gt;; and &lt;strong&gt;two&lt;/strong&gt;, that &lt;strong&gt;this scenario was not properly supported under Entity Framework V1&lt;/strong&gt;.&lt;/p&gt;
&lt;h3&gt;The Problem&lt;/h3&gt;
&lt;p&gt;The specific issue concerns how identity values are assigned to new records under this scenario. Usually, a new GUID is generated in the database by using the SQL Server &lt;tt&gt;NEWID()&lt;/tt&gt; function as the default binding for the PK column. In Entity Framework, as you probably know, when you insert a new record by calling the &lt;tt&gt;SaveChanges&lt;/tt&gt; method on the &lt;tt&gt;ObjectContext&lt;/tt&gt;, you are supposed to be able to access &lt;em&gt;all&lt;/em&gt; of the properties of that record, &lt;em&gt;including the new server-generated ID.&lt;/em&gt; In order for this to work, the provider has to support returning the identity value from the server. Under EF 1, the provider didn&amp;rsquo;t generate the required TSQL to make that happen.&lt;/p&gt;
&lt;p&gt;When an entity is created from a database table in Entity Framework, it uses the primary key (or keys) from the table to create an &lt;tt&gt;EntityKey&lt;/tt&gt;. Under EF1, there was certainly no problem &lt;em&gt;creating&lt;/em&gt; entities with &lt;tt&gt;EntityKey&lt;/tt&gt; of type &lt;tt&gt;Guid&lt;/tt&gt;. You could &lt;em&gt;query&lt;/em&gt; those entities just fine. You could even &lt;em&gt;update&lt;/em&gt; them. But &lt;strong&gt;doing inserts with server-generated GUID values was a big fat no-go.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The usual workaround for this was to simply forgo generating the identity values in the database with the &lt;tt&gt;NEWID()&lt;/tt&gt; function, and instead&amp;nbsp; generate it on the client and pass it in as part of the entity&amp;rsquo;s constructor, as you can see in Listing 1.&lt;/p&gt;
&lt;pre class="brush: csharp; auto-links: false; highlight: [5];"&gt;
using (EFSampleEntities context = new EFSampleEntities())
{
   Motorcycle motorcycle = new Motorcycle
                              {
                                 ID = Guid.NewGuid(),
                                 Make = &amp;quot;Suzuki&amp;quot;, 
                                 Model = &amp;quot;B-King&amp;quot;, 
                                 EngineCC = 1340
                              };
   context.Motorcycles.AddObject(motorcycle);
   context.SaveChanges();
}&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Listing 1&lt;/strong&gt;&lt;/p&gt;
&lt;h3&gt;How It Works Under Entity Framework 4&lt;/h3&gt;
&lt;p&gt;Obviously, failing to support server-generated GUID identity values was one (unfortunately, one of the many) shortcomings of Entity Framework V1. This was something the EF Team promised to address under the second version of the Entity Framework (dubbed Entity Framework &lt;strong&gt;&amp;ldquo;4&amp;rdquo;&lt;/strong&gt; by the marketing geniuses at Microsoft, in an effort to make the EF version number match the .NET Framework version number, while thoroughly confusing us at the same time).&lt;/p&gt;
&lt;p&gt;I was eager to give it a go the other day, so I set up a little test project with a SQL Server 2008 database, one tiny little table and a single-entity model. As you can see in Figure 1, the ID column of this table in the database is a primary key, is of type &lt;tt&gt;uniqueidentifier&lt;/tt&gt;, and derives its default value from the &lt;tt&gt;NEWID()&lt;/tt&gt; function.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://leedumond.com/files/media/image/WindowsLiveWriter/UsingaGUIDasanEntityKeyinEntityFramework_11ECE/image_2.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" width="411" height="213" src="http://leedumond.com/files/media/image/WindowsLiveWriter/UsingaGUIDasanEntityKeyinEntityFramework_11ECE/image_thumb.png" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Figure 1&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The EF Designer screenshot in Figure 2 shows that the corresponding&amp;nbsp; &lt;tt&gt;Motorcycle &lt;/tt&gt;entity has an &lt;tt&gt;EntityKey&lt;/tt&gt; of type &lt;tt&gt;Guid&lt;/tt&gt; that maps to the table&amp;rsquo;s ID column.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://leedumond.com/files/media/image/WindowsLiveWriter/UsingaGUIDasanEntityKeyinEntityFramework_11ECE/image_4.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" width="304" height="348" src="http://leedumond.com/files/media/image/WindowsLiveWriter/UsingaGUIDasanEntityKeyinEntityFramework_11ECE/image_thumb_1.png" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;strong&gt;Figure 2&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Fantastic. So, I proceed to run the code from Listing 2 below, except this time I don&amp;rsquo;t generate the identity value on the client. The database is going to do that for me, thank you very much!&lt;/p&gt;
&lt;pre class="brush: csharp; auto-links: false;"&gt;
class Program
{
   static void Main(string[] args)
   {
      InsertRecord(&amp;quot;Suzuki&amp;quot;, &amp;quot;B-King&amp;quot;, 1340);
   }

   private static void InsertRecord(string make, string model, int engineCC)
   {
      using (EFSampleEntities context = new EFSampleEntities())
      {
         Motorcycle motorcycle = new Motorcycle
                                    {
                                       Make = make, 
                                       Model = model, 
                                       EngineCC = engineCC
                                    };
         context.Motorcycles.AddObject(motorcycle);
         context.SaveChanges();
      }
   }
}&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Listing 2&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;I type this in, press F5, and it all &lt;em&gt;appears&lt;/em&gt; to work. &lt;em&gt;&lt;strong&gt;Or&amp;hellip; does it?&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The answer is, yes and no. The record&amp;nbsp;&lt;em&gt;did&lt;/em&gt; get inserted into the database. But take a look at Figure 3:&lt;/p&gt;
&lt;p&gt;&lt;a href="http://leedumond.com/files/media/image/WindowsLiveWriter/UsingaGUIDasanEntityKeyinEntityFramework_11ECE/image_8.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" width="404" height="114" src="http://leedumond.com/files/media/image/WindowsLiveWriter/UsingaGUIDasanEntityKeyinEntityFramework_11ECE/image_thumb_3.png" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Figure 3&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;The new GUID is all zeros!&lt;/em&gt; Well, that&amp;rsquo;s not cool. And so I go poking around the IDE for a few minutes and discover that Entity Framework has, for some reason, failed to recognize ID as an identity field that is generated on the database side. That is indicated by the &lt;tt&gt;StoreGeneratedPattern&lt;/tt&gt; property of the ID property, visible in the Properties window, which was still set to &lt;tt&gt;None&lt;/tt&gt;. No problem&amp;hellip; clickety click, and that&amp;rsquo;s all fixed.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://leedumond.com/files/media/image/WindowsLiveWriter/UsingaGUIDasanEntityKeyinEntityFramework_11ECE/image_10.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" width="215" height="244" src="http://leedumond.com/files/media/image/WindowsLiveWriter/UsingaGUIDasanEntityKeyinEntityFramework_11ECE/image_thumb_4.png" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Figure 4&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;And so I run the code again and&amp;hellip; whaaaaaaaa?!? T&lt;em&gt;he new GUID is still 00000000-0000-0000-0000-000000000000.&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;The Payoff&lt;/h3&gt;
&lt;p&gt;Okay, at this point I&amp;rsquo;ll admit I&amp;rsquo;m completely stuck for an answer. I write to my friend &lt;a target="_blank" href="http://thedatafarm.com/"&gt;Julie Lerman&lt;/a&gt;, author of &lt;a target="_blank" href="http://www.amazon.com/Programming-Entity-Framework-Julia-Lerman/dp/059652028X"&gt;Programming Entity Framework&lt;/a&gt; and one of the most knowledgeable EF developers on planet Earth, and even &lt;em&gt;she&lt;/em&gt; is stuck for an answer &amp;ndash; a rare event indeed. I post a question on Stack Overflow. No answers there either.&lt;/p&gt;
&lt;p&gt;Now, I&amp;rsquo;m wondering if this scenario really &lt;em&gt;is&lt;/em&gt; supported in EF4 at all, as the team promised it would be. As seemed to happen so often in the first version of Entity Framework, it was difficult to know if it was the framework that was causing the problem, or if it was just me.&lt;/p&gt;
&lt;p&gt;One pot of coffee, half a tank of premium unleaded, and a few hours of sleep later, I figured it out.&lt;/p&gt;
&lt;p&gt;The issue lies in the fact that the Entity Framework designer is a representation of &lt;strong&gt;only the conceptual part of the data model.&lt;/strong&gt; When you set the &lt;tt&gt;StoreGeneratedPattern&lt;/tt&gt; attribute on an entity property using the designer, it merely adds an annotation to the property in the CDSL part of the underlying &lt;tt&gt;.edmx&lt;/tt&gt; file.&lt;/p&gt;
&lt;pre class="brush: xml; auto-links: false; highlight: [11];"&gt;
&amp;lt;!-- CSDL content --&amp;gt;
&amp;lt;edmx:ConceptualModels&amp;gt;
  &amp;lt;Schema Namespace=&amp;quot;EFSampleModel&amp;quot; Alias=&amp;quot;Self&amp;quot; xmlns:annotation=&amp;quot;http://schemas.microsoft.com/ado/2009/02/edm/annotation&amp;quot; xmlns=&amp;quot;http://schemas.microsoft.com/ado/2008/09/edm&amp;quot;&amp;gt;
    &amp;lt;EntityContainer Name=&amp;quot;EFSampleEntities&amp;quot; annotation:LazyLoadingEnabled=&amp;quot;true&amp;quot;&amp;gt;
      &amp;lt;EntitySet Name=&amp;quot;Motorcycles&amp;quot; EntityType=&amp;quot;EFSampleModel.Motorcycle&amp;quot; /&amp;gt;
    &amp;lt;/EntityContainer&amp;gt;
    &amp;lt;EntityType Name=&amp;quot;Motorcycle&amp;quot;&amp;gt;
      &amp;lt;Key&amp;gt;
        &amp;lt;PropertyRef Name=&amp;quot;ID&amp;quot; /&amp;gt;
      &amp;lt;/Key&amp;gt;
      &amp;lt;Property Name=&amp;quot;ID&amp;quot; Type=&amp;quot;Guid&amp;quot; Nullable=&amp;quot;false&amp;quot; annotation:StoreGeneratedPattern=&amp;quot;Identity&amp;quot; /&amp;gt;
      &amp;lt;Property Name=&amp;quot;Make&amp;quot; Type=&amp;quot;String&amp;quot; Nullable=&amp;quot;false&amp;quot; MaxLength=&amp;quot;50&amp;quot; Unicode=&amp;quot;true&amp;quot; FixedLength=&amp;quot;false&amp;quot; /&amp;gt;
      &amp;lt;Property Name=&amp;quot;Model&amp;quot; Type=&amp;quot;String&amp;quot; Nullable=&amp;quot;false&amp;quot; MaxLength=&amp;quot;50&amp;quot; Unicode=&amp;quot;true&amp;quot; FixedLength=&amp;quot;false&amp;quot; /&amp;gt;
      &amp;lt;Property Name=&amp;quot;EngineCC&amp;quot; Type=&amp;quot;Int32&amp;quot; Nullable=&amp;quot;false&amp;quot; /&amp;gt;
    &amp;lt;/EntityType&amp;gt;
  &amp;lt;/Schema&amp;gt;
&amp;lt;/edmx:ConceptualModels&amp;gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Listing 3&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;However, &lt;strong&gt;this action does not add the required attribute to the storage part of the data model.&lt;/strong&gt; You actually have to open the &lt;tt&gt;.edmx&lt;/tt&gt;&amp;nbsp; file in an XML editor, find the identity column in the SSDL, and add the &lt;tt&gt;StoreGeneratedPattern&lt;/tt&gt; attibute by hand.&lt;/p&gt;
&lt;pre class="brush: xml; auto-links: false; highlight: [11];"&gt;
&amp;lt;!-- SSDL content --&amp;gt;
&amp;lt;edmx:StorageModels&amp;gt;
  &amp;lt;Schema Namespace=&amp;quot;EFSampleModel.Store&amp;quot; Alias=&amp;quot;Self&amp;quot; Provider=&amp;quot;System.Data.SqlClient&amp;quot; ProviderManifestToken=&amp;quot;2008&amp;quot; xmlns:store=&amp;quot;http://schemas.microsoft.com/ado/2007/12/edm/EntityStoreSchemaGenerator&amp;quot; xmlns=&amp;quot;http://schemas.microsoft.com/ado/2009/02/edm/ssdl&amp;quot;&amp;gt;
    &amp;lt;EntityContainer Name=&amp;quot;EFSampleModelStoreContainer&amp;quot;&amp;gt;
      &amp;lt;EntitySet Name=&amp;quot;Motorcycles&amp;quot; EntityType=&amp;quot;EFSampleModel.Store.Motorcycles&amp;quot; store:Type=&amp;quot;Tables&amp;quot; Schema=&amp;quot;dbo&amp;quot; /&amp;gt;
    &amp;lt;/EntityContainer&amp;gt;
    &amp;lt;EntityType Name=&amp;quot;Motorcycles&amp;quot;&amp;gt;
      &amp;lt;Key&amp;gt;
        &amp;lt;PropertyRef Name=&amp;quot;ID&amp;quot; /&amp;gt;
      &amp;lt;/Key&amp;gt;
      &amp;lt;Property Name=&amp;quot;ID&amp;quot; Type=&amp;quot;uniqueidentifier&amp;quot; Nullable=&amp;quot;false&amp;quot; StoreGeneratedPattern=&amp;quot;Identity&amp;quot; /&amp;gt;           
      &amp;lt;Property Name=&amp;quot;Make&amp;quot; Type=&amp;quot;nvarchar&amp;quot; Nullable=&amp;quot;false&amp;quot; MaxLength=&amp;quot;50&amp;quot; /&amp;gt;
      &amp;lt;Property Name=&amp;quot;Model&amp;quot; Type=&amp;quot;nvarchar&amp;quot; Nullable=&amp;quot;false&amp;quot; MaxLength=&amp;quot;50&amp;quot; /&amp;gt;
      &amp;lt;Property Name=&amp;quot;EngineCC&amp;quot; Type=&amp;quot;int&amp;quot; Nullable=&amp;quot;false&amp;quot; /&amp;gt;
    &amp;lt;/EntityType&amp;gt;
  &amp;lt;/Schema&amp;gt;
&amp;lt;/edmx:StorageModels&amp;gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Listing 4&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Once you do this, inserts performed by the provider when calling &lt;tt&gt;SaveChanges&lt;/tt&gt; will allow the database to generate the identity value properly, and retrieve the value reliably so that you can work with it through the &lt;tt&gt;ObjectContext&lt;/tt&gt;.&lt;/p&gt;
&lt;p&gt;In case you are wondering about the T-SQL that the provider actually generates to do the insert, then retrieve the GUID, you can find it below in Listing 5. It creates a temporary table to hold the identity value, uses an &lt;tt&gt;OUTPUT&lt;/tt&gt; clause to grab the new value and insert it into the temporary table, then does a join between the temporary and main tables to select and return the value. Pretty clever stuff.&lt;/p&gt;
&lt;pre class="brush: sql; auto-links: false;"&gt;
declare  @generated_keys  table(
                                [ID] uniqueidentifier
                                )
insert [dbo].[Motorcycles]
      ([Make],
       [Model],
       [EngineCC])
output inserted.[ID] into @generated_keys
values('Suzuki' /* @0 */,
       'B-King' /* @1 */,
       1340 /* @2 */)
select t.[ID]
from   @generated_keys as g
       join [dbo].[Motorcycles] as t
         on g.[ID] = t.[ID]
where  @@ROWCOUNT &amp;gt; 0&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Listing 5&lt;/strong&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/leedumond?a=clD9JduTnOY:aiDrX4pmcs4:Dh-6e3sOTMo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/leedumond?d=Dh-6e3sOTMo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/leedumond?a=clD9JduTnOY:aiDrX4pmcs4:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/leedumond?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/leedumond?a=clD9JduTnOY:aiDrX4pmcs4:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/leedumond?i=clD9JduTnOY:aiDrX4pmcs4:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/leedumond?a=clD9JduTnOY:aiDrX4pmcs4:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/leedumond?i=clD9JduTnOY:aiDrX4pmcs4:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/leedumond?a=clD9JduTnOY:aiDrX4pmcs4:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/leedumond?i=clD9JduTnOY:aiDrX4pmcs4:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/leedumond?a=clD9JduTnOY:aiDrX4pmcs4:l6gmwiTKsz0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/leedumond?d=l6gmwiTKsz0" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/leedumond?a=clD9JduTnOY:aiDrX4pmcs4:TzevzKxY174"&gt;&lt;img src="http://feeds.feedburner.com/~ff/leedumond?d=TzevzKxY174" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/leedumond?a=clD9JduTnOY:aiDrX4pmcs4:dnMXMwOfBR0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/leedumond?d=dnMXMwOfBR0" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/leedumond/~4/clD9JduTnOY" height="1" width="1"/&gt;</description></item><item><title>New in .NET 4: Don’t Forget to Dispose() your SmtpClient Instances</title><link>http://leedumond.com/blog/new-in-net-4-dont-forget-to-dispose-your-smtpclient-instances/</link><pubDate>Tue, 23 Feb 2010 15:34:00 GMT</pubDate><guid isPermaLink="true">http://leedumond.com/blog/new-in-net-4-dont-forget-to-dispose-your-smtpclient-instances/</guid><dc:creator>Lee Dumond</dc:creator><slash:comments>31</slash:comments><category domain="http://leedumond.com/blog/">Blog</category><description>&lt;p&gt;It seems the System.Net.Mail.SmptClient class has gotten a bit of a facelift in .NET 4.&lt;/p&gt;&lt;hr /&gt;
&lt;p&gt;Wow, it seems like forever since I&amp;rsquo;ve written a blog post. That&amp;rsquo;s because I&amp;rsquo;ve been heads-down on my &lt;a target="_blank" href="http://leedumond.com/blog/wanna-help-me-write-a-book/"&gt;new book&lt;/a&gt;, part of which involves getting up to speed on all of the newest features in ASP.NET 4.0.&lt;/p&gt;
&lt;p&gt;I gotta admit that this one caught me by surprise though.&lt;/p&gt;
&lt;p&gt;For the record, I will admit that I'm a little fanatical about &lt;a target="_blank" href="http://leedumond.com/blog/about-disposing-the-datacontext/"&gt;disposing objects&lt;/a&gt;. As you (I hope) already know, that means remembering to call the &lt;tt&gt;Dispose()&lt;/tt&gt; method on objects whose classes implement the &lt;a target="_blank" href="http://msdn.microsoft.com/en-us/library/system.idisposable(VS.100).aspx"&gt;IDisposable&lt;/a&gt; interface. It&amp;rsquo;s often tricky to remember which objects you need to dispose, since in many cases it&amp;rsquo;s not immediately obvious.&lt;/p&gt;
&lt;p&gt;I thought I had this disposing stuff pretty much in my back pocket. That&amp;rsquo;s why I was so surprised when running some static code analysis on my &lt;a target="_blank" href="http://cyclemania.codeplex.com/"&gt;Cyclemania&lt;/a&gt; project and seeing the following warning:&lt;/p&gt;
&lt;p class="syntax_hilite"&gt;Warning&amp;nbsp;&amp;nbsp;&amp;nbsp; 20&amp;nbsp;&amp;nbsp;&amp;nbsp; CA2000 : Microsoft.Reliability : In method 'TestEmail.btnSendTest_Click(object, EventArgs)', call System.IDisposable.Dispose on object 'new SmtpClient()' before all references to it are out of scope.&lt;/p&gt;
&lt;p&gt;Whaaa??? What&amp;rsquo;s this? This can&amp;rsquo;t be right&amp;hellip;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://leedumond.com/files/media/image/WindowsLiveWriter/New.NET4DontForgettoDisposeyourSmtpClien_6DF0/image_2.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" width="400" height="164" src="http://leedumond.com/files/media/image/WindowsLiveWriter/New.NET4DontForgettoDisposeyourSmtpClien_6DF0/image_thumb.png" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Well, as it turns out, &lt;strong&gt;the SmtpClient class does implement IDisposable, starting in .NET 4.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://leedumond.com/files/media/image/WindowsLiveWriter/New.NET4DontForgettoDisposeyourSmtpClien_6DF0/image_4.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" width="400" height="178" src="http://leedumond.com/files/media/image/WindowsLiveWriter/New.NET4DontForgettoDisposeyourSmtpClien_6DF0/image_thumb_1.png" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This is actually a good thing. The .NET 4 implementation of the SmtpClient class pools SMTP connections, which avoids the overhead of re-establishing a connection for every message when sending multiple messages to the same server. Since the same connection can be reused over and over in an application, there&amp;rsquo;s no way to determine when an application is finished using the client object. Calling &lt;tt&gt;Dispose()&lt;/tt&gt; on it iterates through all established connections and sends a QUIT message, which allows the server to free up its own connection resources and begin processing. The TCP connection is then terminated, and any resources used by the underlying socket are released.&lt;/p&gt;
&lt;p&gt;This means that, when you&amp;rsquo;re using SmtpClient to send mail in your .NET 4 application, you should adhere to the following:&lt;/p&gt;
&lt;pre class="brush: csharp; auto-links: false;"&gt;
// correct
try
{
   using (MailMessage message = new MailMessage())
   {
      message.Subject = &amp;quot;Test Email&amp;quot;;
      message.Body = &amp;quot;This is a test email from .NET 4!&amp;quot;;
      message.To.Add(&amp;quot;test@example.com&amp;quot;);

      using (SmtpClient client = new SmtpClient())
      {
         client.Send(message);
      }
   }
}         
catch (SmtpException)
{
   // handle exception here
}


// wrong
try
{
  using (MailMessage message = new MailMessage())
  {               
     message.Subject = &amp;quot;Test Email&amp;quot;;
     message.Body = &amp;quot;This is a test email from .NET 3.5!&amp;quot;;                  
     message.To.Add(&amp;quot;test@example.com&amp;quot;);

     new SmtpClient().Send(message);            
  }            
}         
catch (SmtpException)
{
  // handle exception here
}&lt;/pre&gt;
&lt;p&gt;Just one more .NET 4 &amp;ldquo;gotcha&amp;rdquo; to keep in mind.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;UPDATE: &lt;/strong&gt;&lt;a target="_blank" href="http://agilology.blogspot.com"&gt;&lt;strong&gt;Jeff Tucker&lt;/strong&gt;&lt;/a&gt;&lt;strong&gt; (the guy who implemented this new feature) also points out in the comments below that SmtpClient will also close existing connections if you switch the host or port. Nice!&lt;/strong&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/leedumond?a=oeoE9m86arE:JP4uy8abaWM:Dh-6e3sOTMo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/leedumond?d=Dh-6e3sOTMo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/leedumond?a=oeoE9m86arE:JP4uy8abaWM:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/leedumond?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/leedumond?a=oeoE9m86arE:JP4uy8abaWM:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/leedumond?i=oeoE9m86arE:JP4uy8abaWM:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/leedumond?a=oeoE9m86arE:JP4uy8abaWM:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/leedumond?i=oeoE9m86arE:JP4uy8abaWM:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/leedumond?a=oeoE9m86arE:JP4uy8abaWM:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/leedumond?i=oeoE9m86arE:JP4uy8abaWM:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/leedumond?a=oeoE9m86arE:JP4uy8abaWM:l6gmwiTKsz0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/leedumond?d=l6gmwiTKsz0" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/leedumond?a=oeoE9m86arE:JP4uy8abaWM:TzevzKxY174"&gt;&lt;img src="http://feeds.feedburner.com/~ff/leedumond?d=TzevzKxY174" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/leedumond?a=oeoE9m86arE:JP4uy8abaWM:dnMXMwOfBR0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/leedumond?d=dnMXMwOfBR0" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/leedumond/~4/oeoE9m86arE" height="1" width="1"/&gt;</description></item><item><title>Wanna Help Me Write a Book?</title><link>http://leedumond.com/blog/wanna-help-me-write-a-book/</link><pubDate>Mon, 07 Dec 2009 15:07:00 GMT</pubDate><guid isPermaLink="true">http://leedumond.com/blog/wanna-help-me-write-a-book/</guid><dc:creator>Lee Dumond</dc:creator><slash:comments>42</slash:comments><category domain="http://leedumond.com/blog/">Blog</category><description>&lt;p&gt;It&amp;rsquo;s official &amp;ndash; I&amp;rsquo;ll be writing the next installment in Wrox Press&amp;rsquo; best-selling ASP.NET series. Here&amp;rsquo;s a sneak peek into &lt;em&gt;ASP.NET 4.0 Website Programming: Problem &amp;ndash; Design &amp;ndash; Solution&lt;/em&gt;, and a way you &amp;ndash; yes, &lt;em&gt;you&lt;/em&gt; &amp;ndash; can help make the book better, and even receive written credit in the book&amp;rsquo;s Acknowledgement section.&lt;/p&gt;&lt;hr /&gt;
&lt;p&gt;&lt;img style="border-right-width: 0px; margin: 0px 15px 15px 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="aspmet4-pds" border="0" alt="aspmet4-pds" align="left" width="159" height="200" src="http://leedumond.com/files/media/image/WindowsLiveWriter/WannaHelpMeWriteaBook_13043/aspmet4-pds_3.jpg" /&gt; Marco Bellinaso&amp;rsquo;s &lt;em&gt;&lt;a target="_blank" href="http://www.amazon.com/ASP-NET-2-0-Website-Programming-ebook/dp/B000VI6R8A/"&gt;ASP.NET 2.0 Website Programming: Problem Design Solution&lt;/a&gt;&lt;/em&gt; won&amp;rsquo;t be an easy act to to follow. Published in 2006, it went on to become one of the best-selling ASP.NET books of all time, and has been highly influential for thousands of web developers. For the past 3.5 years, it has been &lt;em&gt;the&lt;/em&gt; book on how to develop a complete website from start-to-finish in ASP.NET.&lt;/p&gt;
&lt;p&gt;It is my sincere hope that the upcoming &lt;em&gt;ASP.NET 4.0 Website Programming: Problem &amp;ndash; Design &amp;ndash; Solution &lt;/em&gt;will be as useful to today&amp;rsquo;s generation of web developers going forward as Marco&amp;rsquo;s book has been. &lt;strong&gt;But to do that, I&amp;rsquo;m going to need your help.&lt;/strong&gt;&lt;/p&gt;
&lt;h3 style="clear: both"&gt;About the Book&lt;/h3&gt;
&lt;p&gt;&lt;i&gt;ASP.NET 4.0 Website Programming Problem &amp;ndash; Design &amp;ndash; Solution&lt;/i&gt; is intended to pick up where Marco&amp;rsquo;s ASP.NET 2.0 book left off. The book will take an example-driven, hands-on approach, while describing in detail the development of a complete ASP.NET 4.0 application from start to finish. In its pages, developers will discover unique and resourceful ways to handle the issues they face daily during the website development process.&lt;/p&gt;
&lt;p&gt;It will be targeted at developers with a moderate degree of experience with ASP.NET 2.0. Those familiar with ASP.NET 3.5 will likely grasp the material a bit quicker, though ASP.NET 3.5 experience will neither be assumed nor required.&lt;/p&gt;
&lt;h3&gt;About the Application&lt;/h3&gt;
&lt;p&gt;The book introduces a brand new sample application called &lt;strong&gt;CycleMania&lt;/strong&gt;. The site will function as a portal / social networking destination for motorcycle enthusiasts of all kinds.&lt;/p&gt;
&lt;p&gt;The new CycleMania application retains the most popular features from the BeerHouse application featured in the 2.0 version of the book such as forums and an online store, as well as localization, personalization, and deployment projects. However, CycleMania introduces many new and exciting features, such as Blogs (this replaces the Articles module from the previous book), an Event Calendar, a Multimedia Gallery, comprehensive Search functionality, an enhanced Administration dashboard, and much more.&lt;/p&gt;
&lt;h3&gt;Where You Fit In&lt;/h3&gt;
&lt;p&gt;Like the original BeerHouse, CycleMania will be a &lt;strong&gt;fully functional, complete, ready-to-use application&lt;/strong&gt;, deployable straight out of the box. Along with the accompanying book, it should also &lt;strong&gt;serve as a teaching aid&lt;/strong&gt; that showcases much of the modern new thinking in ASP.NET Webforms&amp;nbsp;application architecture, design principles, and design patterns; as well as Web standards, semantics, accessibility, advanced CSS techniques, and rich interactive UI. Ultimately, it will &lt;strong&gt;provide a solid framework&lt;/strong&gt; from which readers will be able to develop their own scalable, secure, enterprise-level Web applications.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The success of the book relies on the success of the underlying sample application.&lt;/strong&gt; My hope is to make it as comprehensive, solid, and bug-free as possible, while meeting the overall design goals stated above (in the limited amount of time I&amp;rsquo;ve been given, or course). In order to do that, I need to put as many minds to it as possible. The idea here is to solicit as much feedback from the community as I can. &lt;strong&gt;That&amp;rsquo;s where you come in.&lt;/strong&gt;&lt;/p&gt;
&lt;h3&gt;Download it Now&lt;/h3&gt;
&lt;p&gt;At the risk of totally embarrassing myself, I am going to be posting iterations of &lt;a target="_blank" href="http://cyclemania.codeplex.com/"&gt;Cyclemania on Codeplex&lt;/a&gt; for anyone to freely download. You will need &lt;a target="_blank" href="http://www.microsoft.com/visualstudio/en-us/products/2010/default.mspx"&gt;Visual Studio 2010 Beta 2&lt;/a&gt; or &lt;a target="_blank" href="http://www.microsoft.com/express/future/"&gt;Visual Web Developer 2010 Express Beta 2&lt;/a&gt; to run it (both of these are free).&lt;/p&gt;
&lt;p&gt;The first Alpha version is already available. In it, you&amp;rsquo;ll find the basic UI, along with a couple of themes that you can switch between. The Membership parts are fairly complete as well, so you&amp;rsquo;ll be able to register, confirm registrations by email, log in, reset passwords, change your email address, password, or security question, and so on. In order to do most of those things, you&amp;rsquo;ll need to get email functionality working by configuring the &lt;tt&gt;&amp;lt;mailSettings&amp;gt;&lt;/tt&gt;&amp;nbsp; in the &lt;tt&gt;&amp;lt;system.net&amp;gt;&lt;/tt&gt; section of &lt;tt&gt;Web.config&lt;/tt&gt; to point to your own mail server, or use a &lt;a target="_blank" href="http://leedumond.com/blog/a-better-smtp-development-server/"&gt;SMTP development server like Antix&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s about it for right now, but I&amp;rsquo;ll be uploading new versions every few days. &lt;a target="_blank" href="http://cyclemania.codeplex.com/Release/ProjectReleases.aspx"&gt;Download the latest release&lt;/a&gt;. Play with it. Abuse it. Run it through its paces, and let me know the following:&lt;/p&gt;
&lt;ul&gt;
    &lt;li&gt;&lt;strong&gt;Have you found a bug?&lt;/strong&gt; If so, report it by creating a new item in the &lt;a target="_blank" href="http://cyclemania.codeplex.com/WorkItem/List.aspx"&gt;Issue Tracker&lt;/a&gt;. If you have a suggested fix for it, even better! Contact me via the &lt;a target="_blank" href="http://cyclemania.codeplex.com/team/view"&gt;People&lt;/a&gt; section, or through the contact page of this blog, and send me what you&amp;rsquo;ve come up with. This includes XHTML validation and cross-browser issues as well! &lt;br /&gt;
    &amp;nbsp;&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;Am I on the right track?&lt;/strong&gt; Or am I all wet? Post thoughts, comments, and suggestions in the &lt;a target="_blank" href="http://cyclemania.codeplex.com/Thread/List.aspx"&gt;Discussions&lt;/a&gt; area. Let&amp;rsquo;s get some robust dialogue happening! &lt;br /&gt;
    &amp;nbsp;&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;Got an idea for a theme?&lt;/strong&gt; You&amp;rsquo;ll see I have two somewhat &amp;ldquo;fancy&amp;rdquo; themes created for the site already, but I&amp;rsquo;d love to have more. I especially need at least one simple, high-contrast theme which emphasizes accessibility. The &lt;em&gt;good&lt;/em&gt; news is that the site doesn&amp;rsquo;t use skins, or any of the other features of the ASP.NET Themes system &amp;ndash; just good ol&amp;rsquo; fashioned Cascading Style Sheets. If you&amp;rsquo;re good with CSS, have at it and if it&amp;rsquo;s worthy, I&amp;rsquo;ll include it in the book. The more, the better! &lt;br /&gt;
    &amp;nbsp;&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;Do you know Spanish?&lt;/strong&gt; Part of the plan is to localize the site in both English and Spanish. Unfortunately, I don&amp;rsquo;t know a lick of Spanish. If you want to help with the translation (mostly single words and simple phrases), you&amp;rsquo;ll get mad props.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Of course, it would be of most help to &lt;strong&gt;keep up to date on the very latest and greatest version&lt;/strong&gt;. The best way to do that is to subscribe to the project&amp;rsquo;s RSS feed, so you&amp;rsquo;ll know when new releases are posted. Also, be sure to &lt;a target="_blank" href="http://twitter.com/LeeDumond"&gt;follow me on Twitter&lt;/a&gt;, where&amp;rsquo;ll I&amp;rsquo;ll be tweeting updates and other&amp;nbsp;relevant info&amp;nbsp;as well.&lt;/p&gt;
&lt;p&gt;Again, &lt;strong&gt;the most helpful helpers will receive written acknowledgements in the book&lt;/strong&gt;. Your name in actual print, just like &lt;a target="_blank" href="http://www.imdb.com/title/tt0079367/"&gt;Navin R. Johnson&lt;/a&gt;. ;)&lt;/p&gt;
&lt;p&gt;My sincere hope is, with your help, we&amp;rsquo;ll come up with a really great web application, and a really great book to go with it.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/leedumond?a=-DajUTTX21E:PNdKKx8CB5k:Dh-6e3sOTMo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/leedumond?d=Dh-6e3sOTMo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/leedumond?a=-DajUTTX21E:PNdKKx8CB5k:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/leedumond?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/leedumond?a=-DajUTTX21E:PNdKKx8CB5k:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/leedumond?i=-DajUTTX21E:PNdKKx8CB5k:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/leedumond?a=-DajUTTX21E:PNdKKx8CB5k:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/leedumond?i=-DajUTTX21E:PNdKKx8CB5k:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/leedumond?a=-DajUTTX21E:PNdKKx8CB5k:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/leedumond?i=-DajUTTX21E:PNdKKx8CB5k:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/leedumond?a=-DajUTTX21E:PNdKKx8CB5k:l6gmwiTKsz0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/leedumond?d=l6gmwiTKsz0" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/leedumond?a=-DajUTTX21E:PNdKKx8CB5k:TzevzKxY174"&gt;&lt;img src="http://feeds.feedburner.com/~ff/leedumond?d=TzevzKxY174" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/leedumond?a=-DajUTTX21E:PNdKKx8CB5k:dnMXMwOfBR0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/leedumond?d=dnMXMwOfBR0" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/leedumond/~4/-DajUTTX21E" height="1" width="1"/&gt;</description></item><item><title>GraffitiCMS Going Open Source</title><link>http://leedumond.com/blog/graffiticms-going-open-source/</link><pubDate>Tue, 24 Nov 2009 22:16:00 GMT</pubDate><guid isPermaLink="true">http://leedumond.com/blog/graffiticms-going-open-source/</guid><dc:creator>Lee Dumond</dc:creator><slash:comments>14</slash:comments><category domain="http://leedumond.com/blog/">Blog</category><description>&lt;p&gt;Thanks in part to the many who forwarded, blogged, tweeted, and linked to my May 2009 plea to Telligent to turn GraffitiCMS over to the community, it now appears that Telligent will be doing exactly that.&lt;/p&gt;   &lt;hr /&gt;  &lt;p&gt;&lt;a href="http://leedumond.com/files/media/image/WindowsLiveWriter/GraffitiCMSGoingOpenSource_E4EC/graffiti-oss_2.jpg"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; margin: 0px 0px 15px 15px; display: inline; border-top: 0px; border-right: 0px" title="graffiti-oss" border="0" alt="graffiti-oss" align="right" src="http://leedumond.com/files/media/image/WindowsLiveWriter/GraffitiCMSGoingOpenSource_E4EC/graffiti-oss_thumb.jpg" width="200" height="199" /&gt;&lt;/a&gt; It was a little over six months ago that I &lt;a href="http://leedumond.com/blog/open-source-or-die-the-real-future-of-graffiti/" target="_blank"&gt;posted a modest proposal&lt;/a&gt; to the powers that be at &lt;a href="http://telligent.com/" target="_blank"&gt;Telligent&lt;/a&gt; and to the remaining community of &lt;a href="http://graffiticms.com/" target="_blank"&gt;GraffitiCMS&lt;/a&gt; users: in effect, &lt;strong&gt;that GraffitiCMS should be converted to an open-source project&lt;/strong&gt;, rather than be allowed to languish in ever-increasing obscurity.&lt;/p&gt;  &lt;p&gt;At that time, the company stated that OSS was something they had considered, but had rejected in favor of continuing development in-house. At the same time, a major new upgrade was promised for Q3 2009.&lt;/p&gt;  &lt;p&gt;With summer 2009 having come and gone without the promised upgrades, and with Telligent’s level of support for Graffiti waning from merely dismal to &lt;a href="http://support.graffiticms.com/t/1553.aspx" target="_blank"&gt;completely non-existent&lt;/a&gt;, it looked for all the world that Graffiti had finally flatlined.&lt;/p&gt;  &lt;p&gt;But then, at about 3:45 PM Central time today, &lt;a href="http://twitter.com/scottw/status/6020011113"&gt;this little bundle of joyful news&lt;/a&gt; popped up on my Twitter stream:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://twitter.com/scottw/status/6020011113"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://leedumond.com/files/media/image/WindowsLiveWriter/GraffitiCMSGoingOpenSource_E4EC/image_3.png" width="404" height="279" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;According to Scott, this will be released under a straight-up standard OSS license, and not the “shared source” scenario the company has dabbled with in the past. Congratulations to Rob Howard, Scott Watermasysk, and the rest of the Telligent management team for doing the right thing.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/leedumond?a=7S7UsmBlAHM:3rEpprLEX4s:Dh-6e3sOTMo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/leedumond?d=Dh-6e3sOTMo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/leedumond?a=7S7UsmBlAHM:3rEpprLEX4s:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/leedumond?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/leedumond?a=7S7UsmBlAHM:3rEpprLEX4s:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/leedumond?i=7S7UsmBlAHM:3rEpprLEX4s:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/leedumond?a=7S7UsmBlAHM:3rEpprLEX4s:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/leedumond?i=7S7UsmBlAHM:3rEpprLEX4s:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/leedumond?a=7S7UsmBlAHM:3rEpprLEX4s:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/leedumond?i=7S7UsmBlAHM:3rEpprLEX4s:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/leedumond?a=7S7UsmBlAHM:3rEpprLEX4s:l6gmwiTKsz0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/leedumond?d=l6gmwiTKsz0" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/leedumond?a=7S7UsmBlAHM:3rEpprLEX4s:TzevzKxY174"&gt;&lt;img src="http://feeds.feedburner.com/~ff/leedumond?d=TzevzKxY174" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/leedumond?a=7S7UsmBlAHM:3rEpprLEX4s:dnMXMwOfBR0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/leedumond?d=dnMXMwOfBR0" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/leedumond/~4/7S7UsmBlAHM" height="1" width="1"/&gt;</description></item><item><title>The Wisdom of Crowds: Implementing a Smart Ranking Algorithm</title><link>http://leedumond.com/blog/the-wisdom-of-crowds-implementing-a-smart-ranking-algorithm/</link><pubDate>Mon, 09 Nov 2009 13:57:00 GMT</pubDate><guid isPermaLink="true">http://leedumond.com/blog/the-wisdom-of-crowds-implementing-a-smart-ranking-algorithm/</guid><dc:creator>Lee Dumond</dc:creator><slash:comments>11</slash:comments><category domain="http://leedumond.com/blog/">Blog</category><description>&lt;p&gt;From blog posts to books, sorting a list of items by user rating is a very common task. In this blog post, we discover how the writings of an obscure 18th-century British minister lets us implement a &amp;ldquo;smart&amp;rdquo; ranking algorithm that takes into account the &amp;ldquo;wisdom of the crowd&amp;rdquo;, and presents a fairer and more accurate representation of results.&lt;/p&gt;&lt;hr /&gt;
&lt;h3&gt;The Rating Problem&lt;/h3&gt;
&lt;p&gt;Let&amp;rsquo;s pretend for the moment that you&amp;rsquo;ve hankerin&amp;rsquo; to pick up a few fresh tips on ASP.NET 3.5. You visit a bookstore and type &amp;ldquo;ASP.NET 3.5&amp;rdquo; into the search box. You obtain the results shown in Figure 1.&lt;sup&gt;1&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="screenshot 1" width="600" height="1021" src="http://leedumond.com/files/media/image/WindowsLiveWriter/TheIntelligenceofCrowdsImplementingaSmar_D050/screenshot1.png" /&gt;&lt;/p&gt;
&lt;p class="figureCaption"&gt;Figure 1&lt;/p&gt;
&lt;p&gt;This site, like many others, allows users to rate products on a scale of 1 to 5. The average customer rating is displayed when browsing these products. (I&amp;rsquo;ve included both image-based and numerical displays for the purposes of this discussion). By sorting the results in order of customer rating as shown, you hope to discover which books are most popular among your peers.&lt;sup&gt;2&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;Examining Figure 1, it would appear that &lt;em&gt;ASP.NET 3.5 Website Programming: Problem - Design &amp;ndash; Solution&lt;/em&gt; is the most popular among these books, with a perfect 5-star rating; and that &lt;em&gt;Pro ASP.NET 3.5 in C# 2008&lt;/em&gt; is the least popular among the rated books, with but a single star to call its own. But, as I&amp;rsquo;m sure you already suspect, &lt;strong&gt;the average rating alone doesn&amp;rsquo;t tell us the whole story.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Of course, e-commerce sites understand this too. That&amp;rsquo;s why you&amp;rsquo;ll almost always find, in addition to the average customer rating, the number of customers who contributed to that rating, as shown in Figure 2.&lt;/p&gt;
&lt;p&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" width="604" height="1017" src="http://leedumond.com/files/media/image/WindowsLiveWriter/TheIntelligenceofCrowdsImplementingaSmar_D050/screenshot2.png" /&gt;&lt;/p&gt;
&lt;p class="figureCaption"&gt;Figure 2&lt;/p&gt;
&lt;h3&gt;Leveraging the Wisdom of the Crowd&lt;/h3&gt;
&lt;p&gt;Knowing the number of times an item has been rated makes the results much more meaningful to us &amp;ndash; but have you stopped to consider exactly &lt;em&gt;why&lt;/em&gt; this is so? &lt;strong&gt;Why do we lend more weight to a rating when an item has been rated by more individuals?&lt;/strong&gt; This may appear to the casual observer as the manifestation of a &amp;ldquo;bandwagon effect&amp;rdquo; of some kind, but that&amp;rsquo;s not really the case. As it turns out, &lt;strong&gt;this is the result of an intuitive mental calculation that mirrors an actual real-word phenomenon&lt;/strong&gt; - a true example of crowd intelligence in action. What&amp;rsquo;s more, as we shall soon see,&lt;strong&gt; this phenomenon can be modeled in software.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;We see from Figure 2 that &lt;em&gt;ASP.NET 3.5 Website Programming: Problem - Design &amp;ndash; Solution&lt;/em&gt; has the highest average customer rating, and therefore resides at the top of the list. However, &lt;em&gt;since it has only been rated once, we are less inclined to trust this result.&lt;/em&gt; Intuitively, we understand that with a small number of ratings, the probability of an anomalous rating (an &amp;ldquo;outlier&amp;rdquo; in statistical terms) having a significant impact on the average is greatly increased. In other words, a small number of ratings increases the chance that the overall average rating may be &amp;ldquo;inaccurate&amp;rdquo; &amp;ndash; not &lt;em&gt;arithmetically&lt;/em&gt; inaccurate of course, but inaccurate in the sense that the data doesn&amp;rsquo;t reflect a true consensus of typical, unbiased readers.&lt;/p&gt;
&lt;p&gt;Now take a look at some of the other results. We see that &lt;em&gt;ASP.NET 3.5 Unleashed&lt;/em&gt; appears lower on the list by virtue of its lower average rating of 4.8. Nevertheless, &lt;em&gt;we instinctively lend much more weight to its rating data, because far more customers have rated it.&lt;/em&gt; If we&amp;rsquo;re laying down our hard-earned money, it would appear that &lt;em&gt;ASP.NET 3.5 Unleashed&lt;/em&gt; is the surer bet (all other things being equal, of course). That 4.8 rating may not be the highest on the list, but a rational observer would invariably conclude that this is a &lt;em&gt;more reliable figure&lt;/em&gt;, based on the relatively large proportion of the &amp;ldquo;crowd&amp;rdquo; which contributed to it.&lt;/p&gt;
&lt;p&gt;Including the number of ratings lets us evaluate the average rating data of each individual item not only on its own, but in relation to all the other items. &lt;strong&gt;The more times an item has been rated, the &lt;em&gt;more believable&lt;/em&gt; the data, and thus the &lt;em&gt;more relative weight&lt;/em&gt; we place upon its average rating.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Wouldn&amp;rsquo;t it be great if we could somehow leverage the intelligence of the crowd, and reflect it in the results? What if we could &lt;strong&gt;rank and sort the results based not solely on the raw average rating, but on the &lt;em&gt;believability&lt;/em&gt; of the average rating? &lt;/strong&gt;Such a ranking algorithm would more accurately reflect how we perceive each item&amp;rsquo;s ratings in relation to each other. Using this algorithm, an item which received a single 5-star rating wouldn&amp;rsquo;t immediately jump to the top of the list; likewise, an item that received a single poor rating wouldn&amp;rsquo;t immediately be forced to the bottom. The &amp;ldquo;good stuff&amp;rdquo; (items which receive mostly high ratings from a relatively large proportion of the crowd) would appear near the top, while the &amp;ldquo;not-so-good&amp;rdquo; (again, as determined by the crowd) would be pushed lower.&lt;/p&gt;
&lt;h3&gt;Bayes&amp;rsquo; Theorem&lt;/h3&gt;
&lt;p&gt;&lt;img style="border-bottom: 0px; border-left: 0px; margin: 0px 0px 15px 15px; display: inline; border-top: 0px; border-right: 0px" title="Thomas_Bayes" border="0" alt="Thomas_Bayes" align="right" width="154" height="164" src="http://leedumond.com/files/media/image/WindowsLiveWriter/TheIntelligenceofCrowdsImplementingaSmar_D050/Thomas_Bayes_thumb.gif" /&gt; As it turns out, we can develop such an algorithm with the help of an 18th-century British Presbyterian minister named Thomas Bayes.&lt;/p&gt;
&lt;p&gt;Like many learned men of his day, Reverend Bayes harbored a keen interest in mathematics. In an essay discovered and published in 1764 &amp;ndash; three years after his death &amp;ndash; he presents a special case of what eventually came to be known as &lt;a target="_blank" href="http://en.wikipedia.org/wiki/Bayes%27_theorem"&gt;Bayes&amp;rsquo; theorem&lt;/a&gt;. Today, Bayes theorem is widely accepted among most statisticians. Applications of it can be found throughout many fields in science and engineering, from the evaluation of drug test results, to predicting the outcome of elections, to the development of spam-filtering software.&lt;/p&gt;
&lt;p&gt;Bayes&amp;rsquo; theorem states in part that probabilities are &lt;em&gt;rationally coherent degrees of belief&lt;/em&gt;, or a &lt;em&gt;degree of belief in a proposition given a body of well-specified information&lt;/em&gt;.&lt;sup&gt;3&lt;/sup&gt; Therefore, &lt;strong&gt;Bayes' theorem can be thought of as a representation of how an ideally rational individual would interpret the believability of an outcome, based on a series of prior outcomes.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Bayes&amp;rsquo; theorem can be stated mathematically in terms of how the probability of an outcome A given B, is related to the converse probability of outcome B given A.&lt;/p&gt;
&lt;h3&gt;Applying Bayes&amp;rsquo; Theorem to the Rating Problem&lt;/h3&gt;
&lt;p&gt;A &lt;a target="_blank" href="http://en.wikipedia.org/wiki/Bayesian_average"&gt;Bayesian average&lt;/a&gt; is a method of calculating the mean of a set of data that is consistent with Bayes&amp;rsquo; theorem.&lt;/p&gt;
&lt;p&gt;&lt;img style="border-right-width: 0px; margin: 10px 0px 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="bayesian average" border="0" alt="bayesian average" width="157" height="43" src="http://leedumond.com/files/media/image/WindowsLiveWriter/TheIntelligenceofCrowdsImplementingaSmar_D050/bayesian%20average_thumb.png" /&gt;&lt;/p&gt;
&lt;p&gt;Where:&lt;/p&gt;
&lt;ul&gt;
    &lt;li&gt;&lt;em&gt;C&lt;/em&gt; is a &amp;ldquo;weighting&amp;rdquo; constant, proportional to the typical size of the data set&lt;/li&gt;
    &lt;li&gt;&lt;em&gt;m&lt;/em&gt; is the arithmetic mean taken over all known data&lt;/li&gt;
    &lt;li&gt;&lt;em&gt;n&lt;/em&gt; is the size of the current data set&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For our particular purposes, we can calculate the Bayesian average of each of our books by applying the formula above. This lets us derive a rating that is based on the &amp;ldquo;believability&amp;rdquo; of the raw data.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;BA = ((AvgNumOfRatingsForAll * AvgRatingForAll) + TotalRating) / (RateCount + AvgNumOfRatingsForAll)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Where:&lt;/p&gt;
&lt;ul&gt;
    &lt;li&gt;&lt;strong&gt;RateCount&lt;/strong&gt; is the number of ratings given to &lt;em&gt;this&lt;/em&gt; book&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;TotalRating&lt;/strong&gt; is the sum of all ratings given to &lt;em&gt;this&lt;/em&gt; book&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;AvgNumOfRatingsForAll&lt;/strong&gt; is the average number of ratings for &lt;em&gt;all&lt;/em&gt; books shown (where RateCount &amp;gt; 0)&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;AvgRatingForAll&lt;/strong&gt; is the average unweighted rating for &lt;em&gt;all&lt;/em&gt; books shown (where RateCount &amp;gt; 0)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In Figure 3, the results are now sorted by their Bayesian average. The star displays also reflect the calculated Bayesian average.&lt;/p&gt;
&lt;p&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" width="604" height="1017" src="http://leedumond.com/files/media/image/WindowsLiveWriter/TheIntelligenceofCrowdsImplementingaSmar_D050/screenshot3.png" /&gt;&lt;/p&gt;
&lt;p class="figureCaption"&gt;Figure 3&lt;/p&gt;
&lt;p&gt;If you compare these results to those shown in Figure 2, several conclusions can be drawn:&lt;/p&gt;
&lt;ol&gt;
    &lt;li&gt;The Bayesian average is a truer reflection of how the books are rated &lt;em&gt;in relation to each other&lt;/em&gt;.&lt;/li&gt;
    &lt;li&gt;The Bayesian rank for books with a relatively &lt;em&gt;small&lt;/em&gt; number of ratings tends to gravitate toward the average rating of &lt;em&gt;all&lt;/em&gt; books.&lt;/li&gt;
    &lt;li&gt;The more ratings a book has received, the more &amp;ldquo;believable&amp;rdquo; the ratings are. Therefore, when a book has received a relatively &lt;em&gt;large &lt;/em&gt;number of ratings, its Bayesian rank gravitates toward its unweighted average rating.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;Keeping it Fair&lt;/h3&gt;
&lt;p&gt;As pointed out, the Bayesian average formula uses a constant &lt;em&gt;C&lt;/em&gt; that determines how much &amp;ldquo;correction&amp;rdquo; is applied to the raw, unweighted average. By examining the formula, you&amp;rsquo;ll note that &lt;em&gt;C&lt;/em&gt; is a &amp;ldquo;dampening&amp;rdquo; value that determines the &amp;ldquo;sensitivity&amp;rdquo; of the calculation.&lt;/p&gt;
&lt;p&gt;This means that &lt;em&gt;the higher the value of C, the more customer ratings it will take to influence the Bayesian average.&lt;/em&gt; In other words, &lt;em&gt;C&lt;/em&gt; can be thought of as the number of ratings it will take to have a significant impact on the relative&amp;nbsp;ranking of an item. Higher values of &lt;em&gt;C&lt;/em&gt; make the results &lt;em&gt;less sensitive to the determination of the crowd&lt;/em&gt;, while lower values of &lt;em&gt;C&lt;/em&gt; do the opposite. This is something you might want to take into account when determining what value to use for &lt;em&gt;C&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;In practice, this value should be proportional to the typical size of the data set. In our example, we used the average number of ratings given to all rated items in the search result. This works well in most cases in which new items are added on a regular and&amp;nbsp;ongoing basis, such as an inventory of retail items, blog posts, songs, videos, downloadable applications,&amp;nbsp;and the like.&lt;/p&gt;
&lt;p&gt;However, consider the case in which new items are added relatively infrequently. When the &lt;em&gt;existing&lt;/em&gt; items already have a &lt;em&gt;high number of ratings&lt;/em&gt;, a &lt;em&gt;new&lt;/em&gt; item will have to receive many ratings in order to move up or down in the list. In your particular application, if you want to give new items a chance to move more quickly, you can reduce the value of C by a given factor, like so:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;BA = ((AvgNumOfRatingsForAll * 0.75 * AvgRatingForAll) + TotalRating) / (RateCount + (AvgNumOfRatingsForAll * 0.75))&lt;/strong&gt;&lt;/p&gt;
&lt;h3&gt;Summary&lt;/h3&gt;
&lt;p&gt;When sorting a list of items by the &amp;ldquo;raw&amp;rdquo; average of the ratings it has received, &lt;strong&gt;we often get a distorted view.&lt;/strong&gt; Results are easily skewed by a small number of ratings (or even a single rating) given to any particular item.&lt;/p&gt;
&lt;p&gt;In order to provide a more accurate picture, &lt;strong&gt;we need a smarter rating system &amp;ndash; one that can accurately quantify the &amp;ldquo;wisdom of the crowd.&amp;rdquo;&lt;/strong&gt; This is something we naturally do when the size of the &amp;ldquo;crowd&amp;rdquo; is known to us. When an item has received many ratings, that data should be considered more &amp;ldquo;reliable,&amp;rdquo; and therefore its ranking in the list should more closely reflect the crowd&amp;rsquo;s decision. When an item has received very few ratings, its ranking should approximate the average rating for all the items shown.&lt;/p&gt;
&lt;p&gt;We can use Bayes&amp;rsquo; theorem to derive a smart rating algorithm to accomplish this task. Furthermore, the weighting applied by the Bayesian average formula can be adjusted to suit different purposes, thus making new items more sensitive to the influence of the crowd.&lt;/p&gt;
&lt;h3&gt;Download the Code&lt;/h3&gt;
&lt;p&gt;If you&amp;rsquo;re interested in playing around with this on your own, you can &lt;a target="_blank" href="http://leedumond.com/files/downloads/BayesianRankingSample.zip"&gt;download the solution/database&lt;/a&gt; I used to create the screenshots in this post. You can mess around with the database values and/or the weighting constant in order to see how different scenarios affect the results.&lt;/p&gt;
&lt;div class="footnotes"&gt;
&lt;p&gt;1. Book cover images courtesy of &lt;a target="_blank" href="http://amazon.com/"&gt;Amazon.com&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;2. All screenshots are for illustrative purposes only, and should not be construed as an actual reflection of the popularity or quality of these titles in &amp;ldquo;real life,&amp;rdquo; okay?&lt;/p&gt;
&lt;p&gt;3. From &lt;a title="http://en.wikipedia.org/wiki/Bayes%27_theorem" href="http://en.wikipedia.org/wiki/Bayes%27_theorem"&gt;http://en.wikipedia.org/wiki/Bayes%27_theorem&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/leedumond?a=D-thbv2nlPI:ueHRGlLpeV0:Dh-6e3sOTMo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/leedumond?d=Dh-6e3sOTMo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/leedumond?a=D-thbv2nlPI:ueHRGlLpeV0:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/leedumond?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/leedumond?a=D-thbv2nlPI:ueHRGlLpeV0:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/leedumond?i=D-thbv2nlPI:ueHRGlLpeV0:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/leedumond?a=D-thbv2nlPI:ueHRGlLpeV0:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/leedumond?i=D-thbv2nlPI:ueHRGlLpeV0:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/leedumond?a=D-thbv2nlPI:ueHRGlLpeV0:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/leedumond?i=D-thbv2nlPI:ueHRGlLpeV0:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/leedumond?a=D-thbv2nlPI:ueHRGlLpeV0:l6gmwiTKsz0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/leedumond?d=l6gmwiTKsz0" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/leedumond?a=D-thbv2nlPI:ueHRGlLpeV0:TzevzKxY174"&gt;&lt;img src="http://feeds.feedburner.com/~ff/leedumond?d=TzevzKxY174" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/leedumond?a=D-thbv2nlPI:ueHRGlLpeV0:dnMXMwOfBR0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/leedumond?d=dnMXMwOfBR0" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/leedumond/~4/D-thbv2nlPI" height="1" width="1"/&gt;</description></item><item><title>Dynamic Help Removed From Visual Studio 2010</title><link>http://leedumond.com/blog/dynamic-help-removed-from-visual-studio-2010/</link><pubDate>Mon, 02 Nov 2009 03:52:00 GMT</pubDate><guid isPermaLink="true">http://leedumond.com/blog/dynamic-help-removed-from-visual-studio-2010/</guid><dc:creator>Lee Dumond</dc:creator><slash:comments>38</slash:comments><category domain="http://leedumond.com/blog/">Blog</category><description>&lt;p&gt;Been going blind looking for the Dynamic Help window in the new VS 2010 Beta 2? Me too&amp;hellip; until I learned this bit of bad news from a inside source.&lt;/p&gt;&lt;hr /&gt;
&lt;p&gt;Like many of you, I&amp;rsquo;ve been messing around with the &lt;a target="_blank" href="http://www.microsoft.com/visualstudio/en-us/products/2010/default.mspx"&gt;Visual Studio 2010 &lt;/a&gt;Beta 2. VS 2010 contains a number of long-awaited IDE improvements, like multiple monitor support, new editing and design tools, and a cleaner, more responsive WPF-based interface.&lt;/p&gt;
&lt;p&gt;So far, I&amp;rsquo;m loving all of it. Well, &lt;em&gt;almost&lt;/em&gt; all of it.&lt;/p&gt;
&lt;p&gt;I happen to be a huge fan of Visual Studio&amp;rsquo;s &lt;a target="_blank" href="http://msdn.microsoft.com/en-us/library/f88ctah3.aspx"&gt;Dynamic Help&lt;/a&gt; feature. This is a window containing links to related Help topics based on where you are in the IDE.&lt;/p&gt;
&lt;p&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="dynamic_help" border="0" alt="dynamic_help" width="604" height="389" src="http://leedumond.com/files/media/image/WindowsLiveWriter/DynamicHelpRemovedFromVisualStudio2010_1259D/dynamic_help_3.gif" /&gt;&lt;/p&gt;
&lt;p&gt;In fact, I recently wrote a blog post explaining &lt;a target="_blank" href="http://leedumond.com/blog/integrating-exception-handling-into-the-development-cycle/"&gt;how to integrate exception handling into the development cycle&lt;/a&gt;. The process I presented in that post&amp;nbsp;makes good use of this handy feature.&lt;/p&gt;
&lt;p&gt;In previous versions of Visual Studio, I&amp;rsquo;ve always placed the Dynamic Help window to the right side in Auto-Hide mode, ready at a moment&amp;rsquo;s notice. However, when I went to set up VS 2010 in the same way, I noticed that the Dynamic Help window appeared to have been left off the Help menu.&lt;/p&gt;
&lt;p&gt;&amp;quot;Okay, no big deal,&amp;quot; I thought. It is &lt;em&gt;beta &lt;/em&gt;software after all. You can't really&amp;nbsp;complain if a beta appears a little half-baked, right?&lt;/p&gt;
&lt;p&gt;Nevertheless, this was still disappointing to me. I was tempted to report this as a bug, but just didn&amp;rsquo;t feel like dealing with the massive vortex of suck that is &lt;a target="_blank" href="http://connect.microsoft.com/"&gt;Microsoft Connect&lt;/a&gt;. So instead, I did what most other malcontented developers do &amp;ndash; I &lt;a target="_blank" href="http://twitter.com/LeeDumond/status/5349332955"&gt;complained on Twitter&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" width="304" height="246" src="http://leedumond.com/files/media/image/WindowsLiveWriter/DynamicHelpRemovedFromVisualStudio2010_1259D/image_3.png" /&gt;&lt;/p&gt;
&lt;p&gt;A few hours later -&amp;nbsp;much to my chagrin - I&amp;nbsp;received the &lt;a target="_blank" href="http://twitter.com/ccsjeba/status/5352109398"&gt;following reply&lt;/a&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" width="304" height="246" src="http://leedumond.com/files/media/image/WindowsLiveWriter/DynamicHelpRemovedFromVisualStudio2010_1259D/image_6.png" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Whaaaaaa?&lt;/em&gt; This made absolutely no sense to me. I figured it couldn&amp;rsquo;t be true&amp;hellip; until I learned that &lt;a target="_blank" href="http://twitter.com/ccsjeba"&gt;Charles Christian&lt;/a&gt; is actually a Project Manager on the Visual Studio Help Experience Team. So, there you go. I guess if anyone would be in a position to know, he&amp;rsquo;d be the guy.&lt;/p&gt;
&lt;p&gt;Sure enough, a quick visit to the MSDN seemed to confirm it. Check it out below &amp;ndash; no link to 4.0 up there near the top.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://leedumond.com/files/media/image/WindowsLiveWriter/DynamicHelpRemovedFromVisualStudio2010_1259D/msdn_2.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="msdn" border="0" alt="msdn" width="604" height="443" src="http://leedumond.com/files/media/image/WindowsLiveWriter/DynamicHelpRemovedFromVisualStudio2010_1259D/msdn_thumb.png" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;It looks like Visual Studio 2010 will definitely take some getting used to &amp;ndash; because of all the new features added, and for at least one that was taken away.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve asked Charles to provide an explanation as to why Dynamic Help was removed. As soon as I get a reply, I&amp;rsquo;ll update this post. Stay tuned.&lt;/p&gt;
&lt;p style="color: #ff0000"&gt;&lt;strong&gt;UPDATE&lt;/strong&gt;: Charles' explanation appears below in the comments.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/leedumond?a=_aQR6aUsxjY:NsiQS_slMvo:Dh-6e3sOTMo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/leedumond?d=Dh-6e3sOTMo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/leedumond?a=_aQR6aUsxjY:NsiQS_slMvo:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/leedumond?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/leedumond?a=_aQR6aUsxjY:NsiQS_slMvo:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/leedumond?i=_aQR6aUsxjY:NsiQS_slMvo:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/leedumond?a=_aQR6aUsxjY:NsiQS_slMvo:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/leedumond?i=_aQR6aUsxjY:NsiQS_slMvo:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/leedumond?a=_aQR6aUsxjY:NsiQS_slMvo:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/leedumond?i=_aQR6aUsxjY:NsiQS_slMvo:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/leedumond?a=_aQR6aUsxjY:NsiQS_slMvo:l6gmwiTKsz0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/leedumond?d=l6gmwiTKsz0" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/leedumond?a=_aQR6aUsxjY:NsiQS_slMvo:TzevzKxY174"&gt;&lt;img src="http://feeds.feedburner.com/~ff/leedumond?d=TzevzKxY174" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/leedumond?a=_aQR6aUsxjY:NsiQS_slMvo:dnMXMwOfBR0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/leedumond?d=dnMXMwOfBR0" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/leedumond/~4/_aQR6aUsxjY" height="1" width="1"/&gt;</description></item><item><title>Dr. Explain Gives Your Software a Brain</title><link>http://leedumond.com/blog/dr-explain-gives-your-software-a-brain/</link><pubDate>Wed, 28 Oct 2009 13:11:00 GMT</pubDate><guid isPermaLink="true">http://leedumond.com/blog/dr-explain-gives-your-software-a-brain/</guid><dc:creator>Lee Dumond</dc:creator><slash:comments>7</slash:comments><category domain="http://leedumond.com/blog/">Blog</category><description>&lt;p&gt;Thorough, detailed, and up-to-date documentation is one of the keys to success for any software application. Dr. Explain makes it easy (almost &lt;em&gt;too&lt;/em&gt; easy) to create and maintain CHM help files, web-based HTML documentation, and even printable manuals.&lt;/p&gt;&lt;hr /&gt;
&lt;p&gt;Ask any developer to name his or her least favorite development-related tasks, and there&amp;rsquo;s a good chance that &amp;ldquo;creating documentation&amp;rdquo; will appear very close to the the top of that list.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href="http://leedumond.com/files/media/image/WindowsLiveWriter/Dr.ExplainGivesYourSoftwareaBrain_D0D9/wizardofoz1_2.jpg"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; margin: 0px 0px 15px 15px; display: inline; border-top: 0px; border-right: 0px" title="wizardofoz1" border="0" alt="wizardofoz1" align="right" width="244" height="167" src="http://leedumond.com/files/media/image/WindowsLiveWriter/Dr.ExplainGivesYourSoftwareaBrain_D0D9/wizardofoz1_thumb.jpg" /&gt;&lt;/a&gt;While writing docs may not be nearly as sexy as architecting or slinging code, it&amp;rsquo;s a critically important component to any successful software project.&lt;/strong&gt; Skimpy, incomplete, or disorganized documentation is not only a major impediment to new user adoption, it reflects poorly on the &lt;em&gt;rest&lt;/em&gt; of the application, and even on your company as a whole. Despite what you may have heard, people &lt;em&gt;do&lt;/em&gt; &lt;a target="_blank" href="http://en.wikipedia.org/wiki/RTFM"&gt;RTFM&lt;/a&gt;. Your software&amp;rsquo;s help file is invariably the first place users turns for assistance; if they can&amp;rsquo;t make head or tails of it, your chances of converting that trial download to a cash sale just went down considerably.&lt;/p&gt;
&lt;p&gt;On the other hand, well-documented software is easier to sell, easier for your customers to train new users on, and &lt;em&gt;much&lt;/em&gt; easier for you to support. Thorough, detailed, up-to-date help is one of the keys to helping users be productive, and productive users are happy users.&lt;/p&gt;
&lt;p&gt;Of course, documenting software is time-consuming and tedious, right? I used to think so too. That was until I discovered a solution that makes the entire process easier and quicker than I ever thought possible.&lt;/p&gt;
&lt;h3&gt;Help That Practically Writes Itself&lt;/h3&gt;
&lt;p&gt;&lt;a target="_blank" href="http://www.drexplain.com/"&gt;Dr. Explain&lt;/a&gt; from &lt;a target="_blank" href="http://www.indigobyte.com/"&gt;Indigo Byte Systems&lt;/a&gt; is a software help authoring tool. It provides a powerful, intuitive user interface for composing a tree-navigable help system for Windows/WPF, Java, and Flash applications, as well as Web sites. From a single source file, it can export to &lt;a target="_blank" href="http://en.wikipedia.org/wiki/Microsoft_Compiled_HTML_Help"&gt;CHM&lt;/a&gt;, HTML, and rich text format (RTF). This helps you maintain all of your docs in one place while keeping everything consistent and timely &amp;ndash; whether you electronically distribute help with your application, host it online, or deliver printed copies in the box.&lt;/p&gt;
&lt;p&gt;While Dr. Explain has all the features you&amp;rsquo;d expect from a comprehensive help authoring system, it does &lt;em&gt;one special thing amazingly well&lt;/em&gt;, which in my mind places it head and shoulders above other offerings in this category. &lt;strong&gt;Dr. Explain takes the most difficult, time-consuming, and tedious part of the documentation process &amp;ndash; the annotation of user interfaces &amp;ndash; and totally automates it.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Yes, you read that right. Let the built-in object capture tool take a screenshot of &lt;em&gt;any&lt;/em&gt; part of your application &amp;ndash; from a simple dialog, to a full window, to a web form&amp;hellip;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://leedumond.com/files/media/image/WindowsLiveWriter/Dr.ExplainGivesYourSoftwareaBrain_D0D9/image_2.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" width="575" height="484" src="http://leedumond.com/files/media/image/WindowsLiveWriter/Dr.ExplainGivesYourSoftwareaBrain_D0D9/image_thumb.png" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;hellip;and from there it &lt;em&gt;analyzes&lt;/em&gt; the structure, &lt;em&gt;finds&lt;/em&gt; all the significant GUI elements, and &lt;em&gt;automatically creates&lt;/em&gt; numbered callouts for all of them!&lt;/p&gt;
&lt;p&gt;&lt;a href="http://leedumond.com/files/media/image/WindowsLiveWriter/Dr.ExplainGivesYourSoftwareaBrain_D0D9/image4.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" width="575" height="484" src="http://leedumond.com/files/media/image/WindowsLiveWriter/Dr.ExplainGivesYourSoftwareaBrain_D0D9/image4_thumb.png" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;It optionally allows you to associate a definable portion of the screenshot with each annotation, and displays it (along with the descriptive text you provide) at the bottom. This provides helpful visual cues to your readers, as you can see from the CHM output.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://leedumond.com/files/media/image/WindowsLiveWriter/Dr.ExplainGivesYourSoftwareaBrain_D0D9/image8.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" width="575" height="484" src="http://leedumond.com/files/media/image/WindowsLiveWriter/Dr.ExplainGivesYourSoftwareaBrain_D0D9/image8_thumb.png" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;A screenshot of the HTML output shows how a consistent look and feel is maintained between the CHM and HTML output.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://leedumond.com/files/media/image/WindowsLiveWriter/Dr.ExplainGivesYourSoftwareaBrain_D0D9/image12.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" width="575" height="484" src="http://leedumond.com/files/media/image/WindowsLiveWriter/Dr.ExplainGivesYourSoftwareaBrain_D0D9/image12_thumb.png" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Providing guidance for all the different screens in your application lies at the heart of effective documentation.&lt;/strong&gt; Even with the drawing tools provided by many authoring systems, this process can take hours, even days. By scaffolding screenshot-based help topics in mere &lt;em&gt;minutes,&lt;/em&gt; Dr. Explain dramatically cuts the time required to accomplish this task. Capture the screenshot, add the necessary descriptive text to the automatically generated annotations, and you&amp;rsquo;re practically done.&lt;/p&gt;
&lt;h3&gt;But Wait, There&amp;rsquo;s More&amp;hellip;&lt;/h3&gt;
&lt;p&gt;Despite its amazing screenshot annotation abilities, Dr. Explain is no one-trick pony. It facilitates many other help-building functions as well, including the integration of context-sensitive (F1) help directly into any application; navigation tree management; creation of keyword indexes; full-featured RTF text editing with Unicode support; templating, theme, and CSS support; and lots more. All of this is included in the $125 standard license.&lt;/p&gt;
&lt;p&gt;For an additional $40, an &amp;ldquo;advanced license&amp;rdquo; is available which unlocks some very spiffy features, the slickest of which is the ability to have Dr. Explain automatically crawl the entire submenu structure of a Win32 application on one go:&lt;/p&gt;
&lt;p&gt;&lt;img style="border-bottom: 0px; border-left: 0px; margin: 0px 15px 0px 0px; display: inline; border-top: 0px; border-right: 0px" title="menu-submenus-capturing-annotation_image_0" border="0" alt="menu-submenus-capturing-annotation_image_0" width="244" height="196" src="http://leedumond.com/files/media/image/WindowsLiveWriter/Dr.ExplainGivesYourSoftwareaBrain_D0D9/menusubmenuscapturingannotation_imag%5B2%5D.png" /&gt; &lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="menu-submenus-capturing-annotation_image_1" border="0" alt="menu-submenus-capturing-annotation_image_1" width="401" height="196" src="http://leedumond.com/files/media/image/WindowsLiveWriter/Dr.ExplainGivesYourSoftwareaBrain_D0D9/menusubmenuscapturingannotation_imag.png" /&gt;&lt;/p&gt;
&lt;p&gt;Other advanced features include the ability to invoke the Dr. Explain engine from the command line; the creation of pop-up tooltips; and a tool to generate Google sitemaps from HTML help pages.&lt;/p&gt;
&lt;p&gt;To learn more; feel free to avail yourself of any of the following handy resources:&lt;/p&gt;
&lt;ul&gt;
    &lt;li&gt;Dr. Explain &lt;a target="_blank" href="http://www.drexplain.com/software-documentation-tool/"&gt;features overview&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;A &lt;a target="_blank" href="http://www.youtube.com/watch?v=F-PUmFdeJlI"&gt;six-minute YouTube video&lt;/a&gt; demonstrating Dr. Explain in action&lt;/li&gt;
    &lt;li&gt;The Dr. Explain &lt;a target="_blank" href="http://www.drexplain.com/forum/"&gt;user forum&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a target="_blank" href="http://www.drexplain.com/order/"&gt;Pricing and licensing options&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Summary&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Whether your goal is to sell more software, reduce the burden on your help desk staff, or simply to pave an easier path to adoption for your open-source project, producing and providing excellent help documentation is critically important.&lt;/strong&gt; Dr. Explain is an incredibly useful tool for this purpose, especially if your help files rely heavily on UI illustrations. Its automatic annotation capabilities perform nearly flawlessly, and cut the time required to generate screenshot-based help topic by several orders of magnitude.&lt;/p&gt;
&lt;p&gt;A &lt;a target="_blank" href="http://www.drexplain.com/download/"&gt;trial version&lt;/a&gt; is available that you may use as long as you wish. Until you register, you&amp;rsquo;ll be limited to five help topics per project, and all screen captures will be watermarked. While obviously not suitable for production work, it&amp;rsquo;s more that enough to let you try out the many features for yourself.&lt;/p&gt;
&lt;p&gt;At $125 for a single-user license, Dr. Explain could easily pay for itself several times over in the scope of a single project, merely based on the amount of time saved. For $165, the Advanced License unlocks additional features you&amp;rsquo;re sure to find useful as well.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/leedumond?a=jDyjwqV4caI:YKIhxuYHIqo:Dh-6e3sOTMo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/leedumond?d=Dh-6e3sOTMo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/leedumond?a=jDyjwqV4caI:YKIhxuYHIqo:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/leedumond?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/leedumond?a=jDyjwqV4caI:YKIhxuYHIqo:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/leedumond?i=jDyjwqV4caI:YKIhxuYHIqo:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/leedumond?a=jDyjwqV4caI:YKIhxuYHIqo:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/leedumond?i=jDyjwqV4caI:YKIhxuYHIqo:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/leedumond?a=jDyjwqV4caI:YKIhxuYHIqo:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/leedumond?i=jDyjwqV4caI:YKIhxuYHIqo:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/leedumond?a=jDyjwqV4caI:YKIhxuYHIqo:l6gmwiTKsz0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/leedumond?d=l6gmwiTKsz0" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/leedumond?a=jDyjwqV4caI:YKIhxuYHIqo:TzevzKxY174"&gt;&lt;img src="http://feeds.feedburner.com/~ff/leedumond?d=TzevzKxY174" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/leedumond?a=jDyjwqV4caI:YKIhxuYHIqo:dnMXMwOfBR0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/leedumond?d=dnMXMwOfBR0" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/leedumond/~4/jDyjwqV4caI" height="1" width="1"/&gt;</description></item><item><title>Wrox Blogs Are Now Live!</title><link>http://leedumond.com/blog/wrox-blogs-are-now-live/</link><pubDate>Thu, 22 Oct 2009 14:25:00 GMT</pubDate><guid isPermaLink="true">http://leedumond.com/blog/wrox-blogs-are-now-live/</guid><dc:creator>Lee Dumond</dc:creator><slash:comments>4</slash:comments><category domain="http://leedumond.com/blog/">Blog</category><description>&lt;p&gt;Wrox Press has added a blog site, featuring posts from authors and key staff, to its lineup of great programmer resources.&lt;/p&gt;&lt;hr /&gt;
&lt;p&gt;Just a quick post to let you all know that as of this week, the long-awaited &lt;a target="_blank" href="http://p2p.wrox.com/content/blogs"&gt;Wrox Blogs&lt;/a&gt; are now live at the &lt;a target="_blank" href="http://www.wrox.com/WileyCDA/"&gt;Wrox Press&lt;/a&gt; website. The next time you visit, be sure to note the prominent link on the home page menu.&lt;/p&gt;
&lt;p&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="wrox-homepage" border="0" alt="wrox-homepage" width="604" height="501" src="http://leedumond.com/files/media/image/WindowsLiveWriter/WroxBlogsAreNowLive_80AD/wrox-homepage_thumb_2.gif" /&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;There are only a few of us there right now, but that number is sure to grow as more of your favorite Wrox authors jump on board.&lt;/p&gt;
&lt;p&gt;The guidelines that Wrox has given us in regards to content is pretty wide open, as long as the topics are programming-related, or related to our writing or editing activities. I hope to use &lt;a target="_blank" href="http://p2p.wrox.com/content/blogs/lee-dumond"&gt;my own little corner on the Wrox Blogs&lt;/a&gt; to supplement the material I&amp;rsquo;m publishing here, especially that having to do with Wrox stuff that I&amp;rsquo;m reading, writing, or editing.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re a subscriber to &lt;a target="_blank" href="http://feeds2.feedburner.com/leedumond"&gt;LeeDumond.com&lt;/a&gt; (and if you are, I sincerely thank you), you might want to consider subscribing to the Wrox blog as well, to make sure you&amp;rsquo;re getting all the good stuff!&lt;/p&gt;
&lt;ul&gt;
    &lt;li&gt;&lt;a target="_blank" href="http://p2p.wrox.com/content/blogs/lee-dumond/feed"&gt;Subscribe&lt;/a&gt; to &lt;em&gt;my&lt;/em&gt; Wrox Blog&lt;/li&gt;
    &lt;li&gt;&lt;a target="_blank" href="http://p2p.wrox.com/content/blog/feed"&gt;Subscribe&lt;/a&gt; to &lt;em&gt;all&lt;/em&gt; the Wrox Blogs&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As I hope to be starting on a new book very soon, I&amp;rsquo;m sure I&amp;rsquo;ll have lots of cool things to write about. Stay tuned!&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/leedumond?a=_XeWw9NGQpw:tkOf5RNyXSg:Dh-6e3sOTMo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/leedumond?d=Dh-6e3sOTMo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/leedumond?a=_XeWw9NGQpw:tkOf5RNyXSg:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/leedumond?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/leedumond?a=_XeWw9NGQpw:tkOf5RNyXSg:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/leedumond?i=_XeWw9NGQpw:tkOf5RNyXSg:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/leedumond?a=_XeWw9NGQpw:tkOf5RNyXSg:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/leedumond?i=_XeWw9NGQpw:tkOf5RNyXSg:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/leedumond?a=_XeWw9NGQpw:tkOf5RNyXSg:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/leedumond?i=_XeWw9NGQpw:tkOf5RNyXSg:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/leedumond?a=_XeWw9NGQpw:tkOf5RNyXSg:l6gmwiTKsz0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/leedumond?d=l6gmwiTKsz0" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/leedumond?a=_XeWw9NGQpw:tkOf5RNyXSg:TzevzKxY174"&gt;&lt;img src="http://feeds.feedburner.com/~ff/leedumond?d=TzevzKxY174" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/leedumond?a=_XeWw9NGQpw:tkOf5RNyXSg:dnMXMwOfBR0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/leedumond?d=dnMXMwOfBR0" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/leedumond/~4/_XeWw9NGQpw" height="1" width="1"/&gt;</description></item><item><title>Integrating Exception Handling Into the Development Cycle</title><link>http://leedumond.com/blog/integrating-exception-handling-into-the-development-cycle/</link><pubDate>Wed, 14 Oct 2009 13:46:00 GMT</pubDate><guid isPermaLink="true">http://leedumond.com/blog/integrating-exception-handling-into-the-development-cycle/</guid><dc:creator>Lee Dumond</dc:creator><slash:comments>12</slash:comments><category domain="http://leedumond.com/blog/">Blog</category><description>&lt;p&gt;The time to start thinking about exception handling is&amp;nbsp;right after you click File&amp;nbsp;&amp;gt; New Project. Exception handling shouldn't be something you &amp;quot;tack-on&amp;quot; to an application after the fact.&amp;nbsp;Here, I discuss a practical approach to incorporating exception handling into the normal code-writing process.&lt;/p&gt;&lt;hr /&gt;
&lt;p&gt;Recently, my friend &lt;a target="_blank" href="http://jclaes.blogspot.com/"&gt;Jef Claes&lt;/a&gt; wrote me via my contact page. In part, his inquiry reads:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;In which part of the development cycle, do you have to start thinking about exception handling? How and when do you decide which exceptions to handle specifically, so they won't kill your whole page?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Geesh&amp;hellip; I &lt;a target="_blank" href="http://leedumond.com/tags/exception/"&gt;post a few articles&lt;/a&gt; and &lt;a target="_blank" href="http://www.wrox.com/WileyCDA/WroxTitle/Robust-ASP-NET-Exception-Handling.productCd-047050367X.html"&gt;write a book&lt;/a&gt; about exception handling, and all of a sudden I&amp;rsquo;m Mr. Exception Guy. ;)&lt;/p&gt;
&lt;p&gt;In all seriousness though, this is a great question. There are lots of resources out there that provide information about exception handling from a theoretical or academic point of view, but very few that present a &lt;strong&gt;practical approach to integrating exception handling into your day-to-day development cycle.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Here, I&amp;rsquo;d like to present my own general approach to exception handling.&lt;/p&gt;
&lt;h3&gt;Examine the Possibilities&lt;/h3&gt;
&lt;p&gt;&lt;img style="border-bottom: 0px; border-left: 0px; margin: 0px 0px 15px 15px; display: inline; border-top: 0px; border-right: 0px" title="magnifying_glass" border="0" alt="magnifying_glass" align="right" width="104" height="141" src="http://leedumond.com/files/media/image/WindowsLiveWriter/IntegratingExceptionHandlingIntotheDevel_11122/magnifying_glass_3.jpg" /&gt; I have written before about the &lt;a target="_blank" href="http://leedumond.com/blog/friends-dont-let-friends-catch-exception/"&gt;importance of handling only specific exceptions&lt;/a&gt;. Of course, you can&amp;rsquo;t do that unless you know what exceptions could be thrown from your code. That means the first thing you should do is to &lt;strong&gt;discover which exceptions are possible.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;How you do that depends on the framework or API you&amp;rsquo;re using. If you&amp;rsquo;re using the .NET Framework, the MSDN can tell you which exceptions might be thrown from any given method, constructor, property, event, or indexer.&lt;/p&gt;
&lt;p&gt;Consider the following simple code, which writes a string of text to a text file. The string of text to write (&lt;tt&gt;txtEntry.Text&lt;/tt&gt;), as well as a portion of the path value (&lt;tt&gt;txtPath.Text&lt;/tt&gt;), is supplied by the user.&lt;/p&gt;
&lt;pre class="brush: csharp; auto-links: false;"&gt;
private void LogEntry()
{
   string workingDirectory = ConfigurationManager.AppSettings[&amp;quot;directoryToLog&amp;quot;];

   if (!string.IsNullOrEmpty(workingDirectory))
   {
      string path = workingDirectory + txtPath.Text;

      using (StreamWriter writer = File.AppendText(path))
      {
         writer.WriteLine(DateTime.Now.ToString());
         writer.WriteLine(&amp;quot;Entry: {0}&amp;quot;, txtEntry.Text);
         writer.WriteLine(&amp;quot;--------------------&amp;quot;);
      }

      lblMessage.Text = &amp;quot;The entry &amp;quot; + txtEntry.Text + &amp;quot; was logged successfully.&amp;quot;;
   }
   else
   {
      lblMessage.Text = &amp;quot;The required configuration setting is missing. The entry &amp;quot; + 
         txtEntry.Text + &amp;quot; was not logged.&amp;quot;;
   }      
}&lt;/pre&gt;
&lt;p&gt;We can see the first call is to &lt;tt&gt;ConfigurationManager.AppSettings&lt;/tt&gt;. If you place your cursor on the method in the IDE, the Dynamic Help window will show you a link to the method in the MSDN.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;img style="border-right-width: 0px; margin: 15px 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="screenshot1" border="0" alt="screenshot1" width="604" height="389" src="http://leedumond.com/files/media/image/WindowsLiveWriter/IntegratingExceptionHandlingIntotheDevel_11122/screenshot1_3.gif" /&gt;&lt;/p&gt;
&lt;p&gt;Clicking on this link will take you to the relevant MSDN page. The MSDN will show you any exceptions that could possibly be encountered from accessing this property.&lt;/p&gt;
&lt;p&gt;&lt;img style="border-right-width: 0px; margin: 15px 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="screenshot2" border="0" alt="screenshot2" width="604" height="572" src="http://leedumond.com/files/media/image/WindowsLiveWriter/IntegratingExceptionHandlingIntotheDevel_11122/screenshot2_3.gif" /&gt;&lt;/p&gt;
&lt;p&gt;Doing the same with the &lt;tt&gt;File.AppendText&lt;/tt&gt; method yields the following result:&lt;/p&gt;
&lt;p&gt;&lt;img style="border-right-width: 0px; margin: 15px 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="screenshot3" border="0" alt="screenshot3" width="604" height="605" src="http://leedumond.com/files/media/image/WindowsLiveWriter/IntegratingExceptionHandlingIntotheDevel_11122/screenshot3_3.gif" /&gt;&lt;/p&gt;
&lt;p&gt;Finally, the &lt;tt&gt;WriteLine&lt;/tt&gt; method shows the following:&lt;/p&gt;
&lt;p&gt;&lt;img style="border-right-width: 0px; margin: 15px 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="screenshot4" border="0" alt="screenshot4" width="604" height="605" src="http://leedumond.com/files/media/image/WindowsLiveWriter/IntegratingExceptionHandlingIntotheDevel_11122/screenshot4_3.gif" /&gt;&lt;/p&gt;
&lt;p&gt;As you can see, the Visual Studio IDE and accompanying documentation makes this pretty easy. Keep in mind that when you&amp;rsquo;re calling a third party API, your experience will definitely vary. A well-written API should include &lt;a target="_blank" href="http://msdn.microsoft.com/en-us/library/b2s063f7(loband).aspx"&gt;XML documentation&lt;/a&gt; that exposes via Intellisense any exceptions that could be thrown; and if you&amp;rsquo;re lucky, compiled documentation created by a tool such as &lt;a target="_blank" href="http://www.codeplex.com/Sandcastle"&gt;Sandcastle&lt;/a&gt;. If not, you have to rely on whatever the API provides, or perhaps use &lt;a target="_blank" href="http://www.red-gate.com/products/reflector/"&gt;Reflector&lt;/a&gt; to disassemble the binary.&lt;/p&gt;
&lt;h3&gt;Program Defensively&lt;/h3&gt;
&lt;p&gt;&lt;img style="border-bottom: 0px; border-left: 0px; margin: 0px 0px 15px 15px; display: inline; border-top: 0px; border-right: 0px" title="17_AggressiveDriving" border="0" alt="17_AggressiveDriving" align="right" width="172" height="104" src="http://leedumond.com/files/media/image/WindowsLiveWriter/IntegratingExceptionHandlingIntotheDevel_11122/17_AggressiveDriving_3.jpg" /&gt; Now that we have a list of all the possible exceptions that can be thrown from our code, the next step is to &lt;strong&gt;determine which exceptions we can prevent, and which we can safely ignore.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;In this case, we are supplying part of the path in the configuration file, and this branch of code only runs if that value is not null or empty. Therefore, we can be 100% certain that the argument we pass to &lt;tt&gt;File.AppendText&lt;/tt&gt; will never be null. This lets us safely ignore &lt;tt&gt;ArgumentNullException&lt;/tt&gt; from the &lt;tt&gt;File.AppendText&lt;/tt&gt; method.&lt;/p&gt;
&lt;p&gt;Next, we can employ &lt;a target="_blank" href="http://leedumond.com/blog/defensive-programming-or-why-exception-handling-is-like-car-insurance/"&gt;defensive programming techniques&lt;/a&gt; to prevent most of the remaining exceptions that can be thrown from &lt;tt&gt;File.AppendText&lt;/tt&gt;. By restricting and validating the user input, you can assure that the path supplied by the user can never be too long, contain invalid characters, or be in an invalid format. This relieves us from having to handle &lt;tt&gt;ArgumentException&lt;/tt&gt;, &lt;tt&gt;PathTooLongException&lt;/tt&gt;, or &lt;tt&gt;NotSupportedException&lt;/tt&gt;. That leaves us two possible exceptions we might have to deal with.&lt;/p&gt;
&lt;p&gt;Now, let&amp;rsquo;s consider the two exceptions that can be thrown from the &lt;tt&gt;TextWriter.WriteLine&lt;/tt&gt; method. The first, &lt;tt&gt;ObjectDisposedException&lt;/tt&gt;, can&amp;rsquo;t happen in this code because all of the &lt;tt&gt;WriteLine&lt;/tt&gt; calls are made prior to closing the writer; so we&amp;rsquo;ll ignore it. The second, &lt;tt&gt;IOException&lt;/tt&gt;, would normally be thrown in response to a disk or path error. Since we can&amp;rsquo;t prevent this from happening in code, this exception is a good candidate for handling.&lt;/p&gt;
&lt;h3&gt;Determine Which Exceptions To Handle&lt;/h3&gt;
&lt;p&gt;&lt;img style="border-bottom: 0px; border-left: 0px; margin: 0px 0px 15px 15px; display: inline; border-top: 0px; border-right: 0px" title="Dylan_wrestling_belt1" border="0" alt="Dylan_wrestling_belt1" align="right" width="104" height="137" src="http://leedumond.com/files/media/image/WindowsLiveWriter/IntegratingExceptionHandlingIntotheDevel_11122/Dylan_wrestling_belt1_3.jpg" /&gt; So far, we&amp;rsquo;ve taken two steps. First, we &lt;strong&gt;identified all possible exceptions&lt;/strong&gt;. Second, we determined &lt;strong&gt;which exceptions can be either effectively prevented or safely ignored.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The remaining exceptions we have to worry about are:&lt;/p&gt;
&lt;ul&gt;
    &lt;li&gt;&lt;tt&gt;ConfigurationErrorsException&lt;/tt&gt; (from &lt;tt&gt;ConfigurationManager.AppSettings&lt;/tt&gt;)&lt;/li&gt;
    &lt;li&gt;&lt;tt&gt;UnauthorizedAccessException&lt;/tt&gt; (from &lt;tt&gt;File.AppendText&lt;/tt&gt;)&lt;/li&gt;
    &lt;li&gt;&lt;tt&gt;DirectoryNotFoundException&lt;/tt&gt; (from &lt;tt&gt;File.AppendText&lt;/tt&gt;)&lt;/li&gt;
    &lt;li&gt;&lt;tt&gt;IOException&lt;/tt&gt; (from &lt;tt&gt;writer.WriteLine&lt;/tt&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Now, we must decide which of these we can and should handle, and why.&lt;/p&gt;
&lt;p&gt;There are legitimate reasons for handling exceptions. Among these are:&lt;/p&gt;
&lt;ul&gt;
    &lt;li&gt;Notifying the user, and if appropriate, allowing them to retry the operation&lt;/li&gt;
    &lt;li&gt;Wrapping the exception in a new exception, appending additional relevant detail&lt;/li&gt;
    &lt;li&gt;Wrapping a specific exception in a more general exception to prevent revealing sensitive details, such as exceptions thrown from a Web service&lt;/li&gt;
    &lt;li&gt;Using a finally block to clean up resources that might be abandoned if an exception occurs&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In this simple example, notifying the user is probably sufficient.&lt;/p&gt;
&lt;pre class="brush: csharp; auto-links: false;"&gt;
private void LogEntry()
{
   string workingDirectory = null;

   try
   {
      workingDirectory = ConfigurationManager.AppSettings[&amp;quot;directoryToLog&amp;quot;];
   }
   catch (ConfigurationErrorsException)
   {
      lblMessage.Text = &amp;quot;There was a problem accessing the configuration settings. The entry &amp;quot; +
                        txtEntry.Text + &amp;quot; was not logged.&amp;quot;;
      return;
   }

   if (!string.IsNullOrEmpty(workingDirectory))
   {
      string path = string.Empty;

      try
      {
         path = workingDirectory + txtPath.Text;

         using (StreamWriter writer = File.AppendText(path))
         {
            writer.WriteLine(DateTime.Now.ToString());
            writer.WriteLine(&amp;quot;Entry: {0}&amp;quot;, txtEntry.Text);
            writer.WriteLine(&amp;quot;--------------------&amp;quot;);
         }

         lblMessage.Text = &amp;quot;The entry &amp;quot; + txtEntry.Text + &amp;quot; was logged successfully.&amp;quot;;
      }
      catch (UnauthorizedAccessException)
      {
         lblMessage.Text = &amp;quot;You do not have the required permissions to access the path &amp;quot; +
                           path + &amp;quot; . The entry &amp;quot; + txtEntry.Text + &amp;quot; was not logged.&amp;quot;;
      }
      catch (DirectoryNotFoundException)
      {
         lblMessage.Text = &amp;quot;Part of the path &amp;quot; + path + &amp;quot; was not found. The entry &amp;quot; +
                           txtEntry.Text + &amp;quot; was not logged.&amp;quot;;
      }
      catch (IOException)
      {
         lblMessage.Text = &amp;quot;There was a problem accessing the path &amp;quot; + path + &amp;quot; . The entry &amp;quot; +
                           txtEntry.Text + &amp;quot; was not logged.&amp;quot;;
      }
   }
   else
   {
      lblMessage.Text = &amp;quot;The required configuration setting is missing. The entry &amp;quot; + txtEntry.Text +
                        &amp;quot; was not logged.&amp;quot;;
   }
}&lt;/pre&gt;
&lt;p&gt;Note that we&amp;rsquo;re tailoring the message sent back to the UI to be as informative as possible, &lt;em&gt;without&lt;/em&gt; using the original &lt;tt&gt;Message&lt;/tt&gt; property from the handled exception. While the &lt;tt&gt;Message&lt;/tt&gt; property is quite useful for logging and debugging purposes, I generally find it&amp;rsquo;s &lt;em&gt;not a good idea to pass a framework-generated exception message to your end users&lt;/em&gt;. For one, they often tend to be rather cryptic to non-developers. More importantly, it&amp;rsquo;s possible they could contain sensitive information. While the .NET Framework itself is pretty good at not revealing sensitive information in &lt;tt&gt;Message&lt;/tt&gt;, you&amp;rsquo;ll find that many third-party APIs are not nearly as conscientious in this regard.&lt;/p&gt;
&lt;p&gt;Also note that the order in which you handle exceptions can be important. Here we&amp;rsquo;re handling &lt;tt&gt;DirectoryNotFoundException&lt;/tt&gt; before &lt;tt&gt;IOException&lt;/tt&gt;. This is because &lt;tt&gt;DirectoryNotFoundException&lt;/tt&gt; is a derived type of &lt;tt&gt;IOException&lt;/tt&gt;, and derived types should always be handled first.&lt;/p&gt;
&lt;h3&gt;Do or Do Not. There is No Try.&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;&lt;img style="border-bottom: 0px; border-left: 0px; margin: 0px 0px 15px 15px; display: inline; border-top: 0px; border-right: 0px" title="YODA" border="0" alt="YODA" align="right" width="104" height="134" src="http://leedumond.com/files/media/image/WindowsLiveWriter/IntegratingExceptionHandlingIntotheDevel_11122/YODA_3.jpg" /&gt; If you can&amp;rsquo;t successfully handle an exception in code, it is best to let the application fail as quickly as possible.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;I can hear some of you now: &amp;ldquo;&lt;em&gt;What&lt;/em&gt;? Are you suggesting that we should &lt;em&gt;let our applications fail&lt;/em&gt;?&amp;rdquo;&lt;/p&gt;
&lt;p&gt;The answer is &lt;em&gt;yes&lt;/em&gt;&amp;hellip; that is, in fact, &lt;em&gt;exactly what I am suggesting&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Part of handling exceptions successfully is facing the fact that &lt;a target="_blank" href="http://leedumond.com/blog/friends-dont-let-friends-catch-exception/"&gt;you cannot successfully handle everything&lt;/a&gt;. Remember, an exception being thrown means your application has &lt;em&gt;already&lt;/em&gt; crashed. Sometimes it is possible to handle the exception, giving the application a chance to recover or to &lt;a target="_blank" href="http://leedumond.com/blog/retrying-mail-operations-in-net/"&gt;retry the operation&lt;/a&gt;. Quite often though, you will encounter exceptions for which &lt;em&gt;no sensible handling strategy exists&lt;/em&gt;, in which case &lt;em&gt;catching the exception is totally pointless&lt;/em&gt;. Only catch exceptions which you are prepared to handle; otherwise let them go.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;If you catch exceptions which you subsequently do not handle &amp;ndash;&lt;/strong&gt; usually through the use of general catch blocks &lt;strong&gt;&amp;ndash; you merely postpone the inevitable.&lt;/strong&gt; Allowing an application to continue in the face of an unhandled exception is not only wasteful, it&amp;rsquo;s dangerous. Values might be corrupted. Resources might be left in an unstable or unknown state. This is &lt;em&gt;exactly why exceptions are thrown in the first place&lt;/em&gt; &amp;ndash; to &lt;em&gt;prevent&lt;/em&gt; a program from continuing under these conditions. If you can somehow correct or recover from the condition, then fine. If you cannot, then failing quickly is the best option.&lt;/p&gt;
&lt;h3&gt;Fail Gracefully&lt;/h3&gt;
&lt;p&gt;&lt;img style="border-bottom: 0px; border-left: 0px; margin: 0px 0px 15px 15px; display: inline; border-top: 0px; border-right: 0px" title="kerri-strug-8_6" border="0" alt="kerri-strug-8_6" align="right" width="104" height="153" src="http://leedumond.com/files/media/image/WindowsLiveWriter/IntegratingExceptionHandlingIntotheDevel_11122/kerri-strug-8_6_3.jpg" /&gt; The fact is that in a production application, you never truly let any exception remain &amp;ldquo;unhandled&amp;rdquo; in a technical sense &amp;ndash; the &lt;em&gt;real&lt;/em&gt; question is not &lt;em&gt;whether&lt;/em&gt; to handle exceptions, but &lt;em&gt;where&lt;/em&gt; to handle them.&lt;/p&gt;
&lt;p&gt;What I&amp;rsquo;m saying here is that &lt;strong&gt;you need to make absolutely sure you have a global exception handling mechanism in place.&lt;/strong&gt; In a Windows application, that usually means handling the &lt;tt&gt;AppDomain.CurrentDomain.UnhandledException&lt;/tt&gt; event. In an ASP.NET application, it means handling the &lt;tt&gt;HttpApplication.Error&lt;/tt&gt; event, either manually or via the built-in ASP.NET default exception handler. This &amp;ldquo;last-chance&amp;rdquo; global handler lets you provide a friendly user experience when unrecoverable errors occur, as well as a central location from which to log exceptions that weren&amp;rsquo;t handled by your code.&lt;/p&gt;
&lt;p&gt;Of course, once an exception reaches your global exception handler, it means execution has stopped and your application has failed. That happens &amp;ndash; and as we&amp;rsquo;ve learned, sometimes there&amp;rsquo;s nothing you can do about it. But there&amp;rsquo;s no reason your application can&amp;rsquo;t fail &lt;em&gt;gracefully&lt;/em&gt;.&lt;/p&gt;
&lt;h3&gt;Summary&lt;/h3&gt;
&lt;p&gt;In this post, I&amp;rsquo;ve outlined a series of steps you can use to sensibly and effectively &lt;strong&gt;integrate exception handling into the development cycle.&lt;/strong&gt; These can be summarized as follows:&lt;/p&gt;
&lt;ol&gt;
    &lt;li&gt;Use the available documentation to discover which exceptions are possible.&lt;/li&gt;
    &lt;li&gt;Determine which exceptions you can safely ignore, based on current state.&lt;/li&gt;
    &lt;li&gt;Where possible, use defensive programming techniques to prevent exceptions from being thrown.&lt;/li&gt;
    &lt;li&gt;Determine which of the remaining exceptions you should handle, and develop a strategy for doing so.&lt;/li&gt;
    &lt;li&gt;Allow any exceptions you cannot completely handle in code to propagate to your default exception handler.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This, in a nutshell, pretty much sums up how I approach exception handling as I write code. Hopefully, you&amp;rsquo;ll find this useful in your day-to-day routine as well.&lt;/p&gt;
&lt;p&gt;Questions? Constructive criticism? Suggestions for improvement? Please let me know what you think in the comments below.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/leedumond?a=tH9KSPGNPgY:JP1lZG-pALo:Dh-6e3sOTMo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/leedumond?d=Dh-6e3sOTMo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/leedumond?a=tH9KSPGNPgY:JP1lZG-pALo:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/leedumond?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/leedumond?a=tH9KSPGNPgY:JP1lZG-pALo:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/leedumond?i=tH9KSPGNPgY:JP1lZG-pALo:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/leedumond?a=tH9KSPGNPgY:JP1lZG-pALo:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/leedumond?i=tH9KSPGNPgY:JP1lZG-pALo:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/leedumond?a=tH9KSPGNPgY:JP1lZG-pALo:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/leedumond?i=tH9KSPGNPgY:JP1lZG-pALo:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/leedumond?a=tH9KSPGNPgY:JP1lZG-pALo:l6gmwiTKsz0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/leedumond?d=l6gmwiTKsz0" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/leedumond?a=tH9KSPGNPgY:JP1lZG-pALo:TzevzKxY174"&gt;&lt;img src="http://feeds.feedburner.com/~ff/leedumond?d=TzevzKxY174" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/leedumond?a=tH9KSPGNPgY:JP1lZG-pALo:dnMXMwOfBR0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/leedumond?d=dnMXMwOfBR0" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/leedumond/~4/tH9KSPGNPgY" height="1" width="1"/&gt;</description></item><item><title>ASP.NET Profiles in Web Application Projects</title><link>http://leedumond.com/blog/asp-net-profiles-in-web-application-projects/</link><pubDate>Thu, 08 Oct 2009 11:45:00 GMT</pubDate><guid isPermaLink="true">http://leedumond.com/blog/asp-net-profiles-in-web-application-projects/</guid><dc:creator>Lee Dumond</dc:creator><slash:comments>25</slash:comments><category domain="http://leedumond.com/blog/">Blog</category><description>&lt;p&gt;Accessing ASP.NET profiles from a web application project is remarkably easy once you know how. Here&amp;rsquo;s how it&amp;rsquo;s done, along with a fully working implementation that you can download.&lt;/p&gt;&lt;hr /&gt;
&lt;p&gt;Earlier this year, I blogged about how to create a &lt;a target="_blank" href="http://leedumond.com/blog/getting-strongly-typed-profile-properties-from-a-class-library/"&gt;strongly-typed Profile that could be accessed from a class library&lt;/a&gt;. In response to popular demand, today I&amp;rsquo;m presenting a similar (but more substantial) example geared specifically toward &lt;strong&gt;accessing profiles in web application projects.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;There are plenty of blog posts that &lt;em&gt;talk&lt;/em&gt; about this, but unfortunately very few that show &lt;em&gt;how to do it&lt;/em&gt;. Most of them instead refer to &lt;a target="_blank" href="http://www.codeplex.com/WebProfile"&gt;this relatively obscure add-in for Visual Studio 2005&lt;/a&gt;&amp;nbsp;(apparently. you&amp;rsquo;re out of luck if you&amp;rsquo;re using Visual Studio 2008). However, the good news is &lt;strong&gt;you don&amp;rsquo;t need a third party add-in or build task for this&lt;/strong&gt;, as implementing this functionality yourself is not difficult at all.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re reading this, I&amp;rsquo;ll assume you&amp;rsquo;re aware of the differences between an ASP.NET web &lt;em&gt;site&lt;/em&gt; and a web &lt;em&gt;application&lt;/em&gt;, so I won&amp;rsquo;t go into the various differences in great detail (feel free to &lt;a target="_blank" href="http://msdn.microsoft.com/en-us/library/aa730880(VS.80).aspx"&gt;consult the MSDN&lt;/a&gt; for more information). For our purposes, it&amp;rsquo;s just important to understand that in a web &lt;em&gt;application&lt;/em&gt; project, there is no &lt;tt&gt;ProfileCommon&lt;/tt&gt; class or &lt;tt&gt;Page.Profile&lt;/tt&gt; property to work with, as we&amp;rsquo;re accustomed to seeing in web &lt;em&gt;site&lt;/em&gt; projects &amp;ndash; hence the problem.&lt;/p&gt;
&lt;p&gt;Web site projects accomplish this bit of magic through dynamic compilation &amp;ndash; essentially creating the needed objects at runtime, &amp;ldquo;on the fly&amp;rdquo; as it were. In effect we will be doing the exact same thing, but the old-fashioned way &amp;ndash; with a simple hand-written proxy class.&lt;/p&gt;
&lt;h3&gt;Writing a Profile Proxy Class&lt;/h3&gt;
&lt;p&gt;Let&amp;rsquo;s assume for the sake of discussion that we have several bits of user information we want to store in profile.&lt;/p&gt;
&lt;ul&gt;
    &lt;li&gt;first name&lt;/li&gt;
    &lt;li&gt;last name&lt;/li&gt;
    &lt;li&gt;date of birth&lt;/li&gt;
    &lt;li&gt;gender&lt;/li&gt;
    &lt;li&gt;the user&amp;rsquo;s time zone&lt;/li&gt;
    &lt;li&gt;the user&amp;rsquo;s preferred culture&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Now, we could store these as separate value types, but in practice I find it easier to group related properties together in a class. So let&amp;rsquo;s create a class &lt;tt&gt;Personal&lt;/tt&gt; to store the first four properties, and a class &lt;tt&gt;Preferences&lt;/tt&gt; to store the time zone and culture. Make sure to decorate these classes with the &lt;tt&gt;[Serializable]&lt;/tt&gt; attribute.&lt;/p&gt;
&lt;pre class="brush: csharp; auto-links: false;"&gt;
namespace LD.Sample.BLL
{
   using System;

   [Serializable]
   public class Personal
   {
      public string FirstName { get; set; }

      public string LastName { get; set; }

      public DateTime DateOfBirth { get; set; }

      public Gender Gender { get; set; }
   }
}&lt;/pre&gt;
&lt;pre class="brush: csharp; auto-links: false;"&gt;
namespace LD.Sample.BLL
{
   using System;

   [Serializable]
   public class Preferences
   {
      public string TimeZone { get; set; }
      
      public string Culture { get; set; }
   }
}&lt;/pre&gt;
&lt;p&gt;Next, we&amp;rsquo;ll write a class that will define the profile we want, incorporating the two custom types shown above as properties. This class will inherit &lt;tt&gt;ProfileBase&lt;/tt&gt; . Let&amp;rsquo;s call it &lt;tt&gt;CustomProfile&lt;/tt&gt;.&lt;/p&gt;
&lt;pre class="brush: csharp; auto-links: false;"&gt;
namespace LD.Sample.BLL
{
   using System.Web;
   using System.Web.Profile;

   public class CustomProfile : ProfileBase
   {
      public Personal Personal
      {
         get { return (Personal) GetPropertyValue(&amp;quot;Personal&amp;quot;); }
      }

      public Preferences Preferences
      {
         get { return (Preferences) GetPropertyValue(&amp;quot;Preferences&amp;quot;); }
      }

      /// &amp;lt;summary&amp;gt;
      /// Get the profile of the currently logged-on user.
      /// &amp;lt;/summary&amp;gt;      
      public static CustomProfile GetProfile()
      {
         return (CustomProfile) HttpContext.Current.Profile;
      }

      /// &amp;lt;summary&amp;gt;
      /// Gets the profile of a specific user.
      /// &amp;lt;/summary&amp;gt;
      /// &amp;lt;param name=&amp;quot;userName&amp;quot;&amp;gt;The user name of the user whose profile you want to retrieve.&amp;lt;/param&amp;gt;
      public static CustomProfile GetProfile(string userName)
      {
         return (CustomProfile) Create(userName);
      }
   }
}&lt;/pre&gt;
&lt;p&gt;Note that I&amp;rsquo;ve also included two overloaded static &lt;tt&gt;GetProfile&lt;/tt&gt; methods. &lt;tt&gt;GetProfile()&lt;/tt&gt;&amp;nbsp; lets us obtain the profile of the current user, while &lt;tt&gt;GetProfile(string userName)&lt;/tt&gt; lets us get the profile of a specific user (for administrative purposes, or when creating a new member, for example).&lt;/p&gt;
&lt;p&gt;One more thing &amp;ndash; and this is very important &amp;ndash; we need to define the provider in web.config.&lt;/p&gt;
&lt;pre class="brush: xml; auto-links: false;"&gt;
&amp;lt;profile defaultProvider=&amp;quot;LD.SampleWAP.ProfileProvider&amp;quot; inherits=&amp;quot;LD.Sample.BLL.CustomProfile&amp;quot;&amp;gt;
&amp;lt;providers&amp;gt;
   &amp;lt;add name=&amp;quot;LD.SampleWAP.ProfileProvider&amp;quot;
   type=&amp;quot;System.Web.Profile.SqlProfileProvider&amp;quot;
   applicationName=&amp;quot;/&amp;quot;
   connectionStringName=&amp;quot;LocalSqlServer&amp;quot;/&amp;gt;
&amp;lt;/providers&amp;gt;&lt;/pre&gt;
&lt;p&gt;You&amp;rsquo;ll see that our provider is of type &lt;tt&gt;System.Web.Profile.SqlProfileProvider&lt;/tt&gt;. Of course, this assumes that the database to which this is pointing has been configured with the schema required to support &lt;tt&gt;SqlProfileProvider&lt;/tt&gt;.&lt;/p&gt;
&lt;p&gt;Also note the &lt;tt&gt;inherits&lt;/tt&gt; attribute in &lt;tt&gt;&amp;lt;profile&amp;gt;&lt;/tt&gt;. This is what tells the provider where to obtain the profile properties &amp;ndash; in our case, from the &lt;tt&gt;CustomProfile&lt;/tt&gt; class. When you do this, &lt;em&gt;you do not want to define the properties declaratively&lt;/em&gt; in web.config, as you would in a website project. If you do, you&amp;rsquo;ll get a &lt;tt&gt;System.Configuration.ConfigurationException&lt;/tt&gt;, informing you that the profile property has already been defined.&lt;/p&gt;
&lt;h3&gt;Usage&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;And that, believe it or not, is all there is to setting up a strongly-typed profile for use in a web application project.&lt;/strong&gt; Now, let&amp;rsquo;s take a look at how you can use this newly-implemented goodness.&lt;/p&gt;
&lt;p&gt;Here is some code that shows &lt;strong&gt;setting profile values for the currently logged-on user.&lt;/strong&gt;&lt;/p&gt;
&lt;pre class="brush: csharp; auto-links: false;"&gt;
CustomProfile profile = CustomProfile.GetProfile();

profile.Personal.FirstName = &amp;quot;Tami&amp;quot;;
profile.Personal.LastName = &amp;quot;Gharst&amp;quot;;
profile.Personal.DateOfBirth = new DateTime(1972, 7, 10);
profile.Personal.Gender = Gender.Female;
profile.Preferences.TimeZone = &amp;quot;W. Europe Standard Time&amp;quot;;
profile.Preferences.Culture = &amp;quot;de-DE&amp;quot;;

profile.Save();&lt;/pre&gt;
&lt;p&gt;Here is some code that shows &lt;strong&gt;setting profile values for a specific user&lt;/strong&gt; with the user name &amp;ldquo;Lee&amp;rdquo;.&lt;/p&gt;
&lt;pre class="brush: csharp; auto-links: false;"&gt;
CustomProfile profile = CustomProfile.GetProfile(&amp;quot;Lee&amp;quot;);

profile.Personal.FirstName = &amp;quot;Lee&amp;quot;;
profile.Personal.LastName = &amp;quot;Dumond&amp;quot;;
profile.Personal.DateOfBirth = new DateTime(1961, 10, 9);
profile.Personal.Gender = Gender.Male;
profile.Preferences.TimeZone = &amp;quot;Central Standard Time&amp;quot;;
profile.Preferences.Culture = &amp;quot;en-US&amp;quot;;

profile.Save();&lt;/pre&gt;
&lt;p&gt;If you like, you can make this even simpler by adding a &lt;tt&gt;Profile&lt;/tt&gt; property to the page you&amp;rsquo;re working with &amp;ndash; or even better, inherit your pages from a base type and add it there, so it&amp;rsquo;s available to all of your pages. This lets you &lt;strong&gt;set and retrieve profile properties with the familiar ASP.NET web site syntax&lt;/strong&gt;.&lt;/p&gt;
&lt;pre class="brush: csharp; auto-links: false;"&gt;
// set profile properties

Profile.Personal.FirstName = &amp;quot;Lee&amp;quot;;
Profile.Personal.LastName = &amp;quot;Dumond&amp;quot;;
Profile.Personal.DateOfBirth = new DateTime(1961, 10, 9);
Profile.Personal.Gender = Gender.Male;
Profile.Preferences.TimeZone = &amp;quot;Central Standard Time&amp;quot;;
Profile.Preferences.Culture = &amp;quot;en-US&amp;quot;;

Profile.Save();&lt;/pre&gt;
&lt;pre class="brush: csharp; auto-links: false;"&gt;
// display profile properties

lblDisplay.Text =
   string.Format(
      &amp;quot;User {0} had a real name of {1} {2}, was born on {3}, lives in the {4} time zone, and has a preferred culture of {5}.&amp;quot;,
      Profile.UserName, 
      Profile.Personal.FirstName, 
      Profile.Personal.LastName,
      Profile.Personal.DateOfBirth.ToShortDateString(),
      Profile.Preferences.TimeZone,
      Profile.Preferences.Culture);&lt;/pre&gt;
&lt;h3&gt;Sample Application&lt;/h3&gt;
&lt;p&gt;As I mentioned, I&amp;rsquo;ve included a downloadable web application solution that demonstrates web application profiles in action. When you run it, you&amp;rsquo;ll be greeted by the following screen:&lt;/p&gt;
&lt;p&gt;&lt;a href="http://leedumond.com/files/media/image/WindowsLiveWriter/ASP.NETProfilesinWebApplicationProjects_A73B/image_4.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" width="304" height="264" src="http://leedumond.com/files/media/image/WindowsLiveWriter/ASP.NETProfilesinWebApplicationProjects_A73B/image_thumb_1.png" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;There are screens that let you display a profile, edit a profile, and create a new one.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://leedumond.com/files/media/image/WindowsLiveWriter/ASP.NETProfilesinWebApplicationProjects_A73B/image_6.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" width="304" height="264" src="http://leedumond.com/files/media/image/WindowsLiveWriter/ASP.NETProfilesinWebApplicationProjects_A73B/image_thumb_2.png" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://leedumond.com/files/media/image/WindowsLiveWriter/ASP.NETProfilesinWebApplicationProjects_A73B/image_8.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" width="304" height="317" src="http://leedumond.com/files/media/image/WindowsLiveWriter/ASP.NETProfilesinWebApplicationProjects_A73B/image_thumb_3.png" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Examining the source code, along with the preceding explanation, should make understanding these concepts a snap.&lt;/p&gt;
&lt;p&gt;I hope this helps the next time you find yourself needing access to profile properties&amp;nbsp;from a web application!&lt;/p&gt;
&lt;p&gt;Download sample code: &lt;a target="_blank" href="http://leedumond.com/files/downloads/WebApplicationProfileSample.zip"&gt;WebApplicationProfileSample.zip&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/leedumond?a=tPcD5Ss0zmQ:4LCNC2c6tt4:Dh-6e3sOTMo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/leedumond?d=Dh-6e3sOTMo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/leedumond?a=tPcD5Ss0zmQ:4LCNC2c6tt4:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/leedumond?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/leedumond?a=tPcD5Ss0zmQ:4LCNC2c6tt4:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/leedumond?i=tPcD5Ss0zmQ:4LCNC2c6tt4:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/leedumond?a=tPcD5Ss0zmQ:4LCNC2c6tt4:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/leedumond?i=tPcD5Ss0zmQ:4LCNC2c6tt4:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/leedumond?a=tPcD5Ss0zmQ:4LCNC2c6tt4:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/leedumond?i=tPcD5Ss0zmQ:4LCNC2c6tt4:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/leedumond?a=tPcD5Ss0zmQ:4LCNC2c6tt4:l6gmwiTKsz0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/leedumond?d=l6gmwiTKsz0" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/leedumond?a=tPcD5Ss0zmQ:4LCNC2c6tt4:TzevzKxY174"&gt;&lt;img src="http://feeds.feedburner.com/~ff/leedumond?d=TzevzKxY174" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/leedumond?a=tPcD5Ss0zmQ:4LCNC2c6tt4:dnMXMwOfBR0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/leedumond?d=dnMXMwOfBR0" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/leedumond/~4/tPcD5Ss0zmQ" height="1" width="1"/&gt;</description></item></channel></rss>
